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 /* 22*9e986f0eSFrank Van Der Linden * Portions Copyright (c) 2010, Oracle and/or its affiliates. 23*9e986f0eSFrank Van Der Linden * All rights reserved. 243a634bfcSVikram Hegde */ 253a634bfcSVikram Hegde /* 263a634bfcSVikram Hegde * Copyright (c) 2009, Intel Corporation. 273a634bfcSVikram Hegde * All rights reserved. 283a634bfcSVikram Hegde */ 293a634bfcSVikram Hegde 303a634bfcSVikram Hegde /* 313a634bfcSVikram Hegde * Intel IOMMU implementation 323a634bfcSVikram Hegde * This file contains Intel IOMMU code exported 333a634bfcSVikram Hegde * to the rest of the system and code that deals 343a634bfcSVikram Hegde * with the Intel IOMMU as a whole. 353a634bfcSVikram Hegde */ 363a634bfcSVikram Hegde 373a634bfcSVikram Hegde #include <sys/conf.h> 383a634bfcSVikram Hegde #include <sys/modctl.h> 393a634bfcSVikram Hegde #include <sys/pci.h> 403a634bfcSVikram Hegde #include <sys/pci_impl.h> 413a634bfcSVikram Hegde #include <sys/sysmacros.h> 423a634bfcSVikram Hegde #include <sys/ddi.h> 433a634bfcSVikram Hegde #include <sys/ddidmareq.h> 443a634bfcSVikram Hegde #include <sys/ddi_impldefs.h> 453a634bfcSVikram Hegde #include <sys/ddifm.h> 463a634bfcSVikram Hegde #include <sys/sunndi.h> 473a634bfcSVikram Hegde #include <sys/debug.h> 483a634bfcSVikram Hegde #include <sys/fm/protocol.h> 493a634bfcSVikram Hegde #include <sys/note.h> 503a634bfcSVikram Hegde #include <sys/apic.h> 513a634bfcSVikram Hegde #include <vm/hat_i86.h> 523a634bfcSVikram Hegde #include <sys/smp_impldefs.h> 533a634bfcSVikram Hegde #include <sys/spl.h> 543a634bfcSVikram Hegde #include <sys/archsystm.h> 553a634bfcSVikram Hegde #include <sys/x86_archext.h> 563a634bfcSVikram Hegde #include <sys/rootnex.h> 573a634bfcSVikram Hegde #include <sys/avl.h> 583a634bfcSVikram Hegde #include <sys/bootconf.h> 593a634bfcSVikram Hegde #include <sys/bootinfo.h> 603a634bfcSVikram Hegde #include <sys/atomic.h> 613a634bfcSVikram Hegde #include <sys/immu.h> 623a634bfcSVikram Hegde /* ########################### Globals and tunables ######################## */ 633a634bfcSVikram Hegde /* 643a634bfcSVikram Hegde * Global switches (boolean) that can be toggled either via boot options 653a634bfcSVikram Hegde * or via /etc/system or kmdb 663a634bfcSVikram Hegde */ 673a634bfcSVikram Hegde 683a634bfcSVikram Hegde /* Various features */ 693adb2334SVikram Hegde boolean_t immu_enable = B_TRUE; 703a634bfcSVikram Hegde boolean_t immu_dvma_enable = B_TRUE; 713a634bfcSVikram Hegde 723a634bfcSVikram Hegde /* accessed in other files so not static */ 733a634bfcSVikram Hegde boolean_t immu_gfxdvma_enable = B_TRUE; 743a634bfcSVikram Hegde boolean_t immu_intrmap_enable = B_FALSE; 753a634bfcSVikram Hegde boolean_t immu_qinv_enable = B_FALSE; 763a634bfcSVikram Hegde 773a634bfcSVikram Hegde /* various quirks that need working around */ 783a634bfcSVikram Hegde 793a634bfcSVikram Hegde /* XXX We always map page 0 read/write for now */ 803a634bfcSVikram Hegde boolean_t immu_quirk_usbpage0 = B_TRUE; 813a634bfcSVikram Hegde boolean_t immu_quirk_usbrmrr = B_TRUE; 823a634bfcSVikram Hegde boolean_t immu_quirk_usbfullpa; 833a634bfcSVikram Hegde boolean_t immu_quirk_mobile4; 843a634bfcSVikram Hegde 853a634bfcSVikram Hegde /* debug messages */ 863a634bfcSVikram Hegde boolean_t immu_dmar_print; 873a634bfcSVikram Hegde 88e03dceedSVikram Hegde /* Tunables */ 89e03dceedSVikram Hegde int64_t immu_flush_gran = 5; 90e03dceedSVikram Hegde 91*9e986f0eSFrank Van Der Linden immu_flags_t immu_global_dvma_flags; 92*9e986f0eSFrank Van Der Linden 933a634bfcSVikram Hegde /* ############ END OPTIONS section ################ */ 943a634bfcSVikram Hegde 953a634bfcSVikram Hegde /* 963a634bfcSVikram Hegde * Global used internally by Intel IOMMU code 973a634bfcSVikram Hegde */ 983a634bfcSVikram Hegde dev_info_t *root_devinfo; 993a634bfcSVikram Hegde kmutex_t immu_lock; 1003a634bfcSVikram Hegde list_t immu_list; 101e03dceedSVikram Hegde void *immu_pgtable_cache; 1023a634bfcSVikram Hegde boolean_t immu_setup; 1033a634bfcSVikram Hegde boolean_t immu_running; 1043a634bfcSVikram Hegde boolean_t immu_quiesced; 1053a634bfcSVikram Hegde 1063a634bfcSVikram Hegde /* ######################## END Globals and tunables ###################### */ 1073a634bfcSVikram Hegde /* Globals used only in this file */ 1083a634bfcSVikram Hegde static char **black_array; 1093a634bfcSVikram Hegde static uint_t nblacks; 110*9e986f0eSFrank Van Der Linden 111*9e986f0eSFrank Van Der Linden static char **unity_driver_array; 112*9e986f0eSFrank Van Der Linden static uint_t nunity; 113*9e986f0eSFrank Van Der Linden static char **xlate_driver_array; 114*9e986f0eSFrank Van Der Linden static uint_t nxlate; 1153a634bfcSVikram Hegde /* ###################### Utility routines ############################# */ 1163a634bfcSVikram Hegde 1173a634bfcSVikram Hegde /* 1183a634bfcSVikram Hegde * Check if the device has mobile 4 chipset 1193a634bfcSVikram Hegde */ 1203a634bfcSVikram Hegde static int 1213a634bfcSVikram Hegde check_mobile4(dev_info_t *dip, void *arg) 1223a634bfcSVikram Hegde { 1233a634bfcSVikram Hegde _NOTE(ARGUNUSED(arg)); 1243a634bfcSVikram Hegde int vendor, device; 1253a634bfcSVikram Hegde int *ip = (int *)arg; 1263a634bfcSVikram Hegde 1273a634bfcSVikram Hegde ASSERT(arg); 1283a634bfcSVikram Hegde 1293a634bfcSVikram Hegde vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1303a634bfcSVikram Hegde "vendor-id", -1); 1313a634bfcSVikram Hegde device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1323a634bfcSVikram Hegde "device-id", -1); 1333a634bfcSVikram Hegde 1343a634bfcSVikram Hegde if (vendor == 0x8086 && device == 0x2a40) { 1353a634bfcSVikram Hegde *ip = B_TRUE; 1363a634bfcSVikram Hegde ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. " 1373a634bfcSVikram Hegde "Force setting IOMMU write buffer"); 1383a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 1393a634bfcSVikram Hegde } else { 1403a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 1413a634bfcSVikram Hegde } 1423a634bfcSVikram Hegde } 1433a634bfcSVikram Hegde 1443a634bfcSVikram Hegde static void 1453a634bfcSVikram Hegde map_bios_rsvd_mem(dev_info_t *dip) 1463a634bfcSVikram Hegde { 1473a634bfcSVikram Hegde struct memlist *mp; 1483a634bfcSVikram Hegde int e; 1493a634bfcSVikram Hegde 1503a634bfcSVikram Hegde memlist_read_lock(); 1513a634bfcSVikram Hegde 1523a634bfcSVikram Hegde mp = bios_rsvd; 1533a634bfcSVikram Hegde while (mp != NULL) { 154e03dceedSVikram Hegde memrng_t mrng = {0}; 1553a634bfcSVikram Hegde 1563a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range " 1573a634bfcSVikram Hegde "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address, 1583a634bfcSVikram Hegde mp->ml_address + mp->ml_size); 1593a634bfcSVikram Hegde 160e03dceedSVikram Hegde mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address); 161e03dceedSVikram Hegde mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE; 1623a634bfcSVikram Hegde 163e03dceedSVikram Hegde e = immu_dvma_map(NULL, NULL, &mrng, 0, dip, IMMU_FLAGS_MEMRNG); 1643a634bfcSVikram Hegde ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL); 1653a634bfcSVikram Hegde 1663a634bfcSVikram Hegde mp = mp->ml_next; 1673a634bfcSVikram Hegde } 1683a634bfcSVikram Hegde 1693a634bfcSVikram Hegde memlist_read_unlock(); 1703a634bfcSVikram Hegde } 1713a634bfcSVikram Hegde 172e03dceedSVikram Hegde 173e03dceedSVikram Hegde /* 174*9e986f0eSFrank Van Der Linden * Check if the driver requests a specific type of mapping. 175e03dceedSVikram Hegde */ 176e03dceedSVikram Hegde /*ARGSUSED*/ 177e03dceedSVikram Hegde static void 178*9e986f0eSFrank Van Der Linden check_conf(dev_info_t *dip, void *arg) 179e03dceedSVikram Hegde { 180*9e986f0eSFrank Van Der Linden immu_devi_t *immu_devi; 181*9e986f0eSFrank Van Der Linden const char *dname; 182*9e986f0eSFrank Van Der Linden uint_t i; 183*9e986f0eSFrank Van Der Linden int hasprop = 0; 184e03dceedSVikram Hegde 185e03dceedSVikram Hegde /* 186*9e986f0eSFrank Van Der Linden * Only PCI devices can use an IOMMU. Legacy ISA devices 187*9e986f0eSFrank Van Der Linden * are handled in check_lpc. 188e03dceedSVikram Hegde */ 189*9e986f0eSFrank Van Der Linden if (!DEVI_IS_PCI(dip)) 190*9e986f0eSFrank Van Der Linden return; 191e03dceedSVikram Hegde 192*9e986f0eSFrank Van Der Linden dname = ddi_driver_name(dip); 193*9e986f0eSFrank Van Der Linden if (dname == NULL) 194*9e986f0eSFrank Van Der Linden return; 195*9e986f0eSFrank Van Der Linden immu_devi = immu_devi_get(dip); 196*9e986f0eSFrank Van Der Linden 197*9e986f0eSFrank Van Der Linden for (i = 0; i < nunity; i++) { 198*9e986f0eSFrank Van Der Linden if (strcmp(unity_driver_array[i], dname) == 0) { 199*9e986f0eSFrank Van Der Linden hasprop = 1; 200*9e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 201e03dceedSVikram Hegde } 202e03dceedSVikram Hegde } 203*9e986f0eSFrank Van Der Linden 204*9e986f0eSFrank Van Der Linden for (i = 0; i < nxlate; i++) { 205*9e986f0eSFrank Van Der Linden if (strcmp(xlate_driver_array[i], dname) == 0) { 206*9e986f0eSFrank Van Der Linden hasprop = 1; 207*9e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags &= ~IMMU_FLAGS_UNITY; 208*9e986f0eSFrank Van Der Linden } 209*9e986f0eSFrank Van Der Linden } 210*9e986f0eSFrank Van Der Linden 211*9e986f0eSFrank Van Der Linden /* 212*9e986f0eSFrank Van Der Linden * Report if we changed the value from the default. 213*9e986f0eSFrank Van Der Linden */ 214*9e986f0eSFrank Van Der Linden if (hasprop && (immu_devi->imd_dvma_flags ^ immu_global_dvma_flags)) 215*9e986f0eSFrank Van Der Linden ddi_err(DER_LOG, dip, "using %s DVMA mapping", 216*9e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY ? 217*9e986f0eSFrank Van Der Linden DDI_DVMA_MAPTYPE_UNITY : DDI_DVMA_MAPTYPE_XLATE); 218e03dceedSVikram Hegde } 219e03dceedSVikram Hegde 2203a634bfcSVikram Hegde /* 2213a634bfcSVikram Hegde * Check if the device is USB controller 2223a634bfcSVikram Hegde */ 2233a634bfcSVikram Hegde /*ARGSUSED*/ 2243a634bfcSVikram Hegde static void 2253a634bfcSVikram Hegde check_usb(dev_info_t *dip, void *arg) 2263a634bfcSVikram Hegde { 2273a634bfcSVikram Hegde const char *drv = ddi_driver_name(dip); 228*9e986f0eSFrank Van Der Linden immu_devi_t *immu_devi; 229*9e986f0eSFrank Van Der Linden 2303a634bfcSVikram Hegde 2313a634bfcSVikram Hegde if (drv == NULL || 2323a634bfcSVikram Hegde (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 && 2333a634bfcSVikram Hegde strcmp(drv, "ehci") != 0)) { 2343a634bfcSVikram Hegde return; 2353a634bfcSVikram Hegde } 2363a634bfcSVikram Hegde 237*9e986f0eSFrank Van Der Linden immu_devi = immu_devi_get(dip); 238*9e986f0eSFrank Van Der Linden 239*9e986f0eSFrank Van Der Linden /* 240*9e986f0eSFrank Van Der Linden * If unit mappings are already specified, globally or 241*9e986f0eSFrank Van Der Linden * locally, we're done here, since that covers both 242*9e986f0eSFrank Van Der Linden * quirks below. 243*9e986f0eSFrank Van Der Linden */ 244*9e986f0eSFrank Van Der Linden if (immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY) 245*9e986f0eSFrank Van Der Linden return; 246*9e986f0eSFrank Van Der Linden 2473a634bfcSVikram Hegde /* This must come first since it does unity mapping */ 2483a634bfcSVikram Hegde if (immu_quirk_usbfullpa == B_TRUE) { 249*9e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 250*9e986f0eSFrank Van Der Linden } else if (immu_quirk_usbrmrr == B_TRUE) { 2513a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "Applying USB RMRR quirk"); 2523a634bfcSVikram Hegde map_bios_rsvd_mem(dip); 2533a634bfcSVikram Hegde } 2543a634bfcSVikram Hegde } 2553a634bfcSVikram Hegde 2563a634bfcSVikram Hegde /* 2573a634bfcSVikram Hegde * Check if the device is a LPC device 2583a634bfcSVikram Hegde */ 2593a634bfcSVikram Hegde /*ARGSUSED*/ 2603a634bfcSVikram Hegde static void 2613a634bfcSVikram Hegde check_lpc(dev_info_t *dip, void *arg) 2623a634bfcSVikram Hegde { 2633a634bfcSVikram Hegde immu_devi_t *immu_devi; 2643a634bfcSVikram Hegde 2653a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 2663a634bfcSVikram Hegde ASSERT(immu_devi); 2673a634bfcSVikram Hegde if (immu_devi->imd_lpc == B_TRUE) { 2683a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Found LPC device"); 2693a634bfcSVikram Hegde /* This will put the immu_devi on the LPC "specials" list */ 2703a634bfcSVikram Hegde (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 2713a634bfcSVikram Hegde } 2723a634bfcSVikram Hegde } 2733a634bfcSVikram Hegde 2743a634bfcSVikram Hegde /* 2753a634bfcSVikram Hegde * Check if the device is a GFX device 2763a634bfcSVikram Hegde */ 2773a634bfcSVikram Hegde /*ARGSUSED*/ 2783a634bfcSVikram Hegde static void 2793a634bfcSVikram Hegde check_gfx(dev_info_t *dip, void *arg) 2803a634bfcSVikram Hegde { 2813a634bfcSVikram Hegde immu_devi_t *immu_devi; 2823a634bfcSVikram Hegde 2833a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 2843a634bfcSVikram Hegde ASSERT(immu_devi); 2853a634bfcSVikram Hegde if (immu_devi->imd_display == B_TRUE) { 286*9e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 2873a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Found GFX device"); 2883a634bfcSVikram Hegde /* This will put the immu_devi on the GFX "specials" list */ 2893a634bfcSVikram Hegde (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 2903a634bfcSVikram Hegde } 2913a634bfcSVikram Hegde } 2923a634bfcSVikram Hegde 2933a634bfcSVikram Hegde static void 2943a634bfcSVikram Hegde walk_tree(int (*f)(dev_info_t *, void *), void *arg) 2953a634bfcSVikram Hegde { 2963a634bfcSVikram Hegde int count; 2973a634bfcSVikram Hegde 2983a634bfcSVikram Hegde ndi_devi_enter(root_devinfo, &count); 2993a634bfcSVikram Hegde ddi_walk_devs(ddi_get_child(root_devinfo), f, arg); 3003a634bfcSVikram Hegde ndi_devi_exit(root_devinfo, count); 3013a634bfcSVikram Hegde } 3023a634bfcSVikram Hegde 3033a634bfcSVikram Hegde static int 3043a634bfcSVikram Hegde check_pre_setup_quirks(dev_info_t *dip, void *arg) 3053a634bfcSVikram Hegde { 3063a634bfcSVikram Hegde /* just 1 check right now */ 3073a634bfcSVikram Hegde return (check_mobile4(dip, arg)); 3083a634bfcSVikram Hegde } 3093a634bfcSVikram Hegde 3103a634bfcSVikram Hegde static int 3113a634bfcSVikram Hegde check_pre_startup_quirks(dev_info_t *dip, void *arg) 3123a634bfcSVikram Hegde { 3133a634bfcSVikram Hegde if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) { 3143a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "Failed to get immu_devi"); 3153a634bfcSVikram Hegde } 3163a634bfcSVikram Hegde 3173a634bfcSVikram Hegde check_gfx(dip, arg); 3183a634bfcSVikram Hegde 3193a634bfcSVikram Hegde check_lpc(dip, arg); 3203a634bfcSVikram Hegde 321*9e986f0eSFrank Van Der Linden check_conf(dip, arg); 3223a634bfcSVikram Hegde 323*9e986f0eSFrank Van Der Linden check_usb(dip, arg); 324e03dceedSVikram Hegde 3253a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 3263a634bfcSVikram Hegde } 3273a634bfcSVikram Hegde 3283a634bfcSVikram Hegde static void 3293a634bfcSVikram Hegde pre_setup_quirks(void) 3303a634bfcSVikram Hegde { 3313a634bfcSVikram Hegde walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4); 3323a634bfcSVikram Hegde } 3333a634bfcSVikram Hegde 3343a634bfcSVikram Hegde static void 3353a634bfcSVikram Hegde pre_startup_quirks(void) 3363a634bfcSVikram Hegde { 3373a634bfcSVikram Hegde walk_tree(check_pre_startup_quirks, NULL); 3383a634bfcSVikram Hegde 3393a634bfcSVikram Hegde immu_dmar_rmrr_map(); 3403a634bfcSVikram Hegde } 3413a634bfcSVikram Hegde 342*9e986f0eSFrank Van Der Linden static int 343*9e986f0eSFrank Van Der Linden get_conf_str(char *bopt, char **val) 344*9e986f0eSFrank Van Der Linden { 345*9e986f0eSFrank Van Der Linden int ret; 346*9e986f0eSFrank Van Der Linden 347*9e986f0eSFrank Van Der Linden /* 348*9e986f0eSFrank Van Der Linden * Check the rootnex.conf property 349*9e986f0eSFrank Van Der Linden * Fake up a dev_t since searching the global 350*9e986f0eSFrank Van Der Linden * property list needs it 351*9e986f0eSFrank Van Der Linden */ 352*9e986f0eSFrank Van Der Linden ret = ddi_prop_lookup_string( 353*9e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), 354*9e986f0eSFrank Van Der Linden root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 355*9e986f0eSFrank Van Der Linden bopt, val); 356*9e986f0eSFrank Van Der Linden 357*9e986f0eSFrank Van Der Linden return (ret); 358*9e986f0eSFrank Van Der Linden } 359*9e986f0eSFrank Van Der Linden 3603a634bfcSVikram Hegde /* 3613adb2334SVikram Hegde * get_conf_opt() 3623adb2334SVikram Hegde * get a rootnex.conf setting (always a boolean) 3633adb2334SVikram Hegde */ 3643adb2334SVikram Hegde static void 3653adb2334SVikram Hegde get_conf_opt(char *bopt, boolean_t *kvar) 3663adb2334SVikram Hegde { 3673adb2334SVikram Hegde char *val = NULL; 3683adb2334SVikram Hegde 3693adb2334SVikram Hegde ASSERT(bopt); 3703adb2334SVikram Hegde ASSERT(kvar); 3713adb2334SVikram Hegde 3723adb2334SVikram Hegde /* 3733adb2334SVikram Hegde * Check the rootnex.conf property 3743adb2334SVikram Hegde * Fake up a dev_t since searching the global 3753adb2334SVikram Hegde * property list needs it 3763adb2334SVikram Hegde */ 3773adb2334SVikram Hegde 378*9e986f0eSFrank Van Der Linden if (get_conf_str(bopt, &val) != DDI_PROP_SUCCESS) 379*9e986f0eSFrank Van Der Linden return; 380*9e986f0eSFrank Van Der Linden 3813adb2334SVikram Hegde if (strcmp(val, "true") == 0) { 3823adb2334SVikram Hegde *kvar = B_TRUE; 3833adb2334SVikram Hegde } else if (strcmp(val, "false") == 0) { 3843adb2334SVikram Hegde *kvar = B_FALSE; 3853adb2334SVikram Hegde } else { 3863adb2334SVikram Hegde ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ", 3873adb2334SVikram Hegde "is not set to true or false. Ignoring option.", 3883adb2334SVikram Hegde bopt, val); 3893adb2334SVikram Hegde } 3903adb2334SVikram Hegde ddi_prop_free(val); 3913adb2334SVikram Hegde } 3923adb2334SVikram Hegde 3933adb2334SVikram Hegde /* 3943a634bfcSVikram Hegde * get_bootopt() 3953a634bfcSVikram Hegde * check a boot option (always a boolean) 3963a634bfcSVikram Hegde */ 397*9e986f0eSFrank Van Der Linden static int 398*9e986f0eSFrank Van Der Linden get_boot_str(char *bopt, char **val) 399*9e986f0eSFrank Van Der Linden { 400*9e986f0eSFrank Van Der Linden int ret; 401*9e986f0eSFrank Van Der Linden 402*9e986f0eSFrank Van Der Linden ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo, 403*9e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS, bopt, val); 404*9e986f0eSFrank Van Der Linden 405*9e986f0eSFrank Van Der Linden return (ret); 406*9e986f0eSFrank Van Der Linden } 407*9e986f0eSFrank Van Der Linden 4083a634bfcSVikram Hegde static void 4093a634bfcSVikram Hegde get_bootopt(char *bopt, boolean_t *kvar) 4103a634bfcSVikram Hegde { 4113a634bfcSVikram Hegde char *val = NULL; 4123a634bfcSVikram Hegde 4133a634bfcSVikram Hegde /* 4143a634bfcSVikram Hegde * All boot options set at the GRUB menu become 4153a634bfcSVikram Hegde * properties on the rootnex. 4163a634bfcSVikram Hegde */ 417*9e986f0eSFrank Van Der Linden if (get_boot_str(bopt, &val) != DDI_PROP_SUCCESS) 418*9e986f0eSFrank Van Der Linden return; 419*9e986f0eSFrank Van Der Linden 4203a634bfcSVikram Hegde if (strcmp(val, "true") == 0) { 4213a634bfcSVikram Hegde *kvar = B_TRUE; 4223a634bfcSVikram Hegde } else if (strcmp(val, "false") == 0) { 4233a634bfcSVikram Hegde *kvar = B_FALSE; 4243a634bfcSVikram Hegde } else { 4253a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ", 4263a634bfcSVikram Hegde "is not set to true or false. Ignoring option.", 4273a634bfcSVikram Hegde bopt, val); 4283a634bfcSVikram Hegde } 4293a634bfcSVikram Hegde ddi_prop_free(val); 4303a634bfcSVikram Hegde } 431*9e986f0eSFrank Van Der Linden 432*9e986f0eSFrank Van Der Linden static void 433*9e986f0eSFrank Van Der Linden get_boot_dvma_mode(void) 434*9e986f0eSFrank Van Der Linden { 435*9e986f0eSFrank Van Der Linden char *val = NULL; 436*9e986f0eSFrank Van Der Linden 437*9e986f0eSFrank Van Der Linden if (get_boot_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val) 438*9e986f0eSFrank Van Der Linden != DDI_PROP_SUCCESS) 439*9e986f0eSFrank Van Der Linden return; 440*9e986f0eSFrank Van Der Linden 441*9e986f0eSFrank Van Der Linden if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) { 442*9e986f0eSFrank Van Der Linden immu_global_dvma_flags |= IMMU_FLAGS_UNITY; 443*9e986f0eSFrank Van Der Linden } else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) { 444*9e986f0eSFrank Van Der Linden immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY; 445*9e986f0eSFrank Van Der Linden } else { 446*9e986f0eSFrank Van Der Linden ddi_err(DER_WARN, NULL, "bad value \"%s\" for boot option %s", 447*9e986f0eSFrank Van Der Linden val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP); 4483a634bfcSVikram Hegde } 449*9e986f0eSFrank Van Der Linden ddi_prop_free(val); 450*9e986f0eSFrank Van Der Linden } 451*9e986f0eSFrank Van Der Linden 452*9e986f0eSFrank Van Der Linden static void 453*9e986f0eSFrank Van Der Linden get_conf_dvma_mode(void) 454*9e986f0eSFrank Van Der Linden { 455*9e986f0eSFrank Van Der Linden char *val = NULL; 456*9e986f0eSFrank Van Der Linden 457*9e986f0eSFrank Van Der Linden if (get_conf_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val) 458*9e986f0eSFrank Van Der Linden != DDI_PROP_SUCCESS) 459*9e986f0eSFrank Van Der Linden return; 460*9e986f0eSFrank Van Der Linden 461*9e986f0eSFrank Van Der Linden if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) { 462*9e986f0eSFrank Van Der Linden immu_global_dvma_flags |= IMMU_FLAGS_UNITY; 463*9e986f0eSFrank Van Der Linden } else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) { 464*9e986f0eSFrank Van Der Linden immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY; 465*9e986f0eSFrank Van Der Linden } else { 466*9e986f0eSFrank Van Der Linden ddi_err(DER_WARN, NULL, "bad value \"%s\" for rootnex " 467*9e986f0eSFrank Van Der Linden "option %s", val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP); 468*9e986f0eSFrank Van Der Linden } 469*9e986f0eSFrank Van Der Linden ddi_prop_free(val); 470*9e986f0eSFrank Van Der Linden } 471*9e986f0eSFrank Van Der Linden 4723a634bfcSVikram Hegde 4733a634bfcSVikram Hegde static void 4743adb2334SVikram Hegde get_conf_tunables(char *bopt, int64_t *ivar) 475e03dceedSVikram Hegde { 476e03dceedSVikram Hegde int64_t *iarray; 477e03dceedSVikram Hegde uint_t n; 478e03dceedSVikram Hegde 479e03dceedSVikram Hegde /* 480e03dceedSVikram Hegde * Check the rootnex.conf property 481e03dceedSVikram Hegde * Fake up a dev_t since searching the global 482e03dceedSVikram Hegde * property list needs it 483e03dceedSVikram Hegde */ 484e03dceedSVikram Hegde if (ddi_prop_lookup_int64_array( 485e03dceedSVikram Hegde makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 486e03dceedSVikram Hegde DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt, 487e03dceedSVikram Hegde &iarray, &n) != DDI_PROP_SUCCESS) { 488e03dceedSVikram Hegde return; 489e03dceedSVikram Hegde } 490e03dceedSVikram Hegde 491e03dceedSVikram Hegde if (n != 1) { 492e03dceedSVikram Hegde ddi_err(DER_WARN, NULL, "More than one value specified for " 493e03dceedSVikram Hegde "%s property. Ignoring and using default", 494e03dceedSVikram Hegde "immu-flush-gran"); 495e03dceedSVikram Hegde ddi_prop_free(iarray); 496e03dceedSVikram Hegde return; 497e03dceedSVikram Hegde } 498e03dceedSVikram Hegde 499e03dceedSVikram Hegde if (iarray[0] < 0) { 500e03dceedSVikram Hegde ddi_err(DER_WARN, NULL, "Negative value specified for " 501e03dceedSVikram Hegde "%s property. Inoring and Using default value", 502e03dceedSVikram Hegde "immu-flush-gran"); 503e03dceedSVikram Hegde ddi_prop_free(iarray); 504e03dceedSVikram Hegde return; 505e03dceedSVikram Hegde } 506e03dceedSVikram Hegde 507e03dceedSVikram Hegde *ivar = iarray[0]; 508e03dceedSVikram Hegde 509e03dceedSVikram Hegde ddi_prop_free(iarray); 510e03dceedSVikram Hegde } 511e03dceedSVikram Hegde 512e03dceedSVikram Hegde static void 5133adb2334SVikram Hegde read_conf_options(void) 5143adb2334SVikram Hegde { 5153adb2334SVikram Hegde /* enable/disable options */ 5163adb2334SVikram Hegde get_conf_opt("immu-enable", &immu_enable); 5173adb2334SVikram Hegde get_conf_opt("immu-dvma-enable", &immu_dvma_enable); 5183adb2334SVikram Hegde get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 5193adb2334SVikram Hegde get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable); 5203adb2334SVikram Hegde get_conf_opt("immu-qinv-enable", &immu_qinv_enable); 5213adb2334SVikram Hegde 5223adb2334SVikram Hegde /* workaround switches */ 5233adb2334SVikram Hegde get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 5243adb2334SVikram Hegde get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 5253adb2334SVikram Hegde get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 5263adb2334SVikram Hegde 5273adb2334SVikram Hegde /* debug printing */ 5283adb2334SVikram Hegde get_conf_opt("immu-dmar-print", &immu_dmar_print); 5293adb2334SVikram Hegde 5303adb2334SVikram Hegde /* get tunables */ 5313adb2334SVikram Hegde get_conf_tunables("immu-flush-gran", &immu_flush_gran); 532*9e986f0eSFrank Van Der Linden 533*9e986f0eSFrank Van Der Linden get_conf_dvma_mode(); 5343adb2334SVikram Hegde } 5353adb2334SVikram Hegde 5363adb2334SVikram Hegde static void 5373a634bfcSVikram Hegde read_boot_options(void) 5383a634bfcSVikram Hegde { 5393a634bfcSVikram Hegde /* enable/disable options */ 5403a634bfcSVikram Hegde get_bootopt("immu-enable", &immu_enable); 5413a634bfcSVikram Hegde get_bootopt("immu-dvma-enable", &immu_dvma_enable); 5423a634bfcSVikram Hegde get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 5433a634bfcSVikram Hegde get_bootopt("immu-intrmap-enable", &immu_intrmap_enable); 5443a634bfcSVikram Hegde get_bootopt("immu-qinv-enable", &immu_qinv_enable); 5453a634bfcSVikram Hegde 5463a634bfcSVikram Hegde /* workaround switches */ 5473a634bfcSVikram Hegde get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 5483a634bfcSVikram Hegde get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 5493a634bfcSVikram Hegde get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 5503a634bfcSVikram Hegde 5513a634bfcSVikram Hegde /* debug printing */ 5523a634bfcSVikram Hegde get_bootopt("immu-dmar-print", &immu_dmar_print); 553*9e986f0eSFrank Van Der Linden 554*9e986f0eSFrank Van Der Linden get_boot_dvma_mode(); 555*9e986f0eSFrank Van Der Linden } 556*9e986f0eSFrank Van Der Linden 557*9e986f0eSFrank Van Der Linden static void 558*9e986f0eSFrank Van Der Linden mapping_list_setup(void) 559*9e986f0eSFrank Van Der Linden { 560*9e986f0eSFrank Van Der Linden char **string_array; 561*9e986f0eSFrank Van Der Linden uint_t nstrings; 562*9e986f0eSFrank Van Der Linden 563*9e986f0eSFrank Van Der Linden if (ddi_prop_lookup_string_array( 564*9e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 565*9e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 566*9e986f0eSFrank Van Der Linden "immu-dvma-unity-drivers", 567*9e986f0eSFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 568*9e986f0eSFrank Van Der Linden unity_driver_array = string_array; 569*9e986f0eSFrank Van Der Linden nunity = nstrings; 570*9e986f0eSFrank Van Der Linden } 571*9e986f0eSFrank Van Der Linden 572*9e986f0eSFrank Van Der Linden if (ddi_prop_lookup_string_array( 573*9e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 574*9e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 575*9e986f0eSFrank Van Der Linden "immu-dvma-xlate-drivers", 576*9e986f0eSFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 577*9e986f0eSFrank Van Der Linden xlate_driver_array = string_array; 578*9e986f0eSFrank Van Der Linden nxlate = nstrings; 579*9e986f0eSFrank Van Der Linden } 5803a634bfcSVikram Hegde } 5813a634bfcSVikram Hegde 5823a634bfcSVikram Hegde /* 5833a634bfcSVikram Hegde * Note, this will not catch hardware not enumerated 5843a634bfcSVikram Hegde * in early boot 5853a634bfcSVikram Hegde */ 5863a634bfcSVikram Hegde static boolean_t 5873a634bfcSVikram Hegde blacklisted_driver(void) 5883a634bfcSVikram Hegde { 5893a634bfcSVikram Hegde char **strptr; 5903a634bfcSVikram Hegde int i; 5913a634bfcSVikram Hegde major_t maj; 5923a634bfcSVikram Hegde 5933a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 5943a634bfcSVikram Hegde 5953a634bfcSVikram Hegde /* need at least 2 strings */ 5963a634bfcSVikram Hegde if (nblacks < 2) { 5973a634bfcSVikram Hegde return (B_FALSE); 5983a634bfcSVikram Hegde } 5993a634bfcSVikram Hegde 6003a634bfcSVikram Hegde for (i = 0; nblacks - i > 1; i++) { 601e03dceedSVikram Hegde strptr = &black_array[i]; 6023a634bfcSVikram Hegde if (strcmp(*strptr++, "DRIVER") == 0) { 6033a634bfcSVikram Hegde if ((maj = ddi_name_to_major(*strptr++)) 6043a634bfcSVikram Hegde != DDI_MAJOR_T_NONE) { 6053a634bfcSVikram Hegde /* is there hardware bound to this drvr */ 6063a634bfcSVikram Hegde if (devnamesp[maj].dn_head != NULL) { 6073a634bfcSVikram Hegde return (B_TRUE); 6083a634bfcSVikram Hegde } 6093a634bfcSVikram Hegde } 6103a634bfcSVikram Hegde i += 1; /* for loop adds 1, so add only 1 here */ 6113a634bfcSVikram Hegde } 6123a634bfcSVikram Hegde } 6133a634bfcSVikram Hegde 6143a634bfcSVikram Hegde return (B_FALSE); 6153a634bfcSVikram Hegde } 6163a634bfcSVikram Hegde 6173a634bfcSVikram Hegde static boolean_t 6183a634bfcSVikram Hegde blacklisted_smbios(void) 6193a634bfcSVikram Hegde { 6203a634bfcSVikram Hegde id_t smid; 6213a634bfcSVikram Hegde smbios_hdl_t *smhdl; 6223a634bfcSVikram Hegde smbios_info_t sminf; 6233a634bfcSVikram Hegde smbios_system_t smsys; 6243a634bfcSVikram Hegde char *mfg, *product, *version; 6253a634bfcSVikram Hegde char **strptr; 6263a634bfcSVikram Hegde int i; 6273a634bfcSVikram Hegde 6283a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 6293a634bfcSVikram Hegde 6303a634bfcSVikram Hegde /* need at least 4 strings for this setting */ 6313a634bfcSVikram Hegde if (nblacks < 4) { 6323a634bfcSVikram Hegde return (B_FALSE); 6333a634bfcSVikram Hegde } 6343a634bfcSVikram Hegde 6353a634bfcSVikram Hegde smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL); 6363a634bfcSVikram Hegde if (smhdl == NULL || 6373a634bfcSVikram Hegde (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR || 6383a634bfcSVikram Hegde smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) { 6393a634bfcSVikram Hegde return (B_FALSE); 6403a634bfcSVikram Hegde } 6413a634bfcSVikram Hegde 6423a634bfcSVikram Hegde mfg = (char *)sminf.smbi_manufacturer; 6433a634bfcSVikram Hegde product = (char *)sminf.smbi_product; 6443a634bfcSVikram Hegde version = (char *)sminf.smbi_version; 6453a634bfcSVikram Hegde 6463a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n"); 6473a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg); 6483a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product); 6493a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version); 6503a634bfcSVikram Hegde 6513a634bfcSVikram Hegde for (i = 0; nblacks - i > 3; i++) { 652e03dceedSVikram Hegde strptr = &black_array[i]; 6533a634bfcSVikram Hegde if (strcmp(*strptr++, "SMBIOS") == 0) { 6543a634bfcSVikram Hegde if (strcmp(*strptr++, mfg) == 0 && 6553a634bfcSVikram Hegde ((char *)strptr == '\0' || 6563a634bfcSVikram Hegde strcmp(*strptr++, product) == 0) && 6573a634bfcSVikram Hegde ((char *)strptr == '\0' || 6583a634bfcSVikram Hegde strcmp(*strptr++, version) == 0)) { 6593a634bfcSVikram Hegde return (B_TRUE); 6603a634bfcSVikram Hegde } 6613a634bfcSVikram Hegde i += 3; 6623a634bfcSVikram Hegde } 6633a634bfcSVikram Hegde } 6643a634bfcSVikram Hegde 6653a634bfcSVikram Hegde return (B_FALSE); 6663a634bfcSVikram Hegde } 6673a634bfcSVikram Hegde 6683a634bfcSVikram Hegde static boolean_t 6693a634bfcSVikram Hegde blacklisted_acpi(void) 6703a634bfcSVikram Hegde { 6713a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 6723a634bfcSVikram Hegde if (nblacks == 0) { 6733a634bfcSVikram Hegde return (B_FALSE); 6743a634bfcSVikram Hegde } 6753a634bfcSVikram Hegde 6763a634bfcSVikram Hegde return (immu_dmar_blacklisted(black_array, nblacks)); 6773a634bfcSVikram Hegde } 6783a634bfcSVikram Hegde 6793a634bfcSVikram Hegde /* 6803a634bfcSVikram Hegde * Check if system is blacklisted by Intel IOMMU driver 6813a634bfcSVikram Hegde * i.e. should Intel IOMMU be disabled on this system 6823a634bfcSVikram Hegde * Currently a system can be blacklistd based on the 6833a634bfcSVikram Hegde * following bases: 6843a634bfcSVikram Hegde * 6853a634bfcSVikram Hegde * 1. DMAR ACPI table information. 6863a634bfcSVikram Hegde * This information includes things like 6873a634bfcSVikram Hegde * manufacturer and revision number. If rootnex.conf 6883a634bfcSVikram Hegde * has matching info set in its blacklist property 6893a634bfcSVikram Hegde * then Intel IOMMu will be disabled 6903a634bfcSVikram Hegde * 6913a634bfcSVikram Hegde * 2. SMBIOS information 6923a634bfcSVikram Hegde * 6933a634bfcSVikram Hegde * 3. Driver installed - useful if a particular 6943a634bfcSVikram Hegde * driver or hardware is toxic if Intel IOMMU 6953a634bfcSVikram Hegde * is turned on. 6963a634bfcSVikram Hegde */ 6973a634bfcSVikram Hegde 6983a634bfcSVikram Hegde static void 6993a634bfcSVikram Hegde blacklist_setup(void) 7003a634bfcSVikram Hegde { 7013a634bfcSVikram Hegde char **string_array; 7023a634bfcSVikram Hegde uint_t nstrings; 7033a634bfcSVikram Hegde 7043a634bfcSVikram Hegde /* 7053a634bfcSVikram Hegde * Check the rootnex.conf blacklist property. 7063a634bfcSVikram Hegde * Fake up a dev_t since searching the global 7073a634bfcSVikram Hegde * property list needs it 7083a634bfcSVikram Hegde */ 7093a634bfcSVikram Hegde if (ddi_prop_lookup_string_array( 7103a634bfcSVikram Hegde makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 7113a634bfcSVikram Hegde DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist", 7123a634bfcSVikram Hegde &string_array, &nstrings) != DDI_PROP_SUCCESS) { 7133a634bfcSVikram Hegde return; 7143a634bfcSVikram Hegde } 7153a634bfcSVikram Hegde 7163a634bfcSVikram Hegde /* smallest blacklist criteria works with multiples of 2 */ 7173a634bfcSVikram Hegde if (nstrings % 2 != 0) { 7183a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist " 7193a634bfcSVikram Hegde "rootnex.conf: number of strings must be a " 7203a634bfcSVikram Hegde "multiple of 2"); 7213a634bfcSVikram Hegde ddi_prop_free(string_array); 7223a634bfcSVikram Hegde return; 7233a634bfcSVikram Hegde } 7243a634bfcSVikram Hegde 7253a634bfcSVikram Hegde black_array = string_array; 7263a634bfcSVikram Hegde nblacks = nstrings; 7273a634bfcSVikram Hegde } 7283a634bfcSVikram Hegde 7293a634bfcSVikram Hegde static void 7303a634bfcSVikram Hegde blacklist_destroy(void) 7313a634bfcSVikram Hegde { 7323a634bfcSVikram Hegde if (black_array) { 7333a634bfcSVikram Hegde ddi_prop_free(black_array); 7343a634bfcSVikram Hegde black_array = NULL; 7353a634bfcSVikram Hegde nblacks = 0; 7363a634bfcSVikram Hegde } 7373a634bfcSVikram Hegde 7383a634bfcSVikram Hegde ASSERT(black_array == NULL); 7393a634bfcSVikram Hegde ASSERT(nblacks == 0); 7403a634bfcSVikram Hegde } 7413a634bfcSVikram Hegde 7423a634bfcSVikram Hegde 7433a634bfcSVikram Hegde /* 7443a634bfcSVikram Hegde * Now set all the fields in the order they are defined 7453a634bfcSVikram Hegde * We do this only as a defensive-coding practice, it is 7463a634bfcSVikram Hegde * not a correctness issue. 7473a634bfcSVikram Hegde */ 7483a634bfcSVikram Hegde static void * 7493a634bfcSVikram Hegde immu_state_alloc(int seg, void *dmar_unit) 7503a634bfcSVikram Hegde { 7513a634bfcSVikram Hegde immu_t *immu; 7523a634bfcSVikram Hegde 7533a634bfcSVikram Hegde dmar_unit = immu_dmar_walk_units(seg, dmar_unit); 7543a634bfcSVikram Hegde if (dmar_unit == NULL) { 7553a634bfcSVikram Hegde /* No more IOMMUs in this segment */ 7563a634bfcSVikram Hegde return (NULL); 7573a634bfcSVikram Hegde } 7583a634bfcSVikram Hegde 7593a634bfcSVikram Hegde immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP); 7603a634bfcSVikram Hegde 7613a634bfcSVikram Hegde mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL); 7623a634bfcSVikram Hegde 7633a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 7643a634bfcSVikram Hegde 7653a634bfcSVikram Hegde immu->immu_dmar_unit = dmar_unit; 7663a634bfcSVikram Hegde immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit), 7673a634bfcSVikram Hegde KM_SLEEP); 7683a634bfcSVikram Hegde immu->immu_dip = immu_dmar_unit_dip(dmar_unit); 7693a634bfcSVikram Hegde 7703a634bfcSVikram Hegde /* 7713a634bfcSVikram Hegde * the immu_intr_lock mutex is grabbed by the IOMMU 7723a634bfcSVikram Hegde * unit's interrupt handler so we need to use an 7733a634bfcSVikram Hegde * interrupt cookie for the mutex 7743a634bfcSVikram Hegde */ 7753a634bfcSVikram Hegde mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER, 7763a634bfcSVikram Hegde (void *)ipltospl(IMMU_INTR_IPL)); 7773a634bfcSVikram Hegde 7783a634bfcSVikram Hegde /* IOMMU regs related */ 7793a634bfcSVikram Hegde mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL); 780e03dceedSVikram Hegde cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL); 781e03dceedSVikram Hegde immu->immu_regs_busy = B_FALSE; 7823a634bfcSVikram Hegde 7833a634bfcSVikram Hegde /* DVMA related */ 7843a634bfcSVikram Hegde immu->immu_dvma_coherent = B_FALSE; 7853a634bfcSVikram Hegde 7863a634bfcSVikram Hegde /* DVMA context related */ 7873a634bfcSVikram Hegde rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL); 7883a634bfcSVikram Hegde 7893a634bfcSVikram Hegde /* DVMA domain related */ 7903a634bfcSVikram Hegde list_create(&(immu->immu_domain_list), sizeof (domain_t), 7913a634bfcSVikram Hegde offsetof(domain_t, dom_immu_node)); 7923a634bfcSVikram Hegde 7933a634bfcSVikram Hegde /* DVMA special device lists */ 7943a634bfcSVikram Hegde immu->immu_dvma_gfx_only = B_FALSE; 7953a634bfcSVikram Hegde list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t), 7963a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 7973a634bfcSVikram Hegde list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t), 7983a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 7993a634bfcSVikram Hegde 8003a634bfcSVikram Hegde /* interrupt remapping related */ 8013a634bfcSVikram Hegde mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL); 8023a634bfcSVikram Hegde 8033a634bfcSVikram Hegde /* qinv related */ 8043a634bfcSVikram Hegde mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL); 8053a634bfcSVikram Hegde 8063a634bfcSVikram Hegde /* 8073a634bfcSVikram Hegde * insert this immu unit into the system-wide list 8083a634bfcSVikram Hegde */ 8093a634bfcSVikram Hegde list_insert_tail(&immu_list, immu); 8103a634bfcSVikram Hegde 8113a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 8123a634bfcSVikram Hegde 8133a634bfcSVikram Hegde ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup"); 8143a634bfcSVikram Hegde 8153a634bfcSVikram Hegde immu_dmar_set_immu(dmar_unit, immu); 8163a634bfcSVikram Hegde 8173a634bfcSVikram Hegde return (dmar_unit); 8183a634bfcSVikram Hegde } 8193a634bfcSVikram Hegde 8203a634bfcSVikram Hegde static void 8213a634bfcSVikram Hegde immu_subsystems_setup(void) 8223a634bfcSVikram Hegde { 8233a634bfcSVikram Hegde int seg; 8243a634bfcSVikram Hegde void *unit_hdl; 8253a634bfcSVikram Hegde 8263a634bfcSVikram Hegde ddi_err(DER_VERB, NULL, 8273a634bfcSVikram Hegde "Creating state structures for Intel IOMMU units\n"); 8283a634bfcSVikram Hegde 8293a634bfcSVikram Hegde ASSERT(immu_setup == B_FALSE); 8303a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 8313a634bfcSVikram Hegde 8323a634bfcSVikram Hegde mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL); 8333a634bfcSVikram Hegde list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node)); 8343a634bfcSVikram Hegde 8353a634bfcSVikram Hegde mutex_enter(&immu_lock); 8363a634bfcSVikram Hegde 837e03dceedSVikram Hegde ASSERT(immu_pgtable_cache == NULL); 838e03dceedSVikram Hegde 839e03dceedSVikram Hegde immu_pgtable_cache = kmem_cache_create("immu_pgtable_cache", 840e03dceedSVikram Hegde sizeof (pgtable_t), 0, 841e03dceedSVikram Hegde pgtable_ctor, pgtable_dtor, NULL, NULL, NULL, 0); 842e03dceedSVikram Hegde 8433a634bfcSVikram Hegde unit_hdl = NULL; 8443a634bfcSVikram Hegde for (seg = 0; seg < IMMU_MAXSEG; seg++) { 8453a634bfcSVikram Hegde while (unit_hdl = immu_state_alloc(seg, unit_hdl)) { 8463a634bfcSVikram Hegde ; 8473a634bfcSVikram Hegde } 8483a634bfcSVikram Hegde } 8493a634bfcSVikram Hegde 8503a634bfcSVikram Hegde immu_regs_setup(&immu_list); /* subsequent code needs this first */ 8513a634bfcSVikram Hegde immu_dvma_setup(&immu_list); 8523a634bfcSVikram Hegde immu_intrmap_setup(&immu_list); 8533a634bfcSVikram Hegde immu_qinv_setup(&immu_list); 8543a634bfcSVikram Hegde 8553a634bfcSVikram Hegde mutex_exit(&immu_lock); 8563a634bfcSVikram Hegde } 8573a634bfcSVikram Hegde 8583a634bfcSVikram Hegde /* 8593a634bfcSVikram Hegde * immu_subsystems_startup() 8603a634bfcSVikram Hegde * startup all units that were setup 8613a634bfcSVikram Hegde */ 8623a634bfcSVikram Hegde static void 8633a634bfcSVikram Hegde immu_subsystems_startup(void) 8643a634bfcSVikram Hegde { 8653a634bfcSVikram Hegde immu_t *immu; 8663a634bfcSVikram Hegde 8673a634bfcSVikram Hegde mutex_enter(&immu_lock); 8683a634bfcSVikram Hegde 8693a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 8703a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 8713a634bfcSVikram Hegde 8723a634bfcSVikram Hegde immu_dmar_startup(); 8733a634bfcSVikram Hegde 8743a634bfcSVikram Hegde immu = list_head(&immu_list); 8753a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 8763a634bfcSVikram Hegde 8773a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 8783a634bfcSVikram Hegde 8793a634bfcSVikram Hegde immu_intr_register(immu); 8803a634bfcSVikram Hegde immu_dvma_startup(immu); 8813a634bfcSVikram Hegde immu_intrmap_startup(immu); 8823a634bfcSVikram Hegde immu_qinv_startup(immu); 8833a634bfcSVikram Hegde 8843a634bfcSVikram Hegde /* 8853a634bfcSVikram Hegde * Set IOMMU unit's regs to do 8863a634bfcSVikram Hegde * the actual startup. This will 8873a634bfcSVikram Hegde * set immu->immu_running field 8883a634bfcSVikram Hegde * if the unit is successfully 8893a634bfcSVikram Hegde * started 8903a634bfcSVikram Hegde */ 8913a634bfcSVikram Hegde immu_regs_startup(immu); 8923a634bfcSVikram Hegde 8933a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 8943a634bfcSVikram Hegde } 8953a634bfcSVikram Hegde 8963a634bfcSVikram Hegde mutex_exit(&immu_lock); 8973a634bfcSVikram Hegde } 8983a634bfcSVikram Hegde 8993a634bfcSVikram Hegde /* ################## Intel IOMMU internal interfaces ###################### */ 9003a634bfcSVikram Hegde 9013a634bfcSVikram Hegde /* 9023a634bfcSVikram Hegde * Internal interfaces for IOMMU code (i.e. not exported to rootnex 9033a634bfcSVikram Hegde * or rest of system) 9043a634bfcSVikram Hegde */ 9053a634bfcSVikram Hegde 9063a634bfcSVikram Hegde /* 9073a634bfcSVikram Hegde * ddip can be NULL, in which case we walk up until we find the root dip 9083a634bfcSVikram Hegde * NOTE: We never visit the root dip since its not a hardware node 9093a634bfcSVikram Hegde */ 9103a634bfcSVikram Hegde int 9113a634bfcSVikram Hegde immu_walk_ancestor( 9123a634bfcSVikram Hegde dev_info_t *rdip, 9133a634bfcSVikram Hegde dev_info_t *ddip, 9143a634bfcSVikram Hegde int (*func)(dev_info_t *, void *arg), 9153a634bfcSVikram Hegde void *arg, 9163a634bfcSVikram Hegde int *lvlp, 9173a634bfcSVikram Hegde immu_flags_t immu_flags) 9183a634bfcSVikram Hegde { 9193a634bfcSVikram Hegde dev_info_t *pdip; 9203a634bfcSVikram Hegde int level; 9213a634bfcSVikram Hegde int error = DDI_SUCCESS; 9223a634bfcSVikram Hegde 9233a634bfcSVikram Hegde ASSERT(root_devinfo); 9243a634bfcSVikram Hegde ASSERT(rdip); 9253a634bfcSVikram Hegde ASSERT(rdip != root_devinfo); 9263a634bfcSVikram Hegde ASSERT(func); 9273a634bfcSVikram Hegde 9283a634bfcSVikram Hegde /* ddip and immu can be NULL */ 9293a634bfcSVikram Hegde 9303a634bfcSVikram Hegde /* Hold rdip so that branch is not detached */ 9313a634bfcSVikram Hegde ndi_hold_devi(rdip); 9323a634bfcSVikram Hegde for (pdip = rdip, level = 1; pdip && pdip != root_devinfo; 9333a634bfcSVikram Hegde pdip = ddi_get_parent(pdip), level++) { 9343a634bfcSVikram Hegde 9353a634bfcSVikram Hegde if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) { 9363a634bfcSVikram Hegde error = DDI_FAILURE; 9373a634bfcSVikram Hegde break; 9383a634bfcSVikram Hegde } 9393a634bfcSVikram Hegde if (func(pdip, arg) == DDI_WALK_TERMINATE) { 9403a634bfcSVikram Hegde break; 9413a634bfcSVikram Hegde } 9423a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_DONTPASS) { 9433a634bfcSVikram Hegde break; 9443a634bfcSVikram Hegde } 9453a634bfcSVikram Hegde if (pdip == ddip) { 9463a634bfcSVikram Hegde break; 9473a634bfcSVikram Hegde } 9483a634bfcSVikram Hegde } 9493a634bfcSVikram Hegde 9503a634bfcSVikram Hegde ndi_rele_devi(rdip); 9513a634bfcSVikram Hegde 9523a634bfcSVikram Hegde if (lvlp) 9533a634bfcSVikram Hegde *lvlp = level; 9543a634bfcSVikram Hegde 9553a634bfcSVikram Hegde return (error); 9563a634bfcSVikram Hegde } 9573a634bfcSVikram Hegde 9583a634bfcSVikram Hegde /* ######################## Intel IOMMU entry points ####################### */ 9593a634bfcSVikram Hegde /* 9603a634bfcSVikram Hegde * immu_init() 9613a634bfcSVikram Hegde * called from rootnex_attach(). setup but don't startup the Intel IOMMU 9623a634bfcSVikram Hegde * This is the first function called in Intel IOMMU code 9633a634bfcSVikram Hegde */ 9643a634bfcSVikram Hegde void 9653a634bfcSVikram Hegde immu_init(void) 9663a634bfcSVikram Hegde { 9673a634bfcSVikram Hegde char *phony_reg = "A thing of beauty is a joy forever"; 9683a634bfcSVikram Hegde 9693a634bfcSVikram Hegde /* Set some global shorthands that are needed by all of IOMMU code */ 9703a634bfcSVikram Hegde ASSERT(root_devinfo == NULL); 9713a634bfcSVikram Hegde root_devinfo = ddi_root_node(); 9723a634bfcSVikram Hegde 9733a634bfcSVikram Hegde /* 9743a634bfcSVikram Hegde * Intel IOMMU only supported only if MMU(CPU) page size is == 9753a634bfcSVikram Hegde * IOMMU pages size. 9763a634bfcSVikram Hegde */ 9773a634bfcSVikram Hegde /*LINTED*/ 9783a634bfcSVikram Hegde if (MMU_PAGESIZE != IMMU_PAGESIZE) { 9793a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, 9803a634bfcSVikram Hegde "MMU page size (%d) is not equal to\n" 9813a634bfcSVikram Hegde "IOMMU page size (%d). " 9823a634bfcSVikram Hegde "Disabling Intel IOMMU. ", 9833a634bfcSVikram Hegde MMU_PAGESIZE, IMMU_PAGESIZE); 9843a634bfcSVikram Hegde immu_enable = B_FALSE; 9853a634bfcSVikram Hegde return; 9863a634bfcSVikram Hegde } 9873a634bfcSVikram Hegde 9883a634bfcSVikram Hegde /* 9893adb2334SVikram Hegde * Read rootnex.conf options. Do this before 9903adb2334SVikram Hegde * boot options so boot options can override .conf options. 9913adb2334SVikram Hegde */ 9923adb2334SVikram Hegde read_conf_options(); 9933adb2334SVikram Hegde 9943adb2334SVikram Hegde /* 9953a634bfcSVikram Hegde * retrieve the Intel IOMMU boot options. 9963a634bfcSVikram Hegde * Do this before parsing immu ACPI table 9973a634bfcSVikram Hegde * as a boot option could potentially affect 9983a634bfcSVikram Hegde * ACPI parsing. 9993a634bfcSVikram Hegde */ 10003a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n"); 10013a634bfcSVikram Hegde read_boot_options(); 10023a634bfcSVikram Hegde 10033a634bfcSVikram Hegde /* 10043a634bfcSVikram Hegde * Check the IOMMU enable boot-option first. 10053a634bfcSVikram Hegde * This is so that we can skip parsing the ACPI table 10063a634bfcSVikram Hegde * if necessary because that may cause problems in 10073a634bfcSVikram Hegde * systems with buggy BIOS or ACPI tables 10083a634bfcSVikram Hegde */ 10093a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 10103a634bfcSVikram Hegde return; 10113a634bfcSVikram Hegde } 10123a634bfcSVikram Hegde 10133a634bfcSVikram Hegde /* 10143a634bfcSVikram Hegde * Next, check if the system even has an Intel IOMMU 10153a634bfcSVikram Hegde * We use the presence or absence of the IOMMU ACPI 10163a634bfcSVikram Hegde * table to detect Intel IOMMU. 10173a634bfcSVikram Hegde */ 10183a634bfcSVikram Hegde if (immu_dmar_setup() != DDI_SUCCESS) { 10193a634bfcSVikram Hegde immu_enable = B_FALSE; 10203a634bfcSVikram Hegde return; 10213a634bfcSVikram Hegde } 10223a634bfcSVikram Hegde 1023*9e986f0eSFrank Van Der Linden mapping_list_setup(); 1024*9e986f0eSFrank Van Der Linden 10253a634bfcSVikram Hegde /* 10263a634bfcSVikram Hegde * Check blacklists 10273a634bfcSVikram Hegde */ 10283a634bfcSVikram Hegde blacklist_setup(); 10293a634bfcSVikram Hegde 10303a634bfcSVikram Hegde if (blacklisted_smbios() == B_TRUE) { 10313a634bfcSVikram Hegde blacklist_destroy(); 10323a634bfcSVikram Hegde immu_enable = B_FALSE; 10333a634bfcSVikram Hegde return; 10343a634bfcSVikram Hegde } 10353a634bfcSVikram Hegde 10363a634bfcSVikram Hegde if (blacklisted_driver() == B_TRUE) { 10373a634bfcSVikram Hegde blacklist_destroy(); 10383a634bfcSVikram Hegde immu_enable = B_FALSE; 10393a634bfcSVikram Hegde return; 10403a634bfcSVikram Hegde } 10413a634bfcSVikram Hegde 10423a634bfcSVikram Hegde /* 10433a634bfcSVikram Hegde * Read the "raw" DMAR ACPI table to get information 10443a634bfcSVikram Hegde * and convert into a form we can use. 10453a634bfcSVikram Hegde */ 10463a634bfcSVikram Hegde if (immu_dmar_parse() != DDI_SUCCESS) { 10473a634bfcSVikram Hegde blacklist_destroy(); 10483a634bfcSVikram Hegde immu_enable = B_FALSE; 10493a634bfcSVikram Hegde return; 10503a634bfcSVikram Hegde } 10513a634bfcSVikram Hegde 10523a634bfcSVikram Hegde /* 10533a634bfcSVikram Hegde * now that we have processed the ACPI table 10543a634bfcSVikram Hegde * check if we need to blacklist this system 10553a634bfcSVikram Hegde * based on ACPI info 10563a634bfcSVikram Hegde */ 10573a634bfcSVikram Hegde if (blacklisted_acpi() == B_TRUE) { 10583a634bfcSVikram Hegde immu_dmar_destroy(); 10593a634bfcSVikram Hegde blacklist_destroy(); 10603a634bfcSVikram Hegde immu_enable = B_FALSE; 10613a634bfcSVikram Hegde return; 10623a634bfcSVikram Hegde } 10633a634bfcSVikram Hegde 10643a634bfcSVikram Hegde blacklist_destroy(); 10653a634bfcSVikram Hegde 10663a634bfcSVikram Hegde /* 10673a634bfcSVikram Hegde * Check if system has HW quirks. 10683a634bfcSVikram Hegde */ 10693a634bfcSVikram Hegde pre_setup_quirks(); 10703a634bfcSVikram Hegde 10713a634bfcSVikram Hegde /* Now do the rest of the setup */ 10723a634bfcSVikram Hegde immu_subsystems_setup(); 10733a634bfcSVikram Hegde 10743a634bfcSVikram Hegde /* 10753a634bfcSVikram Hegde * Now that the IMMU is setup, create a phony 10763a634bfcSVikram Hegde * reg prop so that suspend/resume works 10773a634bfcSVikram Hegde */ 10783a634bfcSVikram Hegde if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg", 10793a634bfcSVikram Hegde (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) { 10803a634bfcSVikram Hegde ddi_err(DER_PANIC, NULL, "Failed to create reg prop for " 10813a634bfcSVikram Hegde "rootnex node"); 10823a634bfcSVikram Hegde /*NOTREACHED*/ 10833a634bfcSVikram Hegde } 10843a634bfcSVikram Hegde 10853a634bfcSVikram Hegde immu_setup = B_TRUE; 10863a634bfcSVikram Hegde } 10873a634bfcSVikram Hegde 10883a634bfcSVikram Hegde /* 10893a634bfcSVikram Hegde * immu_startup() 10903a634bfcSVikram Hegde * called directly by boot code to startup 10913a634bfcSVikram Hegde * all units of the IOMMU 10923a634bfcSVikram Hegde */ 10933a634bfcSVikram Hegde void 10943a634bfcSVikram Hegde immu_startup(void) 10953a634bfcSVikram Hegde { 10963a634bfcSVikram Hegde /* 10973a634bfcSVikram Hegde * If IOMMU is disabled, do nothing 10983a634bfcSVikram Hegde */ 10993a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 11003a634bfcSVikram Hegde return; 11013a634bfcSVikram Hegde } 11023a634bfcSVikram Hegde 11033a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 11043a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, " 11053a634bfcSVikram Hegde "skipping IOMU startup"); 11063a634bfcSVikram Hegde return; 11073a634bfcSVikram Hegde } 11083a634bfcSVikram Hegde 11093a634bfcSVikram Hegde pre_startup_quirks(); 11103a634bfcSVikram Hegde 11113a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, 11123a634bfcSVikram Hegde "?Starting Intel IOMMU (dmar) units...\n"); 11133a634bfcSVikram Hegde 11143a634bfcSVikram Hegde immu_subsystems_startup(); 11153a634bfcSVikram Hegde 11163a634bfcSVikram Hegde immu_running = B_TRUE; 11173a634bfcSVikram Hegde } 11183a634bfcSVikram Hegde 11193a634bfcSVikram Hegde /* 11203a634bfcSVikram Hegde * immu_map_sgl() 11213a634bfcSVikram Hegde * called from rootnex_coredma_bindhdl() when Intel 11223a634bfcSVikram Hegde * IOMMU is enabled to build DVMA cookies and map them. 11233a634bfcSVikram Hegde */ 11243a634bfcSVikram Hegde int 11253a634bfcSVikram Hegde immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq, 11263a634bfcSVikram Hegde int prealloc_count, dev_info_t *rdip) 11273a634bfcSVikram Hegde { 11283a634bfcSVikram Hegde if (immu_running == B_FALSE) { 11293a634bfcSVikram Hegde return (DDI_DMA_USE_PHYSICAL); 11303a634bfcSVikram Hegde } 11313a634bfcSVikram Hegde 11323a634bfcSVikram Hegde return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip, 11333a634bfcSVikram Hegde IMMU_FLAGS_DMAHDL)); 11343a634bfcSVikram Hegde } 11353a634bfcSVikram Hegde 11363a634bfcSVikram Hegde /* 11373a634bfcSVikram Hegde * immu_unmap_sgl() 11383a634bfcSVikram Hegde * called from rootnex_coredma_unbindhdl(), to unmap DVMA 11393a634bfcSVikram Hegde * cookies and free them 11403a634bfcSVikram Hegde */ 11413a634bfcSVikram Hegde int 11423a634bfcSVikram Hegde immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip) 11433a634bfcSVikram Hegde { 11443a634bfcSVikram Hegde if (immu_running == B_FALSE) { 11453a634bfcSVikram Hegde return (DDI_DMA_USE_PHYSICAL); 11463a634bfcSVikram Hegde } 11473a634bfcSVikram Hegde 11483a634bfcSVikram Hegde return (immu_dvma_unmap(hp, rdip)); 11493a634bfcSVikram Hegde } 11503a634bfcSVikram Hegde 11513a634bfcSVikram Hegde /* 11523a634bfcSVikram Hegde * Hook to notify IOMMU code of device tree changes 11533a634bfcSVikram Hegde */ 11543a634bfcSVikram Hegde void 11553a634bfcSVikram Hegde immu_device_tree_changed(void) 11563a634bfcSVikram Hegde { 11573a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 11583a634bfcSVikram Hegde return; 11593a634bfcSVikram Hegde } 11603a634bfcSVikram Hegde 11613a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU currently " 11623a634bfcSVikram Hegde "does not use device tree updates"); 11633a634bfcSVikram Hegde } 11643a634bfcSVikram Hegde 11653a634bfcSVikram Hegde /* 11663a634bfcSVikram Hegde * Hook to notify IOMMU code of memory changes 11673a634bfcSVikram Hegde */ 11683a634bfcSVikram Hegde void 11693a634bfcSVikram Hegde immu_physmem_update(uint64_t addr, uint64_t size) 11703a634bfcSVikram Hegde { 11713a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 11723a634bfcSVikram Hegde return; 11733a634bfcSVikram Hegde } 11743a634bfcSVikram Hegde immu_dvma_physmem_update(addr, size); 11753a634bfcSVikram Hegde } 11763a634bfcSVikram Hegde 11773a634bfcSVikram Hegde /* 11783a634bfcSVikram Hegde * immu_quiesce() 11793a634bfcSVikram Hegde * quiesce all units that are running 11803a634bfcSVikram Hegde */ 11813a634bfcSVikram Hegde int 11823a634bfcSVikram Hegde immu_quiesce(void) 11833a634bfcSVikram Hegde { 11843a634bfcSVikram Hegde immu_t *immu; 11853a634bfcSVikram Hegde int ret = DDI_SUCCESS; 11863a634bfcSVikram Hegde 11873a634bfcSVikram Hegde mutex_enter(&immu_lock); 11883a634bfcSVikram Hegde 11893a634bfcSVikram Hegde if (immu_running == B_FALSE) 11903a634bfcSVikram Hegde return (DDI_SUCCESS); 11913a634bfcSVikram Hegde 11923a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 11933a634bfcSVikram Hegde 11943a634bfcSVikram Hegde immu = list_head(&immu_list); 11953a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 11963a634bfcSVikram Hegde 11973a634bfcSVikram Hegde /* if immu is not running, we dont quiesce */ 11983a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) 11993a634bfcSVikram Hegde continue; 12003a634bfcSVikram Hegde 12013a634bfcSVikram Hegde /* flush caches */ 12023a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 12033a634bfcSVikram Hegde immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 12043a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 12053a634bfcSVikram Hegde immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 12063a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 12073a634bfcSVikram Hegde 12083a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 12093a634bfcSVikram Hegde 12103a634bfcSVikram Hegde /* 12113a634bfcSVikram Hegde * Set IOMMU unit's regs to do 12123a634bfcSVikram Hegde * the actual shutdown. 12133a634bfcSVikram Hegde */ 12143a634bfcSVikram Hegde immu_regs_shutdown(immu); 12153a634bfcSVikram Hegde immu_regs_suspend(immu); 12163a634bfcSVikram Hegde 12173a634bfcSVikram Hegde /* if immu is still running, we failed */ 12183a634bfcSVikram Hegde if (immu->immu_regs_running == B_TRUE) 12193a634bfcSVikram Hegde ret = DDI_FAILURE; 12203a634bfcSVikram Hegde else 12213a634bfcSVikram Hegde immu->immu_regs_quiesced = B_TRUE; 12223a634bfcSVikram Hegde 12233a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 12243a634bfcSVikram Hegde } 12253a634bfcSVikram Hegde mutex_exit(&immu_lock); 12263a634bfcSVikram Hegde 12273a634bfcSVikram Hegde if (ret == DDI_SUCCESS) { 12283a634bfcSVikram Hegde immu_running = B_FALSE; 12293a634bfcSVikram Hegde immu_quiesced = B_TRUE; 12303a634bfcSVikram Hegde } 12313a634bfcSVikram Hegde 12323a634bfcSVikram Hegde return (ret); 12333a634bfcSVikram Hegde } 12343a634bfcSVikram Hegde 12353a634bfcSVikram Hegde /* 12363a634bfcSVikram Hegde * immu_unquiesce() 12373a634bfcSVikram Hegde * unquiesce all units 12383a634bfcSVikram Hegde */ 12393a634bfcSVikram Hegde int 12403a634bfcSVikram Hegde immu_unquiesce(void) 12413a634bfcSVikram Hegde { 12423a634bfcSVikram Hegde immu_t *immu; 12433a634bfcSVikram Hegde int ret = DDI_SUCCESS; 12443a634bfcSVikram Hegde 12453a634bfcSVikram Hegde mutex_enter(&immu_lock); 12463a634bfcSVikram Hegde 12473a634bfcSVikram Hegde if (immu_quiesced == B_FALSE) 12483a634bfcSVikram Hegde return (DDI_SUCCESS); 12493a634bfcSVikram Hegde 12503a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 12513a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 12523a634bfcSVikram Hegde 12533a634bfcSVikram Hegde immu = list_head(&immu_list); 12543a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 12553a634bfcSVikram Hegde 12563a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 12573a634bfcSVikram Hegde 12583a634bfcSVikram Hegde /* if immu was not quiesced, i.e was not running before */ 1259e03dceedSVikram Hegde if (immu->immu_regs_quiesced == B_FALSE) { 1260e03dceedSVikram Hegde mutex_exit(&(immu->immu_lock)); 12613a634bfcSVikram Hegde continue; 1262e03dceedSVikram Hegde } 12633a634bfcSVikram Hegde 12643a634bfcSVikram Hegde if (immu_regs_resume(immu) != DDI_SUCCESS) { 12653a634bfcSVikram Hegde ret = DDI_FAILURE; 1266e03dceedSVikram Hegde mutex_exit(&(immu->immu_lock)); 12673a634bfcSVikram Hegde continue; 12683a634bfcSVikram Hegde } 12693a634bfcSVikram Hegde 12703a634bfcSVikram Hegde /* flush caches before unquiesce */ 12713a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 12723a634bfcSVikram Hegde immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 12733a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 12743a634bfcSVikram Hegde immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 12753a634bfcSVikram Hegde 12763a634bfcSVikram Hegde /* 12773a634bfcSVikram Hegde * Set IOMMU unit's regs to do 12783a634bfcSVikram Hegde * the actual startup. This will 12793a634bfcSVikram Hegde * set immu->immu_regs_running field 12803a634bfcSVikram Hegde * if the unit is successfully 12813a634bfcSVikram Hegde * started 12823a634bfcSVikram Hegde */ 12833a634bfcSVikram Hegde immu_regs_startup(immu); 12843a634bfcSVikram Hegde 12853a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) { 12863a634bfcSVikram Hegde ret = DDI_FAILURE; 12873a634bfcSVikram Hegde } else { 12883a634bfcSVikram Hegde immu_quiesced = B_TRUE; 12893a634bfcSVikram Hegde immu_running = B_TRUE; 12903a634bfcSVikram Hegde immu->immu_regs_quiesced = B_FALSE; 12913a634bfcSVikram Hegde } 12923a634bfcSVikram Hegde 12933a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 12943a634bfcSVikram Hegde } 12953a634bfcSVikram Hegde 12963a634bfcSVikram Hegde mutex_exit(&immu_lock); 12973a634bfcSVikram Hegde 12983a634bfcSVikram Hegde return (ret); 12993a634bfcSVikram Hegde } 13003a634bfcSVikram Hegde 13013a634bfcSVikram Hegde /* ############## END Intel IOMMU entry points ################## */ 1302