1*3a634bfcSVikram Hegde /* 2*3a634bfcSVikram Hegde * CDDL HEADER START 3*3a634bfcSVikram Hegde * 4*3a634bfcSVikram Hegde * The contents of this file are subject to the terms of the 5*3a634bfcSVikram Hegde * Common Development and Distribution License (the "License"). 6*3a634bfcSVikram Hegde * You may not use this file except in compliance with the License. 7*3a634bfcSVikram Hegde * 8*3a634bfcSVikram Hegde * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3a634bfcSVikram Hegde * or http://www.opensolaris.org/os/licensing. 10*3a634bfcSVikram Hegde * See the License for the specific language governing permissions 11*3a634bfcSVikram Hegde * and limitations under the License. 12*3a634bfcSVikram Hegde * 13*3a634bfcSVikram Hegde * When distributing Covered Code, include this CDDL HEADER in each 14*3a634bfcSVikram Hegde * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3a634bfcSVikram Hegde * If applicable, add the following below this CDDL HEADER, with the 16*3a634bfcSVikram Hegde * fields enclosed by brackets "[]" replaced with your own identifying 17*3a634bfcSVikram Hegde * information: Portions Copyright [yyyy] [name of copyright owner] 18*3a634bfcSVikram Hegde * 19*3a634bfcSVikram Hegde * CDDL HEADER END 20*3a634bfcSVikram Hegde */ 21*3a634bfcSVikram Hegde /* 22*3a634bfcSVikram Hegde * Portions Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23*3a634bfcSVikram Hegde * Use is subject to license terms. 24*3a634bfcSVikram Hegde */ 25*3a634bfcSVikram Hegde /* 26*3a634bfcSVikram Hegde * Copyright (c) 2009, Intel Corporation. 27*3a634bfcSVikram Hegde * All rights reserved. 28*3a634bfcSVikram Hegde */ 29*3a634bfcSVikram Hegde 30*3a634bfcSVikram Hegde /* 31*3a634bfcSVikram Hegde * Intel IOMMU implementation 32*3a634bfcSVikram Hegde * This file contains Intel IOMMU code exported 33*3a634bfcSVikram Hegde * to the rest of the system and code that deals 34*3a634bfcSVikram Hegde * with the Intel IOMMU as a whole. 35*3a634bfcSVikram Hegde */ 36*3a634bfcSVikram Hegde 37*3a634bfcSVikram Hegde #include <sys/conf.h> 38*3a634bfcSVikram Hegde #include <sys/modctl.h> 39*3a634bfcSVikram Hegde #include <sys/pci.h> 40*3a634bfcSVikram Hegde #include <sys/pci_impl.h> 41*3a634bfcSVikram Hegde #include <sys/sysmacros.h> 42*3a634bfcSVikram Hegde #include <sys/ddi.h> 43*3a634bfcSVikram Hegde #include <sys/ddidmareq.h> 44*3a634bfcSVikram Hegde #include <sys/ddi_impldefs.h> 45*3a634bfcSVikram Hegde #include <sys/ddifm.h> 46*3a634bfcSVikram Hegde #include <sys/sunndi.h> 47*3a634bfcSVikram Hegde #include <sys/debug.h> 48*3a634bfcSVikram Hegde #include <sys/fm/protocol.h> 49*3a634bfcSVikram Hegde #include <sys/note.h> 50*3a634bfcSVikram Hegde #include <sys/apic.h> 51*3a634bfcSVikram Hegde #include <vm/hat_i86.h> 52*3a634bfcSVikram Hegde #include <sys/smp_impldefs.h> 53*3a634bfcSVikram Hegde #include <sys/spl.h> 54*3a634bfcSVikram Hegde #include <sys/archsystm.h> 55*3a634bfcSVikram Hegde #include <sys/x86_archext.h> 56*3a634bfcSVikram Hegde #include <sys/rootnex.h> 57*3a634bfcSVikram Hegde #include <sys/avl.h> 58*3a634bfcSVikram Hegde #include <sys/bootconf.h> 59*3a634bfcSVikram Hegde #include <sys/bootinfo.h> 60*3a634bfcSVikram Hegde #include <sys/atomic.h> 61*3a634bfcSVikram Hegde #include <sys/immu.h> 62*3a634bfcSVikram Hegde 63*3a634bfcSVikram Hegde /* ########################### Globals and tunables ######################## */ 64*3a634bfcSVikram Hegde /* 65*3a634bfcSVikram Hegde * Global switches (boolean) that can be toggled either via boot options 66*3a634bfcSVikram Hegde * or via /etc/system or kmdb 67*3a634bfcSVikram Hegde */ 68*3a634bfcSVikram Hegde 69*3a634bfcSVikram Hegde /* Various features */ 70*3a634bfcSVikram Hegde boolean_t immu_enable = B_TRUE; 71*3a634bfcSVikram Hegde boolean_t immu_dvma_enable = B_TRUE; 72*3a634bfcSVikram Hegde 73*3a634bfcSVikram Hegde /* accessed in other files so not static */ 74*3a634bfcSVikram Hegde boolean_t immu_gfxdvma_enable = B_TRUE; 75*3a634bfcSVikram Hegde boolean_t immu_intrmap_enable = B_FALSE; 76*3a634bfcSVikram Hegde boolean_t immu_qinv_enable = B_FALSE; 77*3a634bfcSVikram Hegde 78*3a634bfcSVikram Hegde /* various quirks that need working around */ 79*3a634bfcSVikram Hegde 80*3a634bfcSVikram Hegde /* XXX We always map page 0 read/write for now */ 81*3a634bfcSVikram Hegde boolean_t immu_quirk_usbpage0 = B_TRUE; 82*3a634bfcSVikram Hegde boolean_t immu_quirk_usbrmrr = B_TRUE; 83*3a634bfcSVikram Hegde boolean_t immu_quirk_usbfullpa; 84*3a634bfcSVikram Hegde boolean_t immu_quirk_mobile4; 85*3a634bfcSVikram Hegde 86*3a634bfcSVikram Hegde boolean_t immu_mmio_safe = B_TRUE; 87*3a634bfcSVikram Hegde 88*3a634bfcSVikram Hegde /* debug messages */ 89*3a634bfcSVikram Hegde boolean_t immu_dmar_print; 90*3a634bfcSVikram Hegde 91*3a634bfcSVikram Hegde /* ############ END OPTIONS section ################ */ 92*3a634bfcSVikram Hegde 93*3a634bfcSVikram Hegde /* 94*3a634bfcSVikram Hegde * Global used internally by Intel IOMMU code 95*3a634bfcSVikram Hegde */ 96*3a634bfcSVikram Hegde dev_info_t *root_devinfo; 97*3a634bfcSVikram Hegde kmutex_t immu_lock; 98*3a634bfcSVikram Hegde list_t immu_list; 99*3a634bfcSVikram Hegde boolean_t immu_setup; 100*3a634bfcSVikram Hegde boolean_t immu_running; 101*3a634bfcSVikram Hegde boolean_t immu_quiesced; 102*3a634bfcSVikram Hegde 103*3a634bfcSVikram Hegde /* ######################## END Globals and tunables ###################### */ 104*3a634bfcSVikram Hegde /* Globals used only in this file */ 105*3a634bfcSVikram Hegde static char **black_array; 106*3a634bfcSVikram Hegde static uint_t nblacks; 107*3a634bfcSVikram Hegde /* ###################### Utility routines ############################# */ 108*3a634bfcSVikram Hegde 109*3a634bfcSVikram Hegde /* 110*3a634bfcSVikram Hegde * Check if the device has mobile 4 chipset 111*3a634bfcSVikram Hegde */ 112*3a634bfcSVikram Hegde static int 113*3a634bfcSVikram Hegde check_mobile4(dev_info_t *dip, void *arg) 114*3a634bfcSVikram Hegde { 115*3a634bfcSVikram Hegde _NOTE(ARGUNUSED(arg)); 116*3a634bfcSVikram Hegde int vendor, device; 117*3a634bfcSVikram Hegde int *ip = (int *)arg; 118*3a634bfcSVikram Hegde 119*3a634bfcSVikram Hegde ASSERT(arg); 120*3a634bfcSVikram Hegde 121*3a634bfcSVikram Hegde vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 122*3a634bfcSVikram Hegde "vendor-id", -1); 123*3a634bfcSVikram Hegde device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 124*3a634bfcSVikram Hegde "device-id", -1); 125*3a634bfcSVikram Hegde 126*3a634bfcSVikram Hegde if (vendor == 0x8086 && device == 0x2a40) { 127*3a634bfcSVikram Hegde *ip = B_TRUE; 128*3a634bfcSVikram Hegde ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. " 129*3a634bfcSVikram Hegde "Force setting IOMMU write buffer"); 130*3a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 131*3a634bfcSVikram Hegde } else { 132*3a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 133*3a634bfcSVikram Hegde } 134*3a634bfcSVikram Hegde } 135*3a634bfcSVikram Hegde 136*3a634bfcSVikram Hegde static void 137*3a634bfcSVikram Hegde map_bios_rsvd_mem(dev_info_t *dip) 138*3a634bfcSVikram Hegde { 139*3a634bfcSVikram Hegde struct memlist *mp; 140*3a634bfcSVikram Hegde int e; 141*3a634bfcSVikram Hegde 142*3a634bfcSVikram Hegde memlist_read_lock(); 143*3a634bfcSVikram Hegde 144*3a634bfcSVikram Hegde mp = bios_rsvd; 145*3a634bfcSVikram Hegde while (mp != NULL) { 146*3a634bfcSVikram Hegde memrng_t *mrng = {0}; 147*3a634bfcSVikram Hegde 148*3a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range " 149*3a634bfcSVikram Hegde "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address, 150*3a634bfcSVikram Hegde mp->ml_address + mp->ml_size); 151*3a634bfcSVikram Hegde 152*3a634bfcSVikram Hegde mrng->mrng_start = IMMU_ROUNDOWN(mp->ml_address); 153*3a634bfcSVikram Hegde mrng->mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE; 154*3a634bfcSVikram Hegde 155*3a634bfcSVikram Hegde e = immu_dvma_map(NULL, NULL, mrng, 0, dip, IMMU_FLAGS_MEMRNG); 156*3a634bfcSVikram Hegde ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL); 157*3a634bfcSVikram Hegde 158*3a634bfcSVikram Hegde mp = mp->ml_next; 159*3a634bfcSVikram Hegde } 160*3a634bfcSVikram Hegde 161*3a634bfcSVikram Hegde memlist_read_unlock(); 162*3a634bfcSVikram Hegde } 163*3a634bfcSVikram Hegde 164*3a634bfcSVikram Hegde /* 165*3a634bfcSVikram Hegde * Check if the device is USB controller 166*3a634bfcSVikram Hegde */ 167*3a634bfcSVikram Hegde /*ARGSUSED*/ 168*3a634bfcSVikram Hegde static void 169*3a634bfcSVikram Hegde check_usb(dev_info_t *dip, void *arg) 170*3a634bfcSVikram Hegde { 171*3a634bfcSVikram Hegde const char *drv = ddi_driver_name(dip); 172*3a634bfcSVikram Hegde 173*3a634bfcSVikram Hegde if (drv == NULL || 174*3a634bfcSVikram Hegde (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 && 175*3a634bfcSVikram Hegde strcmp(drv, "ehci") != 0)) { 176*3a634bfcSVikram Hegde return; 177*3a634bfcSVikram Hegde } 178*3a634bfcSVikram Hegde 179*3a634bfcSVikram Hegde /* This must come first since it does unity mapping */ 180*3a634bfcSVikram Hegde if (immu_quirk_usbfullpa == B_TRUE) { 181*3a634bfcSVikram Hegde int e; 182*3a634bfcSVikram Hegde ddi_err(DER_NOTE, dip, "Applying USB FULL PA quirk"); 183*3a634bfcSVikram Hegde e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY); 184*3a634bfcSVikram Hegde /* for unity mode, map will return USE_PHYSICAL */ 185*3a634bfcSVikram Hegde ASSERT(e == DDI_DMA_USE_PHYSICAL); 186*3a634bfcSVikram Hegde } 187*3a634bfcSVikram Hegde 188*3a634bfcSVikram Hegde if (immu_quirk_usbrmrr == B_TRUE) { 189*3a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "Applying USB RMRR quirk"); 190*3a634bfcSVikram Hegde map_bios_rsvd_mem(dip); 191*3a634bfcSVikram Hegde } 192*3a634bfcSVikram Hegde } 193*3a634bfcSVikram Hegde 194*3a634bfcSVikram Hegde /* 195*3a634bfcSVikram Hegde * Check if the device is a LPC device 196*3a634bfcSVikram Hegde */ 197*3a634bfcSVikram Hegde /*ARGSUSED*/ 198*3a634bfcSVikram Hegde static void 199*3a634bfcSVikram Hegde check_lpc(dev_info_t *dip, void *arg) 200*3a634bfcSVikram Hegde { 201*3a634bfcSVikram Hegde immu_devi_t *immu_devi; 202*3a634bfcSVikram Hegde 203*3a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 204*3a634bfcSVikram Hegde ASSERT(immu_devi); 205*3a634bfcSVikram Hegde if (immu_devi->imd_lpc == B_TRUE) { 206*3a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Found LPC device"); 207*3a634bfcSVikram Hegde /* This will put the immu_devi on the LPC "specials" list */ 208*3a634bfcSVikram Hegde (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 209*3a634bfcSVikram Hegde } 210*3a634bfcSVikram Hegde } 211*3a634bfcSVikram Hegde 212*3a634bfcSVikram Hegde /* 213*3a634bfcSVikram Hegde * Check if the device is a GFX device 214*3a634bfcSVikram Hegde */ 215*3a634bfcSVikram Hegde /*ARGSUSED*/ 216*3a634bfcSVikram Hegde static void 217*3a634bfcSVikram Hegde check_gfx(dev_info_t *dip, void *arg) 218*3a634bfcSVikram Hegde { 219*3a634bfcSVikram Hegde immu_devi_t *immu_devi; 220*3a634bfcSVikram Hegde int e; 221*3a634bfcSVikram Hegde 222*3a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 223*3a634bfcSVikram Hegde ASSERT(immu_devi); 224*3a634bfcSVikram Hegde if (immu_devi->imd_display == B_TRUE) { 225*3a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "IMMU: Found GFX device"); 226*3a634bfcSVikram Hegde /* This will put the immu_devi on the GFX "specials" list */ 227*3a634bfcSVikram Hegde (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 228*3a634bfcSVikram Hegde e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY); 229*3a634bfcSVikram Hegde /* for unity mode, map will return USE_PHYSICAL */ 230*3a634bfcSVikram Hegde ASSERT(e == DDI_DMA_USE_PHYSICAL); 231*3a634bfcSVikram Hegde } 232*3a634bfcSVikram Hegde } 233*3a634bfcSVikram Hegde 234*3a634bfcSVikram Hegde static void 235*3a634bfcSVikram Hegde walk_tree(int (*f)(dev_info_t *, void *), void *arg) 236*3a634bfcSVikram Hegde { 237*3a634bfcSVikram Hegde int count; 238*3a634bfcSVikram Hegde 239*3a634bfcSVikram Hegde ndi_devi_enter(root_devinfo, &count); 240*3a634bfcSVikram Hegde ddi_walk_devs(ddi_get_child(root_devinfo), f, arg); 241*3a634bfcSVikram Hegde ndi_devi_exit(root_devinfo, count); 242*3a634bfcSVikram Hegde } 243*3a634bfcSVikram Hegde 244*3a634bfcSVikram Hegde static int 245*3a634bfcSVikram Hegde check_pre_setup_quirks(dev_info_t *dip, void *arg) 246*3a634bfcSVikram Hegde { 247*3a634bfcSVikram Hegde /* just 1 check right now */ 248*3a634bfcSVikram Hegde return (check_mobile4(dip, arg)); 249*3a634bfcSVikram Hegde } 250*3a634bfcSVikram Hegde 251*3a634bfcSVikram Hegde static int 252*3a634bfcSVikram Hegde check_pre_startup_quirks(dev_info_t *dip, void *arg) 253*3a634bfcSVikram Hegde { 254*3a634bfcSVikram Hegde if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) { 255*3a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "Failed to get immu_devi"); 256*3a634bfcSVikram Hegde } 257*3a634bfcSVikram Hegde 258*3a634bfcSVikram Hegde check_gfx(dip, arg); 259*3a634bfcSVikram Hegde 260*3a634bfcSVikram Hegde check_lpc(dip, arg); 261*3a634bfcSVikram Hegde 262*3a634bfcSVikram Hegde check_usb(dip, arg); 263*3a634bfcSVikram Hegde 264*3a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 265*3a634bfcSVikram Hegde } 266*3a634bfcSVikram Hegde 267*3a634bfcSVikram Hegde static void 268*3a634bfcSVikram Hegde pre_setup_quirks(void) 269*3a634bfcSVikram Hegde { 270*3a634bfcSVikram Hegde walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4); 271*3a634bfcSVikram Hegde } 272*3a634bfcSVikram Hegde 273*3a634bfcSVikram Hegde static void 274*3a634bfcSVikram Hegde pre_startup_quirks(void) 275*3a634bfcSVikram Hegde { 276*3a634bfcSVikram Hegde walk_tree(check_pre_startup_quirks, NULL); 277*3a634bfcSVikram Hegde 278*3a634bfcSVikram Hegde immu_dmar_rmrr_map(); 279*3a634bfcSVikram Hegde } 280*3a634bfcSVikram Hegde 281*3a634bfcSVikram Hegde /* 282*3a634bfcSVikram Hegde * get_bootopt() 283*3a634bfcSVikram Hegde * check a boot option (always a boolean) 284*3a634bfcSVikram Hegde */ 285*3a634bfcSVikram Hegde static void 286*3a634bfcSVikram Hegde get_bootopt(char *bopt, boolean_t *kvar) 287*3a634bfcSVikram Hegde { 288*3a634bfcSVikram Hegde char *val = NULL; 289*3a634bfcSVikram Hegde 290*3a634bfcSVikram Hegde ASSERT(bopt); 291*3a634bfcSVikram Hegde ASSERT(kvar); 292*3a634bfcSVikram Hegde 293*3a634bfcSVikram Hegde /* 294*3a634bfcSVikram Hegde * All boot options set at the GRUB menu become 295*3a634bfcSVikram Hegde * properties on the rootnex. 296*3a634bfcSVikram Hegde */ 297*3a634bfcSVikram Hegde if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo, 298*3a634bfcSVikram Hegde DDI_PROP_DONTPASS, bopt, &val) == DDI_SUCCESS) { 299*3a634bfcSVikram Hegde ASSERT(val); 300*3a634bfcSVikram Hegde if (strcmp(val, "true") == 0) { 301*3a634bfcSVikram Hegde *kvar = B_TRUE; 302*3a634bfcSVikram Hegde } else if (strcmp(val, "false") == 0) { 303*3a634bfcSVikram Hegde *kvar = B_FALSE; 304*3a634bfcSVikram Hegde } else { 305*3a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ", 306*3a634bfcSVikram Hegde "is not set to true or false. Ignoring option.", 307*3a634bfcSVikram Hegde bopt, val); 308*3a634bfcSVikram Hegde } 309*3a634bfcSVikram Hegde ddi_prop_free(val); 310*3a634bfcSVikram Hegde } 311*3a634bfcSVikram Hegde } 312*3a634bfcSVikram Hegde 313*3a634bfcSVikram Hegde static void 314*3a634bfcSVikram Hegde read_boot_options(void) 315*3a634bfcSVikram Hegde { 316*3a634bfcSVikram Hegde /* enable/disable options */ 317*3a634bfcSVikram Hegde get_bootopt("immu-enable", &immu_enable); 318*3a634bfcSVikram Hegde get_bootopt("immu-dvma-enable", &immu_dvma_enable); 319*3a634bfcSVikram Hegde get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 320*3a634bfcSVikram Hegde get_bootopt("immu-intrmap-enable", &immu_intrmap_enable); 321*3a634bfcSVikram Hegde get_bootopt("immu-qinv-enable", &immu_qinv_enable); 322*3a634bfcSVikram Hegde get_bootopt("immu-mmio-safe", &immu_mmio_safe); 323*3a634bfcSVikram Hegde 324*3a634bfcSVikram Hegde /* workaround switches */ 325*3a634bfcSVikram Hegde get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 326*3a634bfcSVikram Hegde get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 327*3a634bfcSVikram Hegde get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 328*3a634bfcSVikram Hegde 329*3a634bfcSVikram Hegde /* debug printing */ 330*3a634bfcSVikram Hegde get_bootopt("immu-dmar-print", &immu_dmar_print); 331*3a634bfcSVikram Hegde } 332*3a634bfcSVikram Hegde 333*3a634bfcSVikram Hegde /* 334*3a634bfcSVikram Hegde * Note, this will not catch hardware not enumerated 335*3a634bfcSVikram Hegde * in early boot 336*3a634bfcSVikram Hegde */ 337*3a634bfcSVikram Hegde static boolean_t 338*3a634bfcSVikram Hegde blacklisted_driver(void) 339*3a634bfcSVikram Hegde { 340*3a634bfcSVikram Hegde char **strptr; 341*3a634bfcSVikram Hegde int i; 342*3a634bfcSVikram Hegde major_t maj; 343*3a634bfcSVikram Hegde 344*3a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 345*3a634bfcSVikram Hegde 346*3a634bfcSVikram Hegde /* need at least 2 strings */ 347*3a634bfcSVikram Hegde if (nblacks < 2) { 348*3a634bfcSVikram Hegde return (B_FALSE); 349*3a634bfcSVikram Hegde } 350*3a634bfcSVikram Hegde 351*3a634bfcSVikram Hegde strptr = black_array; 352*3a634bfcSVikram Hegde for (i = 0; nblacks - i > 1; i++) { 353*3a634bfcSVikram Hegde if (strcmp(*strptr++, "DRIVER") == 0) { 354*3a634bfcSVikram Hegde if ((maj = ddi_name_to_major(*strptr++)) 355*3a634bfcSVikram Hegde != DDI_MAJOR_T_NONE) { 356*3a634bfcSVikram Hegde /* is there hardware bound to this drvr */ 357*3a634bfcSVikram Hegde if (devnamesp[maj].dn_head != NULL) { 358*3a634bfcSVikram Hegde return (B_TRUE); 359*3a634bfcSVikram Hegde } 360*3a634bfcSVikram Hegde } 361*3a634bfcSVikram Hegde i += 1; /* for loop adds 1, so add only 1 here */ 362*3a634bfcSVikram Hegde } 363*3a634bfcSVikram Hegde } 364*3a634bfcSVikram Hegde 365*3a634bfcSVikram Hegde return (B_FALSE); 366*3a634bfcSVikram Hegde } 367*3a634bfcSVikram Hegde 368*3a634bfcSVikram Hegde static boolean_t 369*3a634bfcSVikram Hegde blacklisted_smbios(void) 370*3a634bfcSVikram Hegde { 371*3a634bfcSVikram Hegde id_t smid; 372*3a634bfcSVikram Hegde smbios_hdl_t *smhdl; 373*3a634bfcSVikram Hegde smbios_info_t sminf; 374*3a634bfcSVikram Hegde smbios_system_t smsys; 375*3a634bfcSVikram Hegde char *mfg, *product, *version; 376*3a634bfcSVikram Hegde char **strptr; 377*3a634bfcSVikram Hegde int i; 378*3a634bfcSVikram Hegde 379*3a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 380*3a634bfcSVikram Hegde 381*3a634bfcSVikram Hegde /* need at least 4 strings for this setting */ 382*3a634bfcSVikram Hegde if (nblacks < 4) { 383*3a634bfcSVikram Hegde return (B_FALSE); 384*3a634bfcSVikram Hegde } 385*3a634bfcSVikram Hegde 386*3a634bfcSVikram Hegde smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL); 387*3a634bfcSVikram Hegde if (smhdl == NULL || 388*3a634bfcSVikram Hegde (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR || 389*3a634bfcSVikram Hegde smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) { 390*3a634bfcSVikram Hegde return (B_FALSE); 391*3a634bfcSVikram Hegde } 392*3a634bfcSVikram Hegde 393*3a634bfcSVikram Hegde mfg = (char *)sminf.smbi_manufacturer; 394*3a634bfcSVikram Hegde product = (char *)sminf.smbi_product; 395*3a634bfcSVikram Hegde version = (char *)sminf.smbi_version; 396*3a634bfcSVikram Hegde 397*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n"); 398*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg); 399*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product); 400*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version); 401*3a634bfcSVikram Hegde 402*3a634bfcSVikram Hegde strptr = black_array; 403*3a634bfcSVikram Hegde for (i = 0; nblacks - i > 3; i++) { 404*3a634bfcSVikram Hegde if (strcmp(*strptr++, "SMBIOS") == 0) { 405*3a634bfcSVikram Hegde if (strcmp(*strptr++, mfg) == 0 && 406*3a634bfcSVikram Hegde ((char *)strptr == '\0' || 407*3a634bfcSVikram Hegde strcmp(*strptr++, product) == 0) && 408*3a634bfcSVikram Hegde ((char *)strptr == '\0' || 409*3a634bfcSVikram Hegde strcmp(*strptr++, version) == 0)) { 410*3a634bfcSVikram Hegde return (B_TRUE); 411*3a634bfcSVikram Hegde } 412*3a634bfcSVikram Hegde i += 3; 413*3a634bfcSVikram Hegde } 414*3a634bfcSVikram Hegde } 415*3a634bfcSVikram Hegde 416*3a634bfcSVikram Hegde return (B_FALSE); 417*3a634bfcSVikram Hegde } 418*3a634bfcSVikram Hegde 419*3a634bfcSVikram Hegde static boolean_t 420*3a634bfcSVikram Hegde blacklisted_acpi(void) 421*3a634bfcSVikram Hegde { 422*3a634bfcSVikram Hegde ASSERT((black_array == NULL) ^ (nblacks != 0)); 423*3a634bfcSVikram Hegde if (nblacks == 0) { 424*3a634bfcSVikram Hegde return (B_FALSE); 425*3a634bfcSVikram Hegde } 426*3a634bfcSVikram Hegde 427*3a634bfcSVikram Hegde return (immu_dmar_blacklisted(black_array, nblacks)); 428*3a634bfcSVikram Hegde } 429*3a634bfcSVikram Hegde 430*3a634bfcSVikram Hegde /* 431*3a634bfcSVikram Hegde * Check if system is blacklisted by Intel IOMMU driver 432*3a634bfcSVikram Hegde * i.e. should Intel IOMMU be disabled on this system 433*3a634bfcSVikram Hegde * Currently a system can be blacklistd based on the 434*3a634bfcSVikram Hegde * following bases: 435*3a634bfcSVikram Hegde * 436*3a634bfcSVikram Hegde * 1. DMAR ACPI table information. 437*3a634bfcSVikram Hegde * This information includes things like 438*3a634bfcSVikram Hegde * manufacturer and revision number. If rootnex.conf 439*3a634bfcSVikram Hegde * has matching info set in its blacklist property 440*3a634bfcSVikram Hegde * then Intel IOMMu will be disabled 441*3a634bfcSVikram Hegde * 442*3a634bfcSVikram Hegde * 2. SMBIOS information 443*3a634bfcSVikram Hegde * 444*3a634bfcSVikram Hegde * 3. Driver installed - useful if a particular 445*3a634bfcSVikram Hegde * driver or hardware is toxic if Intel IOMMU 446*3a634bfcSVikram Hegde * is turned on. 447*3a634bfcSVikram Hegde */ 448*3a634bfcSVikram Hegde 449*3a634bfcSVikram Hegde static void 450*3a634bfcSVikram Hegde blacklist_setup(void) 451*3a634bfcSVikram Hegde { 452*3a634bfcSVikram Hegde char **string_array; 453*3a634bfcSVikram Hegde uint_t nstrings; 454*3a634bfcSVikram Hegde 455*3a634bfcSVikram Hegde /* 456*3a634bfcSVikram Hegde * Check the rootnex.conf blacklist property. 457*3a634bfcSVikram Hegde * Fake up a dev_t since searching the global 458*3a634bfcSVikram Hegde * property list needs it 459*3a634bfcSVikram Hegde */ 460*3a634bfcSVikram Hegde if (ddi_prop_lookup_string_array( 461*3a634bfcSVikram Hegde makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 462*3a634bfcSVikram Hegde DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist", 463*3a634bfcSVikram Hegde &string_array, &nstrings) != DDI_PROP_SUCCESS) { 464*3a634bfcSVikram Hegde return; 465*3a634bfcSVikram Hegde } 466*3a634bfcSVikram Hegde 467*3a634bfcSVikram Hegde /* smallest blacklist criteria works with multiples of 2 */ 468*3a634bfcSVikram Hegde if (nstrings % 2 != 0) { 469*3a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist " 470*3a634bfcSVikram Hegde "rootnex.conf: number of strings must be a " 471*3a634bfcSVikram Hegde "multiple of 2"); 472*3a634bfcSVikram Hegde ddi_prop_free(string_array); 473*3a634bfcSVikram Hegde return; 474*3a634bfcSVikram Hegde } 475*3a634bfcSVikram Hegde 476*3a634bfcSVikram Hegde black_array = string_array; 477*3a634bfcSVikram Hegde nblacks = nstrings; 478*3a634bfcSVikram Hegde } 479*3a634bfcSVikram Hegde 480*3a634bfcSVikram Hegde static void 481*3a634bfcSVikram Hegde blacklist_destroy(void) 482*3a634bfcSVikram Hegde { 483*3a634bfcSVikram Hegde if (black_array) { 484*3a634bfcSVikram Hegde ddi_prop_free(black_array); 485*3a634bfcSVikram Hegde black_array = NULL; 486*3a634bfcSVikram Hegde nblacks = 0; 487*3a634bfcSVikram Hegde } 488*3a634bfcSVikram Hegde 489*3a634bfcSVikram Hegde ASSERT(black_array == NULL); 490*3a634bfcSVikram Hegde ASSERT(nblacks == 0); 491*3a634bfcSVikram Hegde } 492*3a634bfcSVikram Hegde 493*3a634bfcSVikram Hegde 494*3a634bfcSVikram Hegde /* 495*3a634bfcSVikram Hegde * Now set all the fields in the order they are defined 496*3a634bfcSVikram Hegde * We do this only as a defensive-coding practice, it is 497*3a634bfcSVikram Hegde * not a correctness issue. 498*3a634bfcSVikram Hegde */ 499*3a634bfcSVikram Hegde static void * 500*3a634bfcSVikram Hegde immu_state_alloc(int seg, void *dmar_unit) 501*3a634bfcSVikram Hegde { 502*3a634bfcSVikram Hegde immu_t *immu; 503*3a634bfcSVikram Hegde 504*3a634bfcSVikram Hegde dmar_unit = immu_dmar_walk_units(seg, dmar_unit); 505*3a634bfcSVikram Hegde if (dmar_unit == NULL) { 506*3a634bfcSVikram Hegde /* No more IOMMUs in this segment */ 507*3a634bfcSVikram Hegde return (NULL); 508*3a634bfcSVikram Hegde } 509*3a634bfcSVikram Hegde 510*3a634bfcSVikram Hegde immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP); 511*3a634bfcSVikram Hegde 512*3a634bfcSVikram Hegde mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL); 513*3a634bfcSVikram Hegde 514*3a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 515*3a634bfcSVikram Hegde 516*3a634bfcSVikram Hegde immu->immu_dmar_unit = dmar_unit; 517*3a634bfcSVikram Hegde immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit), 518*3a634bfcSVikram Hegde KM_SLEEP); 519*3a634bfcSVikram Hegde immu->immu_dip = immu_dmar_unit_dip(dmar_unit); 520*3a634bfcSVikram Hegde 521*3a634bfcSVikram Hegde /* 522*3a634bfcSVikram Hegde * the immu_intr_lock mutex is grabbed by the IOMMU 523*3a634bfcSVikram Hegde * unit's interrupt handler so we need to use an 524*3a634bfcSVikram Hegde * interrupt cookie for the mutex 525*3a634bfcSVikram Hegde */ 526*3a634bfcSVikram Hegde mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER, 527*3a634bfcSVikram Hegde (void *)ipltospl(IMMU_INTR_IPL)); 528*3a634bfcSVikram Hegde 529*3a634bfcSVikram Hegde /* IOMMU regs related */ 530*3a634bfcSVikram Hegde mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL); 531*3a634bfcSVikram Hegde 532*3a634bfcSVikram Hegde /* DVMA related */ 533*3a634bfcSVikram Hegde immu->immu_dvma_coherent = B_FALSE; 534*3a634bfcSVikram Hegde 535*3a634bfcSVikram Hegde /* DVMA context related */ 536*3a634bfcSVikram Hegde rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL); 537*3a634bfcSVikram Hegde 538*3a634bfcSVikram Hegde /* DVMA domain related */ 539*3a634bfcSVikram Hegde list_create(&(immu->immu_domain_list), sizeof (domain_t), 540*3a634bfcSVikram Hegde offsetof(domain_t, dom_immu_node)); 541*3a634bfcSVikram Hegde 542*3a634bfcSVikram Hegde /* DVMA special device lists */ 543*3a634bfcSVikram Hegde immu->immu_dvma_gfx_only = B_FALSE; 544*3a634bfcSVikram Hegde list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t), 545*3a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 546*3a634bfcSVikram Hegde list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t), 547*3a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 548*3a634bfcSVikram Hegde 549*3a634bfcSVikram Hegde /* interrupt remapping related */ 550*3a634bfcSVikram Hegde mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL); 551*3a634bfcSVikram Hegde 552*3a634bfcSVikram Hegde /* qinv related */ 553*3a634bfcSVikram Hegde mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL); 554*3a634bfcSVikram Hegde 555*3a634bfcSVikram Hegde /* 556*3a634bfcSVikram Hegde * insert this immu unit into the system-wide list 557*3a634bfcSVikram Hegde */ 558*3a634bfcSVikram Hegde list_insert_tail(&immu_list, immu); 559*3a634bfcSVikram Hegde 560*3a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 561*3a634bfcSVikram Hegde 562*3a634bfcSVikram Hegde ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup"); 563*3a634bfcSVikram Hegde 564*3a634bfcSVikram Hegde immu_dmar_set_immu(dmar_unit, immu); 565*3a634bfcSVikram Hegde 566*3a634bfcSVikram Hegde return (dmar_unit); 567*3a634bfcSVikram Hegde } 568*3a634bfcSVikram Hegde 569*3a634bfcSVikram Hegde static void 570*3a634bfcSVikram Hegde immu_subsystems_setup(void) 571*3a634bfcSVikram Hegde { 572*3a634bfcSVikram Hegde int seg; 573*3a634bfcSVikram Hegde void *unit_hdl; 574*3a634bfcSVikram Hegde 575*3a634bfcSVikram Hegde ddi_err(DER_VERB, NULL, 576*3a634bfcSVikram Hegde "Creating state structures for Intel IOMMU units\n"); 577*3a634bfcSVikram Hegde 578*3a634bfcSVikram Hegde ASSERT(immu_setup == B_FALSE); 579*3a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 580*3a634bfcSVikram Hegde 581*3a634bfcSVikram Hegde mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL); 582*3a634bfcSVikram Hegde list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node)); 583*3a634bfcSVikram Hegde 584*3a634bfcSVikram Hegde mutex_enter(&immu_lock); 585*3a634bfcSVikram Hegde 586*3a634bfcSVikram Hegde unit_hdl = NULL; 587*3a634bfcSVikram Hegde for (seg = 0; seg < IMMU_MAXSEG; seg++) { 588*3a634bfcSVikram Hegde while (unit_hdl = immu_state_alloc(seg, unit_hdl)) { 589*3a634bfcSVikram Hegde ; 590*3a634bfcSVikram Hegde } 591*3a634bfcSVikram Hegde } 592*3a634bfcSVikram Hegde 593*3a634bfcSVikram Hegde immu_regs_setup(&immu_list); /* subsequent code needs this first */ 594*3a634bfcSVikram Hegde immu_dvma_setup(&immu_list); 595*3a634bfcSVikram Hegde immu_intrmap_setup(&immu_list); 596*3a634bfcSVikram Hegde immu_qinv_setup(&immu_list); 597*3a634bfcSVikram Hegde 598*3a634bfcSVikram Hegde mutex_exit(&immu_lock); 599*3a634bfcSVikram Hegde } 600*3a634bfcSVikram Hegde 601*3a634bfcSVikram Hegde /* 602*3a634bfcSVikram Hegde * immu_subsystems_startup() 603*3a634bfcSVikram Hegde * startup all units that were setup 604*3a634bfcSVikram Hegde */ 605*3a634bfcSVikram Hegde static void 606*3a634bfcSVikram Hegde immu_subsystems_startup(void) 607*3a634bfcSVikram Hegde { 608*3a634bfcSVikram Hegde immu_t *immu; 609*3a634bfcSVikram Hegde 610*3a634bfcSVikram Hegde mutex_enter(&immu_lock); 611*3a634bfcSVikram Hegde 612*3a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 613*3a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 614*3a634bfcSVikram Hegde 615*3a634bfcSVikram Hegde immu_dmar_startup(); 616*3a634bfcSVikram Hegde 617*3a634bfcSVikram Hegde immu = list_head(&immu_list); 618*3a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 619*3a634bfcSVikram Hegde 620*3a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 621*3a634bfcSVikram Hegde 622*3a634bfcSVikram Hegde immu_intr_register(immu); 623*3a634bfcSVikram Hegde immu_dvma_startup(immu); 624*3a634bfcSVikram Hegde immu_intrmap_startup(immu); 625*3a634bfcSVikram Hegde immu_qinv_startup(immu); 626*3a634bfcSVikram Hegde 627*3a634bfcSVikram Hegde /* 628*3a634bfcSVikram Hegde * Set IOMMU unit's regs to do 629*3a634bfcSVikram Hegde * the actual startup. This will 630*3a634bfcSVikram Hegde * set immu->immu_running field 631*3a634bfcSVikram Hegde * if the unit is successfully 632*3a634bfcSVikram Hegde * started 633*3a634bfcSVikram Hegde */ 634*3a634bfcSVikram Hegde immu_regs_startup(immu); 635*3a634bfcSVikram Hegde 636*3a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 637*3a634bfcSVikram Hegde } 638*3a634bfcSVikram Hegde 639*3a634bfcSVikram Hegde mutex_exit(&immu_lock); 640*3a634bfcSVikram Hegde } 641*3a634bfcSVikram Hegde 642*3a634bfcSVikram Hegde /* ################## Intel IOMMU internal interfaces ###################### */ 643*3a634bfcSVikram Hegde 644*3a634bfcSVikram Hegde /* 645*3a634bfcSVikram Hegde * Internal interfaces for IOMMU code (i.e. not exported to rootnex 646*3a634bfcSVikram Hegde * or rest of system) 647*3a634bfcSVikram Hegde */ 648*3a634bfcSVikram Hegde 649*3a634bfcSVikram Hegde /* 650*3a634bfcSVikram Hegde * ddip can be NULL, in which case we walk up until we find the root dip 651*3a634bfcSVikram Hegde * NOTE: We never visit the root dip since its not a hardware node 652*3a634bfcSVikram Hegde */ 653*3a634bfcSVikram Hegde int 654*3a634bfcSVikram Hegde immu_walk_ancestor( 655*3a634bfcSVikram Hegde dev_info_t *rdip, 656*3a634bfcSVikram Hegde dev_info_t *ddip, 657*3a634bfcSVikram Hegde int (*func)(dev_info_t *, void *arg), 658*3a634bfcSVikram Hegde void *arg, 659*3a634bfcSVikram Hegde int *lvlp, 660*3a634bfcSVikram Hegde immu_flags_t immu_flags) 661*3a634bfcSVikram Hegde { 662*3a634bfcSVikram Hegde dev_info_t *pdip; 663*3a634bfcSVikram Hegde int level; 664*3a634bfcSVikram Hegde int error = DDI_SUCCESS; 665*3a634bfcSVikram Hegde 666*3a634bfcSVikram Hegde ASSERT(root_devinfo); 667*3a634bfcSVikram Hegde ASSERT(rdip); 668*3a634bfcSVikram Hegde ASSERT(rdip != root_devinfo); 669*3a634bfcSVikram Hegde ASSERT(func); 670*3a634bfcSVikram Hegde 671*3a634bfcSVikram Hegde /* ddip and immu can be NULL */ 672*3a634bfcSVikram Hegde 673*3a634bfcSVikram Hegde /* Hold rdip so that branch is not detached */ 674*3a634bfcSVikram Hegde ndi_hold_devi(rdip); 675*3a634bfcSVikram Hegde for (pdip = rdip, level = 1; pdip && pdip != root_devinfo; 676*3a634bfcSVikram Hegde pdip = ddi_get_parent(pdip), level++) { 677*3a634bfcSVikram Hegde 678*3a634bfcSVikram Hegde if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) { 679*3a634bfcSVikram Hegde error = DDI_FAILURE; 680*3a634bfcSVikram Hegde break; 681*3a634bfcSVikram Hegde } 682*3a634bfcSVikram Hegde if (func(pdip, arg) == DDI_WALK_TERMINATE) { 683*3a634bfcSVikram Hegde break; 684*3a634bfcSVikram Hegde } 685*3a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_DONTPASS) { 686*3a634bfcSVikram Hegde break; 687*3a634bfcSVikram Hegde } 688*3a634bfcSVikram Hegde if (pdip == ddip) { 689*3a634bfcSVikram Hegde break; 690*3a634bfcSVikram Hegde } 691*3a634bfcSVikram Hegde } 692*3a634bfcSVikram Hegde 693*3a634bfcSVikram Hegde ndi_rele_devi(rdip); 694*3a634bfcSVikram Hegde 695*3a634bfcSVikram Hegde if (lvlp) 696*3a634bfcSVikram Hegde *lvlp = level; 697*3a634bfcSVikram Hegde 698*3a634bfcSVikram Hegde return (error); 699*3a634bfcSVikram Hegde } 700*3a634bfcSVikram Hegde 701*3a634bfcSVikram Hegde /* ######################## Intel IOMMU entry points ####################### */ 702*3a634bfcSVikram Hegde /* 703*3a634bfcSVikram Hegde * immu_init() 704*3a634bfcSVikram Hegde * called from rootnex_attach(). setup but don't startup the Intel IOMMU 705*3a634bfcSVikram Hegde * This is the first function called in Intel IOMMU code 706*3a634bfcSVikram Hegde */ 707*3a634bfcSVikram Hegde void 708*3a634bfcSVikram Hegde immu_init(void) 709*3a634bfcSVikram Hegde { 710*3a634bfcSVikram Hegde char *phony_reg = "A thing of beauty is a joy forever"; 711*3a634bfcSVikram Hegde 712*3a634bfcSVikram Hegde /* Set some global shorthands that are needed by all of IOMMU code */ 713*3a634bfcSVikram Hegde ASSERT(root_devinfo == NULL); 714*3a634bfcSVikram Hegde root_devinfo = ddi_root_node(); 715*3a634bfcSVikram Hegde 716*3a634bfcSVikram Hegde /* 717*3a634bfcSVikram Hegde * Intel IOMMU only supported only if MMU(CPU) page size is == 718*3a634bfcSVikram Hegde * IOMMU pages size. 719*3a634bfcSVikram Hegde */ 720*3a634bfcSVikram Hegde /*LINTED*/ 721*3a634bfcSVikram Hegde if (MMU_PAGESIZE != IMMU_PAGESIZE) { 722*3a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, 723*3a634bfcSVikram Hegde "MMU page size (%d) is not equal to\n" 724*3a634bfcSVikram Hegde "IOMMU page size (%d). " 725*3a634bfcSVikram Hegde "Disabling Intel IOMMU. ", 726*3a634bfcSVikram Hegde MMU_PAGESIZE, IMMU_PAGESIZE); 727*3a634bfcSVikram Hegde immu_enable = B_FALSE; 728*3a634bfcSVikram Hegde return; 729*3a634bfcSVikram Hegde } 730*3a634bfcSVikram Hegde 731*3a634bfcSVikram Hegde /* 732*3a634bfcSVikram Hegde * retrieve the Intel IOMMU boot options. 733*3a634bfcSVikram Hegde * Do this before parsing immu ACPI table 734*3a634bfcSVikram Hegde * as a boot option could potentially affect 735*3a634bfcSVikram Hegde * ACPI parsing. 736*3a634bfcSVikram Hegde */ 737*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n"); 738*3a634bfcSVikram Hegde read_boot_options(); 739*3a634bfcSVikram Hegde 740*3a634bfcSVikram Hegde /* 741*3a634bfcSVikram Hegde * Check the IOMMU enable boot-option first. 742*3a634bfcSVikram Hegde * This is so that we can skip parsing the ACPI table 743*3a634bfcSVikram Hegde * if necessary because that may cause problems in 744*3a634bfcSVikram Hegde * systems with buggy BIOS or ACPI tables 745*3a634bfcSVikram Hegde */ 746*3a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 747*3a634bfcSVikram Hegde return; 748*3a634bfcSVikram Hegde } 749*3a634bfcSVikram Hegde 750*3a634bfcSVikram Hegde /* 751*3a634bfcSVikram Hegde * Next, check if the system even has an Intel IOMMU 752*3a634bfcSVikram Hegde * We use the presence or absence of the IOMMU ACPI 753*3a634bfcSVikram Hegde * table to detect Intel IOMMU. 754*3a634bfcSVikram Hegde */ 755*3a634bfcSVikram Hegde if (immu_dmar_setup() != DDI_SUCCESS) { 756*3a634bfcSVikram Hegde immu_enable = B_FALSE; 757*3a634bfcSVikram Hegde return; 758*3a634bfcSVikram Hegde } 759*3a634bfcSVikram Hegde 760*3a634bfcSVikram Hegde /* 761*3a634bfcSVikram Hegde * Check blacklists 762*3a634bfcSVikram Hegde */ 763*3a634bfcSVikram Hegde blacklist_setup(); 764*3a634bfcSVikram Hegde 765*3a634bfcSVikram Hegde if (blacklisted_smbios() == B_TRUE) { 766*3a634bfcSVikram Hegde blacklist_destroy(); 767*3a634bfcSVikram Hegde immu_enable = B_FALSE; 768*3a634bfcSVikram Hegde return; 769*3a634bfcSVikram Hegde } 770*3a634bfcSVikram Hegde 771*3a634bfcSVikram Hegde if (blacklisted_driver() == B_TRUE) { 772*3a634bfcSVikram Hegde blacklist_destroy(); 773*3a634bfcSVikram Hegde immu_enable = B_FALSE; 774*3a634bfcSVikram Hegde return; 775*3a634bfcSVikram Hegde } 776*3a634bfcSVikram Hegde 777*3a634bfcSVikram Hegde /* 778*3a634bfcSVikram Hegde * Read the "raw" DMAR ACPI table to get information 779*3a634bfcSVikram Hegde * and convert into a form we can use. 780*3a634bfcSVikram Hegde */ 781*3a634bfcSVikram Hegde if (immu_dmar_parse() != DDI_SUCCESS) { 782*3a634bfcSVikram Hegde blacklist_destroy(); 783*3a634bfcSVikram Hegde immu_enable = B_FALSE; 784*3a634bfcSVikram Hegde return; 785*3a634bfcSVikram Hegde } 786*3a634bfcSVikram Hegde 787*3a634bfcSVikram Hegde /* 788*3a634bfcSVikram Hegde * now that we have processed the ACPI table 789*3a634bfcSVikram Hegde * check if we need to blacklist this system 790*3a634bfcSVikram Hegde * based on ACPI info 791*3a634bfcSVikram Hegde */ 792*3a634bfcSVikram Hegde if (blacklisted_acpi() == B_TRUE) { 793*3a634bfcSVikram Hegde immu_dmar_destroy(); 794*3a634bfcSVikram Hegde blacklist_destroy(); 795*3a634bfcSVikram Hegde immu_enable = B_FALSE; 796*3a634bfcSVikram Hegde return; 797*3a634bfcSVikram Hegde } 798*3a634bfcSVikram Hegde 799*3a634bfcSVikram Hegde blacklist_destroy(); 800*3a634bfcSVikram Hegde 801*3a634bfcSVikram Hegde /* 802*3a634bfcSVikram Hegde * Check if system has HW quirks. 803*3a634bfcSVikram Hegde */ 804*3a634bfcSVikram Hegde pre_setup_quirks(); 805*3a634bfcSVikram Hegde 806*3a634bfcSVikram Hegde /* Now do the rest of the setup */ 807*3a634bfcSVikram Hegde immu_subsystems_setup(); 808*3a634bfcSVikram Hegde 809*3a634bfcSVikram Hegde /* 810*3a634bfcSVikram Hegde * Now that the IMMU is setup, create a phony 811*3a634bfcSVikram Hegde * reg prop so that suspend/resume works 812*3a634bfcSVikram Hegde */ 813*3a634bfcSVikram Hegde if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg", 814*3a634bfcSVikram Hegde (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) { 815*3a634bfcSVikram Hegde ddi_err(DER_PANIC, NULL, "Failed to create reg prop for " 816*3a634bfcSVikram Hegde "rootnex node"); 817*3a634bfcSVikram Hegde /*NOTREACHED*/ 818*3a634bfcSVikram Hegde } 819*3a634bfcSVikram Hegde 820*3a634bfcSVikram Hegde immu_setup = B_TRUE; 821*3a634bfcSVikram Hegde } 822*3a634bfcSVikram Hegde 823*3a634bfcSVikram Hegde /* 824*3a634bfcSVikram Hegde * immu_startup() 825*3a634bfcSVikram Hegde * called directly by boot code to startup 826*3a634bfcSVikram Hegde * all units of the IOMMU 827*3a634bfcSVikram Hegde */ 828*3a634bfcSVikram Hegde void 829*3a634bfcSVikram Hegde immu_startup(void) 830*3a634bfcSVikram Hegde { 831*3a634bfcSVikram Hegde /* 832*3a634bfcSVikram Hegde * If IOMMU is disabled, do nothing 833*3a634bfcSVikram Hegde */ 834*3a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 835*3a634bfcSVikram Hegde return; 836*3a634bfcSVikram Hegde } 837*3a634bfcSVikram Hegde 838*3a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 839*3a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, " 840*3a634bfcSVikram Hegde "skipping IOMU startup"); 841*3a634bfcSVikram Hegde return; 842*3a634bfcSVikram Hegde } 843*3a634bfcSVikram Hegde 844*3a634bfcSVikram Hegde pre_startup_quirks(); 845*3a634bfcSVikram Hegde 846*3a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, 847*3a634bfcSVikram Hegde "?Starting Intel IOMMU (dmar) units...\n"); 848*3a634bfcSVikram Hegde 849*3a634bfcSVikram Hegde immu_subsystems_startup(); 850*3a634bfcSVikram Hegde 851*3a634bfcSVikram Hegde immu_running = B_TRUE; 852*3a634bfcSVikram Hegde } 853*3a634bfcSVikram Hegde 854*3a634bfcSVikram Hegde /* 855*3a634bfcSVikram Hegde * immu_map_sgl() 856*3a634bfcSVikram Hegde * called from rootnex_coredma_bindhdl() when Intel 857*3a634bfcSVikram Hegde * IOMMU is enabled to build DVMA cookies and map them. 858*3a634bfcSVikram Hegde */ 859*3a634bfcSVikram Hegde int 860*3a634bfcSVikram Hegde immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq, 861*3a634bfcSVikram Hegde int prealloc_count, dev_info_t *rdip) 862*3a634bfcSVikram Hegde { 863*3a634bfcSVikram Hegde if (immu_running == B_FALSE) { 864*3a634bfcSVikram Hegde return (DDI_DMA_USE_PHYSICAL); 865*3a634bfcSVikram Hegde } 866*3a634bfcSVikram Hegde 867*3a634bfcSVikram Hegde return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip, 868*3a634bfcSVikram Hegde IMMU_FLAGS_DMAHDL)); 869*3a634bfcSVikram Hegde } 870*3a634bfcSVikram Hegde 871*3a634bfcSVikram Hegde /* 872*3a634bfcSVikram Hegde * immu_unmap_sgl() 873*3a634bfcSVikram Hegde * called from rootnex_coredma_unbindhdl(), to unmap DVMA 874*3a634bfcSVikram Hegde * cookies and free them 875*3a634bfcSVikram Hegde */ 876*3a634bfcSVikram Hegde int 877*3a634bfcSVikram Hegde immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip) 878*3a634bfcSVikram Hegde { 879*3a634bfcSVikram Hegde if (immu_running == B_FALSE) { 880*3a634bfcSVikram Hegde return (DDI_DMA_USE_PHYSICAL); 881*3a634bfcSVikram Hegde } 882*3a634bfcSVikram Hegde 883*3a634bfcSVikram Hegde return (immu_dvma_unmap(hp, rdip)); 884*3a634bfcSVikram Hegde } 885*3a634bfcSVikram Hegde 886*3a634bfcSVikram Hegde /* 887*3a634bfcSVikram Hegde * Hook to notify IOMMU code of device tree changes 888*3a634bfcSVikram Hegde */ 889*3a634bfcSVikram Hegde void 890*3a634bfcSVikram Hegde immu_device_tree_changed(void) 891*3a634bfcSVikram Hegde { 892*3a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 893*3a634bfcSVikram Hegde return; 894*3a634bfcSVikram Hegde } 895*3a634bfcSVikram Hegde 896*3a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU currently " 897*3a634bfcSVikram Hegde "does not use device tree updates"); 898*3a634bfcSVikram Hegde } 899*3a634bfcSVikram Hegde 900*3a634bfcSVikram Hegde /* 901*3a634bfcSVikram Hegde * Hook to notify IOMMU code of memory changes 902*3a634bfcSVikram Hegde */ 903*3a634bfcSVikram Hegde void 904*3a634bfcSVikram Hegde immu_physmem_update(uint64_t addr, uint64_t size) 905*3a634bfcSVikram Hegde { 906*3a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 907*3a634bfcSVikram Hegde return; 908*3a634bfcSVikram Hegde } 909*3a634bfcSVikram Hegde immu_dvma_physmem_update(addr, size); 910*3a634bfcSVikram Hegde } 911*3a634bfcSVikram Hegde 912*3a634bfcSVikram Hegde /* 913*3a634bfcSVikram Hegde * immu_quiesce() 914*3a634bfcSVikram Hegde * quiesce all units that are running 915*3a634bfcSVikram Hegde */ 916*3a634bfcSVikram Hegde int 917*3a634bfcSVikram Hegde immu_quiesce(void) 918*3a634bfcSVikram Hegde { 919*3a634bfcSVikram Hegde immu_t *immu; 920*3a634bfcSVikram Hegde int ret = DDI_SUCCESS; 921*3a634bfcSVikram Hegde 922*3a634bfcSVikram Hegde mutex_enter(&immu_lock); 923*3a634bfcSVikram Hegde 924*3a634bfcSVikram Hegde if (immu_running == B_FALSE) 925*3a634bfcSVikram Hegde return (DDI_SUCCESS); 926*3a634bfcSVikram Hegde 927*3a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 928*3a634bfcSVikram Hegde 929*3a634bfcSVikram Hegde immu = list_head(&immu_list); 930*3a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 931*3a634bfcSVikram Hegde 932*3a634bfcSVikram Hegde /* if immu is not running, we dont quiesce */ 933*3a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) 934*3a634bfcSVikram Hegde continue; 935*3a634bfcSVikram Hegde 936*3a634bfcSVikram Hegde /* flush caches */ 937*3a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 938*3a634bfcSVikram Hegde immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 939*3a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 940*3a634bfcSVikram Hegde immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 941*3a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 942*3a634bfcSVikram Hegde 943*3a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 944*3a634bfcSVikram Hegde 945*3a634bfcSVikram Hegde /* 946*3a634bfcSVikram Hegde * Set IOMMU unit's regs to do 947*3a634bfcSVikram Hegde * the actual shutdown. 948*3a634bfcSVikram Hegde */ 949*3a634bfcSVikram Hegde immu_regs_shutdown(immu); 950*3a634bfcSVikram Hegde immu_regs_suspend(immu); 951*3a634bfcSVikram Hegde 952*3a634bfcSVikram Hegde /* if immu is still running, we failed */ 953*3a634bfcSVikram Hegde if (immu->immu_regs_running == B_TRUE) 954*3a634bfcSVikram Hegde ret = DDI_FAILURE; 955*3a634bfcSVikram Hegde else 956*3a634bfcSVikram Hegde immu->immu_regs_quiesced = B_TRUE; 957*3a634bfcSVikram Hegde 958*3a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 959*3a634bfcSVikram Hegde } 960*3a634bfcSVikram Hegde mutex_exit(&immu_lock); 961*3a634bfcSVikram Hegde 962*3a634bfcSVikram Hegde if (ret == DDI_SUCCESS) { 963*3a634bfcSVikram Hegde immu_running = B_FALSE; 964*3a634bfcSVikram Hegde immu_quiesced = B_TRUE; 965*3a634bfcSVikram Hegde } 966*3a634bfcSVikram Hegde 967*3a634bfcSVikram Hegde return (ret); 968*3a634bfcSVikram Hegde } 969*3a634bfcSVikram Hegde 970*3a634bfcSVikram Hegde /* 971*3a634bfcSVikram Hegde * immu_unquiesce() 972*3a634bfcSVikram Hegde * unquiesce all units 973*3a634bfcSVikram Hegde */ 974*3a634bfcSVikram Hegde int 975*3a634bfcSVikram Hegde immu_unquiesce(void) 976*3a634bfcSVikram Hegde { 977*3a634bfcSVikram Hegde immu_t *immu; 978*3a634bfcSVikram Hegde int ret = DDI_SUCCESS; 979*3a634bfcSVikram Hegde 980*3a634bfcSVikram Hegde mutex_enter(&immu_lock); 981*3a634bfcSVikram Hegde 982*3a634bfcSVikram Hegde if (immu_quiesced == B_FALSE) 983*3a634bfcSVikram Hegde return (DDI_SUCCESS); 984*3a634bfcSVikram Hegde 985*3a634bfcSVikram Hegde ASSERT(immu_setup == B_TRUE); 986*3a634bfcSVikram Hegde ASSERT(immu_running == B_FALSE); 987*3a634bfcSVikram Hegde 988*3a634bfcSVikram Hegde immu = list_head(&immu_list); 989*3a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 990*3a634bfcSVikram Hegde 991*3a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 992*3a634bfcSVikram Hegde 993*3a634bfcSVikram Hegde /* if immu was not quiesced, i.e was not running before */ 994*3a634bfcSVikram Hegde if (immu->immu_regs_quiesced == B_FALSE) 995*3a634bfcSVikram Hegde continue; 996*3a634bfcSVikram Hegde 997*3a634bfcSVikram Hegde if (immu_regs_resume(immu) != DDI_SUCCESS) { 998*3a634bfcSVikram Hegde ret = DDI_FAILURE; 999*3a634bfcSVikram Hegde continue; 1000*3a634bfcSVikram Hegde } 1001*3a634bfcSVikram Hegde 1002*3a634bfcSVikram Hegde /* flush caches before unquiesce */ 1003*3a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 1004*3a634bfcSVikram Hegde immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 1005*3a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 1006*3a634bfcSVikram Hegde immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 1007*3a634bfcSVikram Hegde 1008*3a634bfcSVikram Hegde /* 1009*3a634bfcSVikram Hegde * Set IOMMU unit's regs to do 1010*3a634bfcSVikram Hegde * the actual startup. This will 1011*3a634bfcSVikram Hegde * set immu->immu_regs_running field 1012*3a634bfcSVikram Hegde * if the unit is successfully 1013*3a634bfcSVikram Hegde * started 1014*3a634bfcSVikram Hegde */ 1015*3a634bfcSVikram Hegde immu_regs_startup(immu); 1016*3a634bfcSVikram Hegde 1017*3a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) { 1018*3a634bfcSVikram Hegde ret = DDI_FAILURE; 1019*3a634bfcSVikram Hegde } else { 1020*3a634bfcSVikram Hegde immu_quiesced = B_TRUE; 1021*3a634bfcSVikram Hegde immu_running = B_TRUE; 1022*3a634bfcSVikram Hegde immu->immu_regs_quiesced = B_FALSE; 1023*3a634bfcSVikram Hegde } 1024*3a634bfcSVikram Hegde 1025*3a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 1026*3a634bfcSVikram Hegde } 1027*3a634bfcSVikram Hegde 1028*3a634bfcSVikram Hegde mutex_exit(&immu_lock); 1029*3a634bfcSVikram Hegde 1030*3a634bfcSVikram Hegde return (ret); 1031*3a634bfcSVikram Hegde } 1032*3a634bfcSVikram Hegde 1033*3a634bfcSVikram Hegde /* ############## END Intel IOMMU entry points ################## */ 1034