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 /* 229e986f0eSFrank Van Der Linden * Portions Copyright (c) 2010, Oracle and/or its affiliates. 239e986f0eSFrank 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/avl.h> 573a634bfcSVikram Hegde #include <sys/bootconf.h> 583a634bfcSVikram Hegde #include <sys/bootinfo.h> 593a634bfcSVikram Hegde #include <sys/atomic.h> 603a634bfcSVikram Hegde #include <sys/immu.h> 613a634bfcSVikram Hegde /* ########################### Globals and tunables ######################## */ 623a634bfcSVikram Hegde /* 633a634bfcSVikram Hegde * Global switches (boolean) that can be toggled either via boot options 643a634bfcSVikram Hegde * or via /etc/system or kmdb 653a634bfcSVikram Hegde */ 663a634bfcSVikram Hegde 673a634bfcSVikram Hegde /* Various features */ 683adb2334SVikram Hegde boolean_t immu_enable = B_TRUE; 693a634bfcSVikram Hegde boolean_t immu_dvma_enable = B_TRUE; 703a634bfcSVikram Hegde 713a634bfcSVikram Hegde /* accessed in other files so not static */ 723a634bfcSVikram Hegde boolean_t immu_gfxdvma_enable = B_TRUE; 733a634bfcSVikram Hegde boolean_t immu_intrmap_enable = B_FALSE; 7450200e77SFrank Van Der Linden boolean_t immu_qinv_enable = B_TRUE; 753a634bfcSVikram Hegde 763a634bfcSVikram Hegde /* various quirks that need working around */ 773a634bfcSVikram Hegde 783a634bfcSVikram Hegde /* XXX We always map page 0 read/write for now */ 793a634bfcSVikram Hegde boolean_t immu_quirk_usbpage0 = B_TRUE; 803a634bfcSVikram Hegde boolean_t immu_quirk_usbrmrr = B_TRUE; 813a634bfcSVikram Hegde boolean_t immu_quirk_usbfullpa; 823a634bfcSVikram Hegde boolean_t immu_quirk_mobile4; 833a634bfcSVikram Hegde 843a634bfcSVikram Hegde /* debug messages */ 853a634bfcSVikram Hegde boolean_t immu_dmar_print; 863a634bfcSVikram Hegde 87e03dceedSVikram Hegde /* Tunables */ 88e03dceedSVikram Hegde int64_t immu_flush_gran = 5; 89e03dceedSVikram Hegde 909e986f0eSFrank Van Der Linden immu_flags_t immu_global_dvma_flags; 919e986f0eSFrank Van Der Linden 923a634bfcSVikram Hegde /* ############ END OPTIONS section ################ */ 933a634bfcSVikram Hegde 943a634bfcSVikram Hegde /* 953a634bfcSVikram Hegde * Global used internally by Intel IOMMU code 963a634bfcSVikram Hegde */ 973a634bfcSVikram Hegde dev_info_t *root_devinfo; 983a634bfcSVikram Hegde kmutex_t immu_lock; 993a634bfcSVikram Hegde list_t immu_list; 1003a634bfcSVikram Hegde boolean_t immu_setup; 1013a634bfcSVikram Hegde boolean_t immu_running; 1023a634bfcSVikram Hegde boolean_t immu_quiesced; 1033a634bfcSVikram Hegde 1043a634bfcSVikram Hegde /* ######################## END Globals and tunables ###################### */ 1053a634bfcSVikram Hegde /* Globals used only in this file */ 1063a634bfcSVikram Hegde static char **black_array; 1073a634bfcSVikram Hegde static uint_t nblacks; 1089e986f0eSFrank Van Der Linden 1099e986f0eSFrank Van Der Linden static char **unity_driver_array; 1109e986f0eSFrank Van Der Linden static uint_t nunity; 1119e986f0eSFrank Van Der Linden static char **xlate_driver_array; 1129e986f0eSFrank Van Der Linden static uint_t nxlate; 11350200e77SFrank Van Der Linden 11450200e77SFrank Van Der Linden static char **premap_driver_array; 11550200e77SFrank Van Der Linden static uint_t npremap; 11650200e77SFrank Van Der Linden static char **nopremap_driver_array; 11750200e77SFrank Van Der Linden static uint_t nnopremap; 1183a634bfcSVikram Hegde /* ###################### Utility routines ############################# */ 1193a634bfcSVikram Hegde 1203a634bfcSVikram Hegde /* 1213a634bfcSVikram Hegde * Check if the device has mobile 4 chipset 1223a634bfcSVikram Hegde */ 1233a634bfcSVikram Hegde static int 1243a634bfcSVikram Hegde check_mobile4(dev_info_t *dip, void *arg) 1253a634bfcSVikram Hegde { 1263a634bfcSVikram Hegde _NOTE(ARGUNUSED(arg)); 1273a634bfcSVikram Hegde int vendor, device; 1283a634bfcSVikram Hegde int *ip = (int *)arg; 1293a634bfcSVikram Hegde 1303a634bfcSVikram Hegde vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1313a634bfcSVikram Hegde "vendor-id", -1); 1323a634bfcSVikram Hegde device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1333a634bfcSVikram Hegde "device-id", -1); 1343a634bfcSVikram Hegde 1353a634bfcSVikram Hegde if (vendor == 0x8086 && device == 0x2a40) { 1363a634bfcSVikram Hegde *ip = B_TRUE; 13750200e77SFrank Van Der Linden ddi_err(DER_NOTE, dip, "iommu: Mobile 4 chipset detected. " 1383a634bfcSVikram Hegde "Force setting IOMMU write buffer"); 1393a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 1403a634bfcSVikram Hegde } else { 1413a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 1423a634bfcSVikram Hegde } 1433a634bfcSVikram Hegde } 1443a634bfcSVikram Hegde 1453a634bfcSVikram Hegde static void 1463a634bfcSVikram Hegde map_bios_rsvd_mem(dev_info_t *dip) 1473a634bfcSVikram Hegde { 1483a634bfcSVikram Hegde struct memlist *mp; 14950200e77SFrank Van Der Linden 15050200e77SFrank Van Der Linden /* 15150200e77SFrank Van Der Linden * Make sure the domain for the device is set up before 15250200e77SFrank Van Der Linden * mapping anything. 15350200e77SFrank Van Der Linden */ 15450200e77SFrank Van Der Linden (void) immu_dvma_device_setup(dip, 0); 1553a634bfcSVikram Hegde 1563a634bfcSVikram Hegde memlist_read_lock(); 1573a634bfcSVikram Hegde 1583a634bfcSVikram Hegde mp = bios_rsvd; 1593a634bfcSVikram Hegde while (mp != NULL) { 160e03dceedSVikram Hegde memrng_t mrng = {0}; 1613a634bfcSVikram Hegde 16250200e77SFrank Van Der Linden ddi_err(DER_LOG, dip, "iommu: Mapping BIOS rsvd range " 1633a634bfcSVikram Hegde "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address, 1643a634bfcSVikram Hegde mp->ml_address + mp->ml_size); 1653a634bfcSVikram Hegde 166e03dceedSVikram Hegde mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address); 167e03dceedSVikram Hegde mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE; 1683a634bfcSVikram Hegde 16950200e77SFrank Van Der Linden (void) immu_map_memrange(dip, &mrng); 1703a634bfcSVikram Hegde 1713a634bfcSVikram Hegde mp = mp->ml_next; 1723a634bfcSVikram Hegde } 1733a634bfcSVikram Hegde 1743a634bfcSVikram Hegde memlist_read_unlock(); 1753a634bfcSVikram Hegde } 1763a634bfcSVikram Hegde 177e03dceedSVikram Hegde 178e03dceedSVikram Hegde /* 1799e986f0eSFrank Van Der Linden * Check if the driver requests a specific type of mapping. 180e03dceedSVikram Hegde */ 181e03dceedSVikram Hegde /*ARGSUSED*/ 182e03dceedSVikram Hegde static void 1839e986f0eSFrank Van Der Linden check_conf(dev_info_t *dip, void *arg) 184e03dceedSVikram Hegde { 1859e986f0eSFrank Van Der Linden immu_devi_t *immu_devi; 1869e986f0eSFrank Van Der Linden const char *dname; 1879e986f0eSFrank Van Der Linden uint_t i; 18850200e77SFrank Van Der Linden int hasmapprop = 0, haspreprop = 0; 18950200e77SFrank Van Der Linden boolean_t old_premap; 190e03dceedSVikram Hegde 191e03dceedSVikram Hegde /* 1929e986f0eSFrank Van Der Linden * Only PCI devices can use an IOMMU. Legacy ISA devices 1939e986f0eSFrank Van Der Linden * are handled in check_lpc. 194e03dceedSVikram Hegde */ 1959e986f0eSFrank Van Der Linden if (!DEVI_IS_PCI(dip)) 1969e986f0eSFrank Van Der Linden return; 197e03dceedSVikram Hegde 1989e986f0eSFrank Van Der Linden dname = ddi_driver_name(dip); 1999e986f0eSFrank Van Der Linden if (dname == NULL) 2009e986f0eSFrank Van Der Linden return; 2019e986f0eSFrank Van Der Linden immu_devi = immu_devi_get(dip); 2029e986f0eSFrank Van Der Linden 2039e986f0eSFrank Van Der Linden for (i = 0; i < nunity; i++) { 2049e986f0eSFrank Van Der Linden if (strcmp(unity_driver_array[i], dname) == 0) { 20550200e77SFrank Van Der Linden hasmapprop = 1; 2069e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 207e03dceedSVikram Hegde } 208e03dceedSVikram Hegde } 2099e986f0eSFrank Van Der Linden 2109e986f0eSFrank Van Der Linden for (i = 0; i < nxlate; i++) { 2119e986f0eSFrank Van Der Linden if (strcmp(xlate_driver_array[i], dname) == 0) { 21250200e77SFrank Van Der Linden hasmapprop = 1; 2139e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags &= ~IMMU_FLAGS_UNITY; 2149e986f0eSFrank Van Der Linden } 2159e986f0eSFrank Van Der Linden } 2169e986f0eSFrank Van Der Linden 21750200e77SFrank Van Der Linden old_premap = immu_devi->imd_use_premap; 21850200e77SFrank Van Der Linden 21950200e77SFrank Van Der Linden for (i = 0; i < nnopremap; i++) { 22050200e77SFrank Van Der Linden if (strcmp(nopremap_driver_array[i], dname) == 0) { 22150200e77SFrank Van Der Linden haspreprop = 1; 22250200e77SFrank Van Der Linden immu_devi->imd_use_premap = B_FALSE; 22350200e77SFrank Van Der Linden } 22450200e77SFrank Van Der Linden } 22550200e77SFrank Van Der Linden 22650200e77SFrank Van Der Linden for (i = 0; i < npremap; i++) { 22750200e77SFrank Van Der Linden if (strcmp(premap_driver_array[i], dname) == 0) { 22850200e77SFrank Van Der Linden haspreprop = 1; 22950200e77SFrank Van Der Linden immu_devi->imd_use_premap = B_TRUE; 23050200e77SFrank Van Der Linden } 23150200e77SFrank Van Der Linden } 23250200e77SFrank Van Der Linden 2339e986f0eSFrank Van Der Linden /* 2349e986f0eSFrank Van Der Linden * Report if we changed the value from the default. 2359e986f0eSFrank Van Der Linden */ 23650200e77SFrank Van Der Linden if (hasmapprop && (immu_devi->imd_dvma_flags ^ immu_global_dvma_flags)) 2379e986f0eSFrank Van Der Linden ddi_err(DER_LOG, dip, "using %s DVMA mapping", 2389e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY ? 2399e986f0eSFrank Van Der Linden DDI_DVMA_MAPTYPE_UNITY : DDI_DVMA_MAPTYPE_XLATE); 24050200e77SFrank Van Der Linden 24150200e77SFrank Van Der Linden if (haspreprop && (immu_devi->imd_use_premap != old_premap)) 24250200e77SFrank Van Der Linden ddi_err(DER_LOG, dip, "%susing premapped DVMA space", 24350200e77SFrank Van Der Linden immu_devi->imd_use_premap ? "" : "not "); 244e03dceedSVikram Hegde } 245e03dceedSVikram Hegde 2463a634bfcSVikram Hegde /* 2473a634bfcSVikram Hegde * Check if the device is USB controller 2483a634bfcSVikram Hegde */ 2493a634bfcSVikram Hegde /*ARGSUSED*/ 2503a634bfcSVikram Hegde static void 2513a634bfcSVikram Hegde check_usb(dev_info_t *dip, void *arg) 2523a634bfcSVikram Hegde { 2533a634bfcSVikram Hegde const char *drv = ddi_driver_name(dip); 2549e986f0eSFrank Van Der Linden immu_devi_t *immu_devi; 2559e986f0eSFrank Van Der Linden 2563a634bfcSVikram Hegde 2573a634bfcSVikram Hegde if (drv == NULL || 2583a634bfcSVikram Hegde (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 && 2593a634bfcSVikram Hegde strcmp(drv, "ehci") != 0)) { 2603a634bfcSVikram Hegde return; 2613a634bfcSVikram Hegde } 2623a634bfcSVikram Hegde 2639e986f0eSFrank Van Der Linden immu_devi = immu_devi_get(dip); 2649e986f0eSFrank Van Der Linden 2659e986f0eSFrank Van Der Linden /* 2669e986f0eSFrank Van Der Linden * If unit mappings are already specified, globally or 2679e986f0eSFrank Van Der Linden * locally, we're done here, since that covers both 2689e986f0eSFrank Van Der Linden * quirks below. 2699e986f0eSFrank Van Der Linden */ 2709e986f0eSFrank Van Der Linden if (immu_devi->imd_dvma_flags & IMMU_FLAGS_UNITY) 2719e986f0eSFrank Van Der Linden return; 2729e986f0eSFrank Van Der Linden 2733a634bfcSVikram Hegde /* This must come first since it does unity mapping */ 2743a634bfcSVikram Hegde if (immu_quirk_usbfullpa == B_TRUE) { 2759e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 2769e986f0eSFrank Van Der Linden } else if (immu_quirk_usbrmrr == B_TRUE) { 2773a634bfcSVikram Hegde ddi_err(DER_LOG, dip, "Applying USB RMRR quirk"); 2783a634bfcSVikram Hegde map_bios_rsvd_mem(dip); 2793a634bfcSVikram Hegde } 2803a634bfcSVikram Hegde } 2813a634bfcSVikram Hegde 2823a634bfcSVikram Hegde /* 2833a634bfcSVikram Hegde * Check if the device is a LPC device 2843a634bfcSVikram Hegde */ 2853a634bfcSVikram Hegde /*ARGSUSED*/ 2863a634bfcSVikram Hegde static void 2873a634bfcSVikram Hegde check_lpc(dev_info_t *dip, void *arg) 2883a634bfcSVikram Hegde { 2893a634bfcSVikram Hegde immu_devi_t *immu_devi; 2903a634bfcSVikram Hegde 2913a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 2923a634bfcSVikram Hegde if (immu_devi->imd_lpc == B_TRUE) { 29350200e77SFrank Van Der Linden ddi_err(DER_LOG, dip, "iommu: Found LPC device"); 2943a634bfcSVikram Hegde /* This will put the immu_devi on the LPC "specials" list */ 29550200e77SFrank Van Der Linden (void) immu_dvma_device_setup(dip, IMMU_FLAGS_SLEEP); 2963a634bfcSVikram Hegde } 2973a634bfcSVikram Hegde } 2983a634bfcSVikram Hegde 2993a634bfcSVikram Hegde /* 3003a634bfcSVikram Hegde * Check if the device is a GFX device 3013a634bfcSVikram Hegde */ 3023a634bfcSVikram Hegde /*ARGSUSED*/ 3033a634bfcSVikram Hegde static void 3043a634bfcSVikram Hegde check_gfx(dev_info_t *dip, void *arg) 3053a634bfcSVikram Hegde { 3063a634bfcSVikram Hegde immu_devi_t *immu_devi; 3073a634bfcSVikram Hegde 3083a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 3093a634bfcSVikram Hegde if (immu_devi->imd_display == B_TRUE) { 3109e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags |= IMMU_FLAGS_UNITY; 31150200e77SFrank Van Der Linden ddi_err(DER_LOG, dip, "iommu: Found GFX device"); 3123a634bfcSVikram Hegde /* This will put the immu_devi on the GFX "specials" list */ 3133a634bfcSVikram Hegde (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 3143a634bfcSVikram Hegde } 3153a634bfcSVikram Hegde } 3163a634bfcSVikram Hegde 3173a634bfcSVikram Hegde static void 3183a634bfcSVikram Hegde walk_tree(int (*f)(dev_info_t *, void *), void *arg) 3193a634bfcSVikram Hegde { 3203a634bfcSVikram Hegde int count; 3213a634bfcSVikram Hegde 3223a634bfcSVikram Hegde ndi_devi_enter(root_devinfo, &count); 3233a634bfcSVikram Hegde ddi_walk_devs(ddi_get_child(root_devinfo), f, arg); 3243a634bfcSVikram Hegde ndi_devi_exit(root_devinfo, count); 3253a634bfcSVikram Hegde } 3263a634bfcSVikram Hegde 3273a634bfcSVikram Hegde static int 3283a634bfcSVikram Hegde check_pre_setup_quirks(dev_info_t *dip, void *arg) 3293a634bfcSVikram Hegde { 3303a634bfcSVikram Hegde /* just 1 check right now */ 3313a634bfcSVikram Hegde return (check_mobile4(dip, arg)); 3323a634bfcSVikram Hegde } 3333a634bfcSVikram Hegde 3343a634bfcSVikram Hegde static int 3353a634bfcSVikram Hegde check_pre_startup_quirks(dev_info_t *dip, void *arg) 3363a634bfcSVikram Hegde { 3373a634bfcSVikram Hegde if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) { 3383a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "Failed to get immu_devi"); 3393a634bfcSVikram Hegde } 3403a634bfcSVikram Hegde 3413a634bfcSVikram Hegde check_gfx(dip, arg); 3423a634bfcSVikram Hegde 3433a634bfcSVikram Hegde check_lpc(dip, arg); 3443a634bfcSVikram Hegde 3459e986f0eSFrank Van Der Linden check_conf(dip, arg); 3463a634bfcSVikram Hegde 3479e986f0eSFrank Van Der Linden check_usb(dip, arg); 348e03dceedSVikram Hegde 3493a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 3503a634bfcSVikram Hegde } 3513a634bfcSVikram Hegde 3523a634bfcSVikram Hegde static void 3533a634bfcSVikram Hegde pre_setup_quirks(void) 3543a634bfcSVikram Hegde { 3553a634bfcSVikram Hegde walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4); 3563a634bfcSVikram Hegde } 3573a634bfcSVikram Hegde 3583a634bfcSVikram Hegde static void 3593a634bfcSVikram Hegde pre_startup_quirks(void) 3603a634bfcSVikram Hegde { 3613a634bfcSVikram Hegde walk_tree(check_pre_startup_quirks, NULL); 3623a634bfcSVikram Hegde 3633a634bfcSVikram Hegde immu_dmar_rmrr_map(); 3643a634bfcSVikram Hegde } 3653a634bfcSVikram Hegde 3669e986f0eSFrank Van Der Linden static int 3679e986f0eSFrank Van Der Linden get_conf_str(char *bopt, char **val) 3689e986f0eSFrank Van Der Linden { 3699e986f0eSFrank Van Der Linden int ret; 3709e986f0eSFrank Van Der Linden 3719e986f0eSFrank Van Der Linden /* 3729e986f0eSFrank Van Der Linden * Check the rootnex.conf property 3739e986f0eSFrank Van Der Linden * Fake up a dev_t since searching the global 3749e986f0eSFrank Van Der Linden * property list needs it 3759e986f0eSFrank Van Der Linden */ 3769e986f0eSFrank Van Der Linden ret = ddi_prop_lookup_string( 3779e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), 3789e986f0eSFrank Van Der Linden root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 3799e986f0eSFrank Van Der Linden bopt, val); 3809e986f0eSFrank Van Der Linden 3819e986f0eSFrank Van Der Linden return (ret); 3829e986f0eSFrank Van Der Linden } 3839e986f0eSFrank Van Der Linden 3843a634bfcSVikram Hegde /* 3853adb2334SVikram Hegde * get_conf_opt() 3863adb2334SVikram Hegde * get a rootnex.conf setting (always a boolean) 3873adb2334SVikram Hegde */ 3883adb2334SVikram Hegde static void 3893adb2334SVikram Hegde get_conf_opt(char *bopt, boolean_t *kvar) 3903adb2334SVikram Hegde { 3913adb2334SVikram Hegde char *val = NULL; 3923adb2334SVikram Hegde 3933adb2334SVikram Hegde /* 3943adb2334SVikram Hegde * Check the rootnex.conf property 3953adb2334SVikram Hegde * Fake up a dev_t since searching the global 3963adb2334SVikram Hegde * property list needs it 3973adb2334SVikram Hegde */ 3983adb2334SVikram Hegde 3999e986f0eSFrank Van Der Linden if (get_conf_str(bopt, &val) != DDI_PROP_SUCCESS) 4009e986f0eSFrank Van Der Linden return; 4019e986f0eSFrank Van Der Linden 4023adb2334SVikram Hegde if (strcmp(val, "true") == 0) { 4033adb2334SVikram Hegde *kvar = B_TRUE; 4043adb2334SVikram Hegde } else if (strcmp(val, "false") == 0) { 4053adb2334SVikram Hegde *kvar = B_FALSE; 4063adb2334SVikram Hegde } else { 4073adb2334SVikram Hegde ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ", 4083adb2334SVikram Hegde "is not set to true or false. Ignoring option.", 4093adb2334SVikram Hegde bopt, val); 4103adb2334SVikram Hegde } 4113adb2334SVikram Hegde ddi_prop_free(val); 4123adb2334SVikram Hegde } 4133adb2334SVikram Hegde 4143adb2334SVikram Hegde /* 4153a634bfcSVikram Hegde * get_bootopt() 4163a634bfcSVikram Hegde * check a boot option (always a boolean) 4173a634bfcSVikram Hegde */ 4189e986f0eSFrank Van Der Linden static int 4199e986f0eSFrank Van Der Linden get_boot_str(char *bopt, char **val) 4209e986f0eSFrank Van Der Linden { 4219e986f0eSFrank Van Der Linden int ret; 4229e986f0eSFrank Van Der Linden 4239e986f0eSFrank Van Der Linden ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo, 4249e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS, bopt, val); 4259e986f0eSFrank Van Der Linden 4269e986f0eSFrank Van Der Linden return (ret); 4279e986f0eSFrank Van Der Linden } 4289e986f0eSFrank Van Der Linden 4293a634bfcSVikram Hegde static void 4303a634bfcSVikram Hegde get_bootopt(char *bopt, boolean_t *kvar) 4313a634bfcSVikram Hegde { 4323a634bfcSVikram Hegde char *val = NULL; 4333a634bfcSVikram Hegde 4343a634bfcSVikram Hegde /* 4353a634bfcSVikram Hegde * All boot options set at the GRUB menu become 4363a634bfcSVikram Hegde * properties on the rootnex. 4373a634bfcSVikram Hegde */ 4389e986f0eSFrank Van Der Linden if (get_boot_str(bopt, &val) != DDI_PROP_SUCCESS) 4399e986f0eSFrank Van Der Linden return; 4409e986f0eSFrank Van Der Linden 4413a634bfcSVikram Hegde if (strcmp(val, "true") == 0) { 4423a634bfcSVikram Hegde *kvar = B_TRUE; 4433a634bfcSVikram Hegde } else if (strcmp(val, "false") == 0) { 4443a634bfcSVikram Hegde *kvar = B_FALSE; 4453a634bfcSVikram Hegde } else { 4463a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ", 4473a634bfcSVikram Hegde "is not set to true or false. Ignoring option.", 4483a634bfcSVikram Hegde bopt, val); 4493a634bfcSVikram Hegde } 4503a634bfcSVikram Hegde ddi_prop_free(val); 4513a634bfcSVikram Hegde } 4529e986f0eSFrank Van Der Linden 4539e986f0eSFrank Van Der Linden static void 4549e986f0eSFrank Van Der Linden get_boot_dvma_mode(void) 4559e986f0eSFrank Van Der Linden { 4569e986f0eSFrank Van Der Linden char *val = NULL; 4579e986f0eSFrank Van Der Linden 4589e986f0eSFrank Van Der Linden if (get_boot_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val) 4599e986f0eSFrank Van Der Linden != DDI_PROP_SUCCESS) 4609e986f0eSFrank Van Der Linden return; 4619e986f0eSFrank Van Der Linden 4629e986f0eSFrank Van Der Linden if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) { 4639e986f0eSFrank Van Der Linden immu_global_dvma_flags |= IMMU_FLAGS_UNITY; 4649e986f0eSFrank Van Der Linden } else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) { 4659e986f0eSFrank Van Der Linden immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY; 4669e986f0eSFrank Van Der Linden } else { 4679e986f0eSFrank Van Der Linden ddi_err(DER_WARN, NULL, "bad value \"%s\" for boot option %s", 4689e986f0eSFrank Van Der Linden val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP); 4693a634bfcSVikram Hegde } 4709e986f0eSFrank Van Der Linden ddi_prop_free(val); 4719e986f0eSFrank Van Der Linden } 4729e986f0eSFrank Van Der Linden 4739e986f0eSFrank Van Der Linden static void 4749e986f0eSFrank Van Der Linden get_conf_dvma_mode(void) 4759e986f0eSFrank Van Der Linden { 4769e986f0eSFrank Van Der Linden char *val = NULL; 4779e986f0eSFrank Van Der Linden 4789e986f0eSFrank Van Der Linden if (get_conf_str(DDI_DVMA_MAPTYPE_ROOTNEX_PROP, &val) 4799e986f0eSFrank Van Der Linden != DDI_PROP_SUCCESS) 4809e986f0eSFrank Van Der Linden return; 4819e986f0eSFrank Van Der Linden 4829e986f0eSFrank Van Der Linden if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) == 0) { 4839e986f0eSFrank Van Der Linden immu_global_dvma_flags |= IMMU_FLAGS_UNITY; 4849e986f0eSFrank Van Der Linden } else if (strcmp(val, DDI_DVMA_MAPTYPE_XLATE) == 0) { 4859e986f0eSFrank Van Der Linden immu_global_dvma_flags &= ~IMMU_FLAGS_UNITY; 4869e986f0eSFrank Van Der Linden } else { 4879e986f0eSFrank Van Der Linden ddi_err(DER_WARN, NULL, "bad value \"%s\" for rootnex " 4889e986f0eSFrank Van Der Linden "option %s", val, DDI_DVMA_MAPTYPE_ROOTNEX_PROP); 4899e986f0eSFrank Van Der Linden } 4909e986f0eSFrank Van Der Linden ddi_prop_free(val); 4919e986f0eSFrank Van Der Linden } 4929e986f0eSFrank Van Der Linden 4933a634bfcSVikram Hegde 4943a634bfcSVikram Hegde static void 4953adb2334SVikram Hegde get_conf_tunables(char *bopt, int64_t *ivar) 496e03dceedSVikram Hegde { 497e03dceedSVikram Hegde int64_t *iarray; 498e03dceedSVikram Hegde uint_t n; 499e03dceedSVikram Hegde 500e03dceedSVikram Hegde /* 501e03dceedSVikram Hegde * Check the rootnex.conf property 502e03dceedSVikram Hegde * Fake up a dev_t since searching the global 503e03dceedSVikram Hegde * property list needs it 504e03dceedSVikram Hegde */ 505e03dceedSVikram Hegde if (ddi_prop_lookup_int64_array( 506e03dceedSVikram Hegde makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 507e03dceedSVikram Hegde DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt, 508e03dceedSVikram Hegde &iarray, &n) != DDI_PROP_SUCCESS) { 509e03dceedSVikram Hegde return; 510e03dceedSVikram Hegde } 511e03dceedSVikram Hegde 512e03dceedSVikram Hegde if (n != 1) { 513e03dceedSVikram Hegde ddi_err(DER_WARN, NULL, "More than one value specified for " 514e03dceedSVikram Hegde "%s property. Ignoring and using default", 515e03dceedSVikram Hegde "immu-flush-gran"); 516e03dceedSVikram Hegde ddi_prop_free(iarray); 517e03dceedSVikram Hegde return; 518e03dceedSVikram Hegde } 519e03dceedSVikram Hegde 520e03dceedSVikram Hegde if (iarray[0] < 0) { 521e03dceedSVikram Hegde ddi_err(DER_WARN, NULL, "Negative value specified for " 522e03dceedSVikram Hegde "%s property. Inoring and Using default value", 523e03dceedSVikram Hegde "immu-flush-gran"); 524e03dceedSVikram Hegde ddi_prop_free(iarray); 525e03dceedSVikram Hegde return; 526e03dceedSVikram Hegde } 527e03dceedSVikram Hegde 528e03dceedSVikram Hegde *ivar = iarray[0]; 529e03dceedSVikram Hegde 530e03dceedSVikram Hegde ddi_prop_free(iarray); 531e03dceedSVikram Hegde } 532e03dceedSVikram Hegde 533e03dceedSVikram Hegde static void 5343adb2334SVikram Hegde read_conf_options(void) 5353adb2334SVikram Hegde { 5363adb2334SVikram Hegde /* enable/disable options */ 5373adb2334SVikram Hegde get_conf_opt("immu-enable", &immu_enable); 5383adb2334SVikram Hegde get_conf_opt("immu-dvma-enable", &immu_dvma_enable); 5393adb2334SVikram Hegde get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 5403adb2334SVikram Hegde get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable); 5413adb2334SVikram Hegde get_conf_opt("immu-qinv-enable", &immu_qinv_enable); 5423adb2334SVikram Hegde 5433adb2334SVikram Hegde /* workaround switches */ 5443adb2334SVikram Hegde get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 5453adb2334SVikram Hegde get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 5463adb2334SVikram Hegde get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 5473adb2334SVikram Hegde 5483adb2334SVikram Hegde /* debug printing */ 5493adb2334SVikram Hegde get_conf_opt("immu-dmar-print", &immu_dmar_print); 5503adb2334SVikram Hegde 5513adb2334SVikram Hegde /* get tunables */ 5523adb2334SVikram Hegde get_conf_tunables("immu-flush-gran", &immu_flush_gran); 5539e986f0eSFrank Van Der Linden 5549e986f0eSFrank Van Der Linden get_conf_dvma_mode(); 5553adb2334SVikram Hegde } 5563adb2334SVikram Hegde 5573adb2334SVikram Hegde static void 5583a634bfcSVikram Hegde read_boot_options(void) 5593a634bfcSVikram Hegde { 5603a634bfcSVikram Hegde /* enable/disable options */ 5613a634bfcSVikram Hegde get_bootopt("immu-enable", &immu_enable); 5623a634bfcSVikram Hegde get_bootopt("immu-dvma-enable", &immu_dvma_enable); 5633a634bfcSVikram Hegde get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 5643a634bfcSVikram Hegde get_bootopt("immu-intrmap-enable", &immu_intrmap_enable); 5653a634bfcSVikram Hegde get_bootopt("immu-qinv-enable", &immu_qinv_enable); 5663a634bfcSVikram Hegde 5673a634bfcSVikram Hegde /* workaround switches */ 5683a634bfcSVikram Hegde get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 5693a634bfcSVikram Hegde get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 5703a634bfcSVikram Hegde get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 5713a634bfcSVikram Hegde 5723a634bfcSVikram Hegde /* debug printing */ 5733a634bfcSVikram Hegde get_bootopt("immu-dmar-print", &immu_dmar_print); 5749e986f0eSFrank Van Der Linden 5759e986f0eSFrank Van Der Linden get_boot_dvma_mode(); 5769e986f0eSFrank Van Der Linden } 5779e986f0eSFrank Van Der Linden 5789e986f0eSFrank Van Der Linden static void 5799e986f0eSFrank Van Der Linden mapping_list_setup(void) 5809e986f0eSFrank Van Der Linden { 5819e986f0eSFrank Van Der Linden char **string_array; 5829e986f0eSFrank Van Der Linden uint_t nstrings; 5839e986f0eSFrank Van Der Linden 5849e986f0eSFrank Van Der Linden if (ddi_prop_lookup_string_array( 5859e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 5869e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 5879e986f0eSFrank Van Der Linden "immu-dvma-unity-drivers", 5889e986f0eSFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 5899e986f0eSFrank Van Der Linden unity_driver_array = string_array; 5909e986f0eSFrank Van Der Linden nunity = nstrings; 5919e986f0eSFrank Van Der Linden } 5929e986f0eSFrank Van Der Linden 5939e986f0eSFrank Van Der Linden if (ddi_prop_lookup_string_array( 5949e986f0eSFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 5959e986f0eSFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 5969e986f0eSFrank Van Der Linden "immu-dvma-xlate-drivers", 5979e986f0eSFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 5989e986f0eSFrank Van Der Linden xlate_driver_array = string_array; 5999e986f0eSFrank Van Der Linden nxlate = nstrings; 6009e986f0eSFrank Van Der Linden } 60150200e77SFrank Van Der Linden 60250200e77SFrank Van Der Linden if (ddi_prop_lookup_string_array( 60350200e77SFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 60450200e77SFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 60550200e77SFrank Van Der Linden "immu-dvma-premap-drivers", 60650200e77SFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 60750200e77SFrank Van Der Linden premap_driver_array = string_array; 60850200e77SFrank Van Der Linden npremap = nstrings; 60950200e77SFrank Van Der Linden } 61050200e77SFrank Van Der Linden 61150200e77SFrank Van Der Linden if (ddi_prop_lookup_string_array( 61250200e77SFrank Van Der Linden makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 61350200e77SFrank Van Der Linden DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 61450200e77SFrank Van Der Linden "immu-dvma-nopremap-drivers", 61550200e77SFrank Van Der Linden &string_array, &nstrings) == DDI_PROP_SUCCESS) { 61650200e77SFrank Van Der Linden nopremap_driver_array = string_array; 61750200e77SFrank Van Der Linden nnopremap = nstrings; 61850200e77SFrank Van Der Linden } 6193a634bfcSVikram Hegde } 6203a634bfcSVikram Hegde 6213a634bfcSVikram Hegde /* 6223a634bfcSVikram Hegde * Note, this will not catch hardware not enumerated 6233a634bfcSVikram Hegde * in early boot 6243a634bfcSVikram Hegde */ 6253a634bfcSVikram Hegde static boolean_t 6263a634bfcSVikram Hegde blacklisted_driver(void) 6273a634bfcSVikram Hegde { 6283a634bfcSVikram Hegde char **strptr; 6293a634bfcSVikram Hegde int i; 6303a634bfcSVikram Hegde major_t maj; 6313a634bfcSVikram Hegde 6323a634bfcSVikram Hegde /* need at least 2 strings */ 6333a634bfcSVikram Hegde if (nblacks < 2) { 6343a634bfcSVikram Hegde return (B_FALSE); 6353a634bfcSVikram Hegde } 6363a634bfcSVikram Hegde 6373a634bfcSVikram Hegde for (i = 0; nblacks - i > 1; i++) { 638e03dceedSVikram Hegde strptr = &black_array[i]; 6393a634bfcSVikram Hegde if (strcmp(*strptr++, "DRIVER") == 0) { 6403a634bfcSVikram Hegde if ((maj = ddi_name_to_major(*strptr++)) 6413a634bfcSVikram Hegde != DDI_MAJOR_T_NONE) { 6423a634bfcSVikram Hegde /* is there hardware bound to this drvr */ 6433a634bfcSVikram Hegde if (devnamesp[maj].dn_head != NULL) { 6443a634bfcSVikram Hegde return (B_TRUE); 6453a634bfcSVikram Hegde } 6463a634bfcSVikram Hegde } 6473a634bfcSVikram Hegde i += 1; /* for loop adds 1, so add only 1 here */ 6483a634bfcSVikram Hegde } 6493a634bfcSVikram Hegde } 6503a634bfcSVikram Hegde 6513a634bfcSVikram Hegde return (B_FALSE); 6523a634bfcSVikram Hegde } 6533a634bfcSVikram Hegde 6543a634bfcSVikram Hegde static boolean_t 6553a634bfcSVikram Hegde blacklisted_smbios(void) 6563a634bfcSVikram Hegde { 6573a634bfcSVikram Hegde id_t smid; 6583a634bfcSVikram Hegde smbios_hdl_t *smhdl; 6593a634bfcSVikram Hegde smbios_info_t sminf; 6603a634bfcSVikram Hegde smbios_system_t smsys; 6613a634bfcSVikram Hegde char *mfg, *product, *version; 6623a634bfcSVikram Hegde char **strptr; 6633a634bfcSVikram Hegde int i; 6643a634bfcSVikram Hegde 6653a634bfcSVikram Hegde /* need at least 4 strings for this setting */ 6663a634bfcSVikram Hegde if (nblacks < 4) { 6673a634bfcSVikram Hegde return (B_FALSE); 6683a634bfcSVikram Hegde } 6693a634bfcSVikram Hegde 6703a634bfcSVikram Hegde smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL); 6713a634bfcSVikram Hegde if (smhdl == NULL || 6723a634bfcSVikram Hegde (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR || 6733a634bfcSVikram Hegde smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) { 6743a634bfcSVikram Hegde return (B_FALSE); 6753a634bfcSVikram Hegde } 6763a634bfcSVikram Hegde 6773a634bfcSVikram Hegde mfg = (char *)sminf.smbi_manufacturer; 6783a634bfcSVikram Hegde product = (char *)sminf.smbi_product; 6793a634bfcSVikram Hegde version = (char *)sminf.smbi_version; 6803a634bfcSVikram Hegde 6813a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n"); 6823a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg); 6833a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product); 6843a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version); 6853a634bfcSVikram Hegde 6863a634bfcSVikram Hegde for (i = 0; nblacks - i > 3; i++) { 687e03dceedSVikram Hegde strptr = &black_array[i]; 6883a634bfcSVikram Hegde if (strcmp(*strptr++, "SMBIOS") == 0) { 6893a634bfcSVikram Hegde if (strcmp(*strptr++, mfg) == 0 && 6903a634bfcSVikram Hegde ((char *)strptr == '\0' || 6913a634bfcSVikram Hegde strcmp(*strptr++, product) == 0) && 6923a634bfcSVikram Hegde ((char *)strptr == '\0' || 6933a634bfcSVikram Hegde strcmp(*strptr++, version) == 0)) { 6943a634bfcSVikram Hegde return (B_TRUE); 6953a634bfcSVikram Hegde } 6963a634bfcSVikram Hegde i += 3; 6973a634bfcSVikram Hegde } 6983a634bfcSVikram Hegde } 6993a634bfcSVikram Hegde 7003a634bfcSVikram Hegde return (B_FALSE); 7013a634bfcSVikram Hegde } 7023a634bfcSVikram Hegde 7033a634bfcSVikram Hegde static boolean_t 7043a634bfcSVikram Hegde blacklisted_acpi(void) 7053a634bfcSVikram Hegde { 7063a634bfcSVikram Hegde if (nblacks == 0) { 7073a634bfcSVikram Hegde return (B_FALSE); 7083a634bfcSVikram Hegde } 7093a634bfcSVikram Hegde 7103a634bfcSVikram Hegde return (immu_dmar_blacklisted(black_array, nblacks)); 7113a634bfcSVikram Hegde } 7123a634bfcSVikram Hegde 7133a634bfcSVikram Hegde /* 7143a634bfcSVikram Hegde * Check if system is blacklisted by Intel IOMMU driver 7153a634bfcSVikram Hegde * i.e. should Intel IOMMU be disabled on this system 7163a634bfcSVikram Hegde * Currently a system can be blacklistd based on the 7173a634bfcSVikram Hegde * following bases: 7183a634bfcSVikram Hegde * 7193a634bfcSVikram Hegde * 1. DMAR ACPI table information. 7203a634bfcSVikram Hegde * This information includes things like 7213a634bfcSVikram Hegde * manufacturer and revision number. If rootnex.conf 7223a634bfcSVikram Hegde * has matching info set in its blacklist property 7233a634bfcSVikram Hegde * then Intel IOMMu will be disabled 7243a634bfcSVikram Hegde * 7253a634bfcSVikram Hegde * 2. SMBIOS information 7263a634bfcSVikram Hegde * 7273a634bfcSVikram Hegde * 3. Driver installed - useful if a particular 7283a634bfcSVikram Hegde * driver or hardware is toxic if Intel IOMMU 7293a634bfcSVikram Hegde * is turned on. 7303a634bfcSVikram Hegde */ 7313a634bfcSVikram Hegde 7323a634bfcSVikram Hegde static void 7333a634bfcSVikram Hegde blacklist_setup(void) 7343a634bfcSVikram Hegde { 7353a634bfcSVikram Hegde char **string_array; 7363a634bfcSVikram Hegde uint_t nstrings; 7373a634bfcSVikram Hegde 7383a634bfcSVikram Hegde /* 7393a634bfcSVikram Hegde * Check the rootnex.conf blacklist property. 7403a634bfcSVikram Hegde * Fake up a dev_t since searching the global 7413a634bfcSVikram Hegde * property list needs it 7423a634bfcSVikram Hegde */ 7433a634bfcSVikram Hegde if (ddi_prop_lookup_string_array( 7443a634bfcSVikram Hegde makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 7453a634bfcSVikram Hegde DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist", 7463a634bfcSVikram Hegde &string_array, &nstrings) != DDI_PROP_SUCCESS) { 7473a634bfcSVikram Hegde return; 7483a634bfcSVikram Hegde } 7493a634bfcSVikram Hegde 7503a634bfcSVikram Hegde /* smallest blacklist criteria works with multiples of 2 */ 7513a634bfcSVikram Hegde if (nstrings % 2 != 0) { 7523a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist " 7533a634bfcSVikram Hegde "rootnex.conf: number of strings must be a " 7543a634bfcSVikram Hegde "multiple of 2"); 7553a634bfcSVikram Hegde ddi_prop_free(string_array); 7563a634bfcSVikram Hegde return; 7573a634bfcSVikram Hegde } 7583a634bfcSVikram Hegde 7593a634bfcSVikram Hegde black_array = string_array; 7603a634bfcSVikram Hegde nblacks = nstrings; 7613a634bfcSVikram Hegde } 7623a634bfcSVikram Hegde 7633a634bfcSVikram Hegde static void 7643a634bfcSVikram Hegde blacklist_destroy(void) 7653a634bfcSVikram Hegde { 7663a634bfcSVikram Hegde if (black_array) { 7673a634bfcSVikram Hegde ddi_prop_free(black_array); 7683a634bfcSVikram Hegde black_array = NULL; 7693a634bfcSVikram Hegde nblacks = 0; 7703a634bfcSVikram Hegde } 77150200e77SFrank Van Der Linden } 7723a634bfcSVikram Hegde 77350200e77SFrank Van Der Linden static char * 77450200e77SFrank Van Der Linden immu_alloc_name(const char *str, int instance) 77550200e77SFrank Van Der Linden { 77650200e77SFrank Van Der Linden size_t slen; 77750200e77SFrank Van Der Linden char *s; 77850200e77SFrank Van Der Linden 77950200e77SFrank Van Der Linden slen = strlen(str) + IMMU_ISTRLEN + 1; 78050200e77SFrank Van Der Linden s = kmem_zalloc(slen, VM_SLEEP); 78150200e77SFrank Van Der Linden if (s != NULL) 78250200e77SFrank Van Der Linden (void) snprintf(s, slen, "%s%d", str, instance); 78350200e77SFrank Van Der Linden 78450200e77SFrank Van Der Linden return (s); 7853a634bfcSVikram Hegde } 7863a634bfcSVikram Hegde 7873a634bfcSVikram Hegde 7883a634bfcSVikram Hegde /* 7893a634bfcSVikram Hegde * Now set all the fields in the order they are defined 7903a634bfcSVikram Hegde * We do this only as a defensive-coding practice, it is 7913a634bfcSVikram Hegde * not a correctness issue. 7923a634bfcSVikram Hegde */ 7933a634bfcSVikram Hegde static void * 7943a634bfcSVikram Hegde immu_state_alloc(int seg, void *dmar_unit) 7953a634bfcSVikram Hegde { 7963a634bfcSVikram Hegde immu_t *immu; 79750200e77SFrank Van Der Linden char *nodename, *hcachename, *pcachename; 79850200e77SFrank Van Der Linden int instance; 7993a634bfcSVikram Hegde 8003a634bfcSVikram Hegde dmar_unit = immu_dmar_walk_units(seg, dmar_unit); 8013a634bfcSVikram Hegde if (dmar_unit == NULL) { 8023a634bfcSVikram Hegde /* No more IOMMUs in this segment */ 8033a634bfcSVikram Hegde return (NULL); 8043a634bfcSVikram Hegde } 8053a634bfcSVikram Hegde 8063a634bfcSVikram Hegde immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP); 8073a634bfcSVikram Hegde 8083a634bfcSVikram Hegde mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL); 8093a634bfcSVikram Hegde 8103a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 8113a634bfcSVikram Hegde 8123a634bfcSVikram Hegde immu->immu_dmar_unit = dmar_unit; 8133a634bfcSVikram Hegde immu->immu_dip = immu_dmar_unit_dip(dmar_unit); 8143a634bfcSVikram Hegde 81550200e77SFrank Van Der Linden nodename = ddi_node_name(immu->immu_dip); 81650200e77SFrank Van Der Linden instance = ddi_get_instance(immu->immu_dip); 81750200e77SFrank Van Der Linden 81850200e77SFrank Van Der Linden immu->immu_name = immu_alloc_name(nodename, instance); 81950200e77SFrank Van Der Linden if (immu->immu_name == NULL) 82050200e77SFrank Van Der Linden return (NULL); 82150200e77SFrank Van Der Linden 8223a634bfcSVikram Hegde /* 8233a634bfcSVikram Hegde * the immu_intr_lock mutex is grabbed by the IOMMU 8243a634bfcSVikram Hegde * unit's interrupt handler so we need to use an 8253a634bfcSVikram Hegde * interrupt cookie for the mutex 8263a634bfcSVikram Hegde */ 8273a634bfcSVikram Hegde mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER, 8283a634bfcSVikram Hegde (void *)ipltospl(IMMU_INTR_IPL)); 8293a634bfcSVikram Hegde 8303a634bfcSVikram Hegde /* IOMMU regs related */ 8313a634bfcSVikram Hegde mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL); 832e03dceedSVikram Hegde cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL); 833e03dceedSVikram Hegde immu->immu_regs_busy = B_FALSE; 8343a634bfcSVikram Hegde 8353a634bfcSVikram Hegde /* DVMA related */ 8363a634bfcSVikram Hegde immu->immu_dvma_coherent = B_FALSE; 8373a634bfcSVikram Hegde 8383a634bfcSVikram Hegde /* DVMA context related */ 8393a634bfcSVikram Hegde rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL); 8403a634bfcSVikram Hegde 8413a634bfcSVikram Hegde /* DVMA domain related */ 8423a634bfcSVikram Hegde list_create(&(immu->immu_domain_list), sizeof (domain_t), 8433a634bfcSVikram Hegde offsetof(domain_t, dom_immu_node)); 8443a634bfcSVikram Hegde 8453a634bfcSVikram Hegde /* DVMA special device lists */ 8463a634bfcSVikram Hegde immu->immu_dvma_gfx_only = B_FALSE; 8473a634bfcSVikram Hegde list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t), 8483a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 8493a634bfcSVikram Hegde list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t), 8503a634bfcSVikram Hegde offsetof(immu_devi_t, imd_spc_node)); 8513a634bfcSVikram Hegde 8523a634bfcSVikram Hegde /* interrupt remapping related */ 8533a634bfcSVikram Hegde mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL); 8543a634bfcSVikram Hegde 8553a634bfcSVikram Hegde /* qinv related */ 8563a634bfcSVikram Hegde mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL); 8573a634bfcSVikram Hegde 8583a634bfcSVikram Hegde /* 8593a634bfcSVikram Hegde * insert this immu unit into the system-wide list 8603a634bfcSVikram Hegde */ 8613a634bfcSVikram Hegde list_insert_tail(&immu_list, immu); 8623a634bfcSVikram Hegde 86350200e77SFrank Van Der Linden pcachename = immu_alloc_name("immu_pgtable_cache", instance); 86450200e77SFrank Van Der Linden if (pcachename == NULL) 86550200e77SFrank Van Der Linden return (NULL); 86650200e77SFrank Van Der Linden 86750200e77SFrank Van Der Linden hcachename = immu_alloc_name("immu_hdl_cache", instance); 86850200e77SFrank Van Der Linden if (hcachename == NULL) 86950200e77SFrank Van Der Linden return (NULL); 87050200e77SFrank Van Der Linden 87150200e77SFrank Van Der Linden immu->immu_pgtable_cache = kmem_cache_create(pcachename, 87250200e77SFrank Van Der Linden sizeof (pgtable_t), 0, pgtable_ctor, pgtable_dtor, NULL, immu, 87350200e77SFrank Van Der Linden NULL, 0); 87450200e77SFrank Van Der Linden immu->immu_hdl_cache = kmem_cache_create(hcachename, 87550200e77SFrank Van Der Linden sizeof (immu_hdl_priv_t), 64, immu_hdl_priv_ctor, 87650200e77SFrank Van Der Linden NULL, NULL, immu, NULL, 0); 87750200e77SFrank Van Der Linden 8783a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 8793a634bfcSVikram Hegde 88050200e77SFrank Van Der Linden ddi_err(DER_LOG, immu->immu_dip, "unit setup"); 8813a634bfcSVikram Hegde 8823a634bfcSVikram Hegde immu_dmar_set_immu(dmar_unit, immu); 8833a634bfcSVikram Hegde 8843a634bfcSVikram Hegde return (dmar_unit); 8853a634bfcSVikram Hegde } 8863a634bfcSVikram Hegde 8873a634bfcSVikram Hegde static void 8883a634bfcSVikram Hegde immu_subsystems_setup(void) 8893a634bfcSVikram Hegde { 8903a634bfcSVikram Hegde int seg; 8913a634bfcSVikram Hegde void *unit_hdl; 8923a634bfcSVikram Hegde 8933a634bfcSVikram Hegde ddi_err(DER_VERB, NULL, 89450200e77SFrank Van Der Linden "Creating state structures for Intel IOMMU units"); 8953a634bfcSVikram Hegde 8963a634bfcSVikram Hegde mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL); 8973a634bfcSVikram Hegde list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node)); 8983a634bfcSVikram Hegde 8993a634bfcSVikram Hegde mutex_enter(&immu_lock); 9003a634bfcSVikram Hegde 9013a634bfcSVikram Hegde unit_hdl = NULL; 9023a634bfcSVikram Hegde for (seg = 0; seg < IMMU_MAXSEG; seg++) { 9033a634bfcSVikram Hegde while (unit_hdl = immu_state_alloc(seg, unit_hdl)) { 9043a634bfcSVikram Hegde ; 9053a634bfcSVikram Hegde } 9063a634bfcSVikram Hegde } 9073a634bfcSVikram Hegde 9083a634bfcSVikram Hegde immu_regs_setup(&immu_list); /* subsequent code needs this first */ 9093a634bfcSVikram Hegde immu_dvma_setup(&immu_list); 910d2256d26SFrank Van Der Linden if (immu_qinv_setup(&immu_list) == DDI_SUCCESS) 9113a634bfcSVikram Hegde immu_intrmap_setup(&immu_list); 912d2256d26SFrank Van Der Linden else 913d2256d26SFrank Van Der Linden immu_intrmap_enable = B_FALSE; 9143a634bfcSVikram Hegde 9153a634bfcSVikram Hegde mutex_exit(&immu_lock); 9163a634bfcSVikram Hegde } 9173a634bfcSVikram Hegde 9183a634bfcSVikram Hegde /* 9193a634bfcSVikram Hegde * immu_subsystems_startup() 9203a634bfcSVikram Hegde * startup all units that were setup 9213a634bfcSVikram Hegde */ 9223a634bfcSVikram Hegde static void 9233a634bfcSVikram Hegde immu_subsystems_startup(void) 9243a634bfcSVikram Hegde { 9253a634bfcSVikram Hegde immu_t *immu; 92650200e77SFrank Van Der Linden iommulib_ops_t *iommulib_ops; 9273a634bfcSVikram Hegde 9283a634bfcSVikram Hegde mutex_enter(&immu_lock); 9293a634bfcSVikram Hegde 9303a634bfcSVikram Hegde immu_dmar_startup(); 9313a634bfcSVikram Hegde 9323a634bfcSVikram Hegde immu = list_head(&immu_list); 9333a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 9343a634bfcSVikram Hegde 9353a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 9363a634bfcSVikram Hegde 9373a634bfcSVikram Hegde immu_intr_register(immu); 9383a634bfcSVikram Hegde immu_dvma_startup(immu); 9393a634bfcSVikram Hegde immu_intrmap_startup(immu); 9403a634bfcSVikram Hegde immu_qinv_startup(immu); 9413a634bfcSVikram Hegde 9423a634bfcSVikram Hegde /* 9433a634bfcSVikram Hegde * Set IOMMU unit's regs to do 9443a634bfcSVikram Hegde * the actual startup. This will 9453a634bfcSVikram Hegde * set immu->immu_running field 9463a634bfcSVikram Hegde * if the unit is successfully 9473a634bfcSVikram Hegde * started 9483a634bfcSVikram Hegde */ 9493a634bfcSVikram Hegde immu_regs_startup(immu); 9503a634bfcSVikram Hegde 9513a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 95250200e77SFrank Van Der Linden 95350200e77SFrank Van Der Linden iommulib_ops = kmem_alloc(sizeof (iommulib_ops_t), KM_SLEEP); 95450200e77SFrank Van Der Linden *iommulib_ops = immulib_ops; 95550200e77SFrank Van Der Linden iommulib_ops->ilops_data = (void *)immu; 95650200e77SFrank Van Der Linden (void) iommulib_iommu_register(immu->immu_dip, iommulib_ops, 95750200e77SFrank Van Der Linden &immu->immu_iommulib_handle); 9583a634bfcSVikram Hegde } 9593a634bfcSVikram Hegde 9603a634bfcSVikram Hegde mutex_exit(&immu_lock); 9613a634bfcSVikram Hegde } 9623a634bfcSVikram Hegde 9633a634bfcSVikram Hegde /* ################## Intel IOMMU internal interfaces ###################### */ 9643a634bfcSVikram Hegde 9653a634bfcSVikram Hegde /* 9663a634bfcSVikram Hegde * Internal interfaces for IOMMU code (i.e. not exported to rootnex 9673a634bfcSVikram Hegde * or rest of system) 9683a634bfcSVikram Hegde */ 9693a634bfcSVikram Hegde 9703a634bfcSVikram Hegde /* 9713a634bfcSVikram Hegde * ddip can be NULL, in which case we walk up until we find the root dip 9723a634bfcSVikram Hegde * NOTE: We never visit the root dip since its not a hardware node 9733a634bfcSVikram Hegde */ 9743a634bfcSVikram Hegde int 9753a634bfcSVikram Hegde immu_walk_ancestor( 9763a634bfcSVikram Hegde dev_info_t *rdip, 9773a634bfcSVikram Hegde dev_info_t *ddip, 9783a634bfcSVikram Hegde int (*func)(dev_info_t *, void *arg), 9793a634bfcSVikram Hegde void *arg, 9803a634bfcSVikram Hegde int *lvlp, 9813a634bfcSVikram Hegde immu_flags_t immu_flags) 9823a634bfcSVikram Hegde { 9833a634bfcSVikram Hegde dev_info_t *pdip; 9843a634bfcSVikram Hegde int level; 9853a634bfcSVikram Hegde int error = DDI_SUCCESS; 9863a634bfcSVikram Hegde 9873a634bfcSVikram Hegde /* ddip and immu can be NULL */ 9883a634bfcSVikram Hegde 9893a634bfcSVikram Hegde /* Hold rdip so that branch is not detached */ 9903a634bfcSVikram Hegde ndi_hold_devi(rdip); 9913a634bfcSVikram Hegde for (pdip = rdip, level = 1; pdip && pdip != root_devinfo; 9923a634bfcSVikram Hegde pdip = ddi_get_parent(pdip), level++) { 9933a634bfcSVikram Hegde 9943a634bfcSVikram Hegde if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) { 9953a634bfcSVikram Hegde error = DDI_FAILURE; 9963a634bfcSVikram Hegde break; 9973a634bfcSVikram Hegde } 9983a634bfcSVikram Hegde if (func(pdip, arg) == DDI_WALK_TERMINATE) { 9993a634bfcSVikram Hegde break; 10003a634bfcSVikram Hegde } 10013a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_DONTPASS) { 10023a634bfcSVikram Hegde break; 10033a634bfcSVikram Hegde } 10043a634bfcSVikram Hegde if (pdip == ddip) { 10053a634bfcSVikram Hegde break; 10063a634bfcSVikram Hegde } 10073a634bfcSVikram Hegde } 10083a634bfcSVikram Hegde 10093a634bfcSVikram Hegde ndi_rele_devi(rdip); 10103a634bfcSVikram Hegde 10113a634bfcSVikram Hegde if (lvlp) 10123a634bfcSVikram Hegde *lvlp = level; 10133a634bfcSVikram Hegde 10143a634bfcSVikram Hegde return (error); 10153a634bfcSVikram Hegde } 10163a634bfcSVikram Hegde 10173a634bfcSVikram Hegde /* ######################## Intel IOMMU entry points ####################### */ 10183a634bfcSVikram Hegde /* 10193a634bfcSVikram Hegde * immu_init() 10203a634bfcSVikram Hegde * called from rootnex_attach(). setup but don't startup the Intel IOMMU 10213a634bfcSVikram Hegde * This is the first function called in Intel IOMMU code 10223a634bfcSVikram Hegde */ 10233a634bfcSVikram Hegde void 10243a634bfcSVikram Hegde immu_init(void) 10253a634bfcSVikram Hegde { 10263a634bfcSVikram Hegde char *phony_reg = "A thing of beauty is a joy forever"; 10273a634bfcSVikram Hegde 10283a634bfcSVikram Hegde /* Set some global shorthands that are needed by all of IOMMU code */ 10293a634bfcSVikram Hegde root_devinfo = ddi_root_node(); 10303a634bfcSVikram Hegde 10313a634bfcSVikram Hegde /* 10323a634bfcSVikram Hegde * Intel IOMMU only supported only if MMU(CPU) page size is == 10333a634bfcSVikram Hegde * IOMMU pages size. 10343a634bfcSVikram Hegde */ 10353a634bfcSVikram Hegde /*LINTED*/ 10363a634bfcSVikram Hegde if (MMU_PAGESIZE != IMMU_PAGESIZE) { 10373a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, 10383a634bfcSVikram Hegde "MMU page size (%d) is not equal to\n" 10393a634bfcSVikram Hegde "IOMMU page size (%d). " 10403a634bfcSVikram Hegde "Disabling Intel IOMMU. ", 10413a634bfcSVikram Hegde MMU_PAGESIZE, IMMU_PAGESIZE); 10423a634bfcSVikram Hegde immu_enable = B_FALSE; 10433a634bfcSVikram Hegde return; 10443a634bfcSVikram Hegde } 10453a634bfcSVikram Hegde 10463a634bfcSVikram Hegde /* 10473adb2334SVikram Hegde * Read rootnex.conf options. Do this before 10483adb2334SVikram Hegde * boot options so boot options can override .conf options. 10493adb2334SVikram Hegde */ 10503adb2334SVikram Hegde read_conf_options(); 10513adb2334SVikram Hegde 10523adb2334SVikram Hegde /* 10533a634bfcSVikram Hegde * retrieve the Intel IOMMU boot options. 10543a634bfcSVikram Hegde * Do this before parsing immu ACPI table 10553a634bfcSVikram Hegde * as a boot option could potentially affect 10563a634bfcSVikram Hegde * ACPI parsing. 10573a634bfcSVikram Hegde */ 10583a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n"); 10593a634bfcSVikram Hegde read_boot_options(); 10603a634bfcSVikram Hegde 10613a634bfcSVikram Hegde /* 10623a634bfcSVikram Hegde * Check the IOMMU enable boot-option first. 10633a634bfcSVikram Hegde * This is so that we can skip parsing the ACPI table 10643a634bfcSVikram Hegde * if necessary because that may cause problems in 10653a634bfcSVikram Hegde * systems with buggy BIOS or ACPI tables 10663a634bfcSVikram Hegde */ 10673a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 10683a634bfcSVikram Hegde return; 10693a634bfcSVikram Hegde } 10703a634bfcSVikram Hegde 1071c94adbf9SFrank Van Der Linden if (immu_intrmap_enable == B_TRUE) 1072c94adbf9SFrank Van Der Linden immu_qinv_enable = B_TRUE; 1073c94adbf9SFrank Van Der Linden 10743a634bfcSVikram Hegde /* 10753a634bfcSVikram Hegde * Next, check if the system even has an Intel IOMMU 10763a634bfcSVikram Hegde * We use the presence or absence of the IOMMU ACPI 10773a634bfcSVikram Hegde * table to detect Intel IOMMU. 10783a634bfcSVikram Hegde */ 10793a634bfcSVikram Hegde if (immu_dmar_setup() != DDI_SUCCESS) { 10803a634bfcSVikram Hegde immu_enable = B_FALSE; 10813a634bfcSVikram Hegde return; 10823a634bfcSVikram Hegde } 10833a634bfcSVikram Hegde 10849e986f0eSFrank Van Der Linden mapping_list_setup(); 10859e986f0eSFrank Van Der Linden 10863a634bfcSVikram Hegde /* 10873a634bfcSVikram Hegde * Check blacklists 10883a634bfcSVikram Hegde */ 10893a634bfcSVikram Hegde blacklist_setup(); 10903a634bfcSVikram Hegde 10913a634bfcSVikram Hegde if (blacklisted_smbios() == B_TRUE) { 10923a634bfcSVikram Hegde blacklist_destroy(); 10933a634bfcSVikram Hegde immu_enable = B_FALSE; 10943a634bfcSVikram Hegde return; 10953a634bfcSVikram Hegde } 10963a634bfcSVikram Hegde 10973a634bfcSVikram Hegde if (blacklisted_driver() == B_TRUE) { 10983a634bfcSVikram Hegde blacklist_destroy(); 10993a634bfcSVikram Hegde immu_enable = B_FALSE; 11003a634bfcSVikram Hegde return; 11013a634bfcSVikram Hegde } 11023a634bfcSVikram Hegde 11033a634bfcSVikram Hegde /* 11043a634bfcSVikram Hegde * Read the "raw" DMAR ACPI table to get information 11053a634bfcSVikram Hegde * and convert into a form we can use. 11063a634bfcSVikram Hegde */ 11073a634bfcSVikram Hegde if (immu_dmar_parse() != DDI_SUCCESS) { 11083a634bfcSVikram Hegde blacklist_destroy(); 11093a634bfcSVikram Hegde immu_enable = B_FALSE; 11103a634bfcSVikram Hegde return; 11113a634bfcSVikram Hegde } 11123a634bfcSVikram Hegde 11133a634bfcSVikram Hegde /* 11143a634bfcSVikram Hegde * now that we have processed the ACPI table 11153a634bfcSVikram Hegde * check if we need to blacklist this system 11163a634bfcSVikram Hegde * based on ACPI info 11173a634bfcSVikram Hegde */ 11183a634bfcSVikram Hegde if (blacklisted_acpi() == B_TRUE) { 11193a634bfcSVikram Hegde immu_dmar_destroy(); 11203a634bfcSVikram Hegde blacklist_destroy(); 11213a634bfcSVikram Hegde immu_enable = B_FALSE; 11223a634bfcSVikram Hegde return; 11233a634bfcSVikram Hegde } 11243a634bfcSVikram Hegde 11253a634bfcSVikram Hegde blacklist_destroy(); 11263a634bfcSVikram Hegde 11273a634bfcSVikram Hegde /* 11283a634bfcSVikram Hegde * Check if system has HW quirks. 11293a634bfcSVikram Hegde */ 11303a634bfcSVikram Hegde pre_setup_quirks(); 11313a634bfcSVikram Hegde 11323a634bfcSVikram Hegde /* Now do the rest of the setup */ 11333a634bfcSVikram Hegde immu_subsystems_setup(); 11343a634bfcSVikram Hegde 11353a634bfcSVikram Hegde /* 11363a634bfcSVikram Hegde * Now that the IMMU is setup, create a phony 11373a634bfcSVikram Hegde * reg prop so that suspend/resume works 11383a634bfcSVikram Hegde */ 11393a634bfcSVikram Hegde if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg", 11403a634bfcSVikram Hegde (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) { 11413a634bfcSVikram Hegde ddi_err(DER_PANIC, NULL, "Failed to create reg prop for " 11423a634bfcSVikram Hegde "rootnex node"); 11433a634bfcSVikram Hegde /*NOTREACHED*/ 11443a634bfcSVikram Hegde } 11453a634bfcSVikram Hegde 11463a634bfcSVikram Hegde immu_setup = B_TRUE; 11473a634bfcSVikram Hegde } 11483a634bfcSVikram Hegde 11493a634bfcSVikram Hegde /* 11503a634bfcSVikram Hegde * immu_startup() 11513a634bfcSVikram Hegde * called directly by boot code to startup 11523a634bfcSVikram Hegde * all units of the IOMMU 11533a634bfcSVikram Hegde */ 11543a634bfcSVikram Hegde void 11553a634bfcSVikram Hegde immu_startup(void) 11563a634bfcSVikram Hegde { 11573a634bfcSVikram Hegde /* 11583a634bfcSVikram Hegde * If IOMMU is disabled, do nothing 11593a634bfcSVikram Hegde */ 11603a634bfcSVikram Hegde if (immu_enable == B_FALSE) { 11613a634bfcSVikram Hegde return; 11623a634bfcSVikram Hegde } 11633a634bfcSVikram Hegde 11643a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 11653a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, " 116650200e77SFrank Van Der Linden "skipping IOMMU startup"); 11673a634bfcSVikram Hegde return; 11683a634bfcSVikram Hegde } 11693a634bfcSVikram Hegde 11703a634bfcSVikram Hegde pre_startup_quirks(); 11713a634bfcSVikram Hegde 11723a634bfcSVikram Hegde ddi_err(DER_CONT, NULL, 11733a634bfcSVikram Hegde "?Starting Intel IOMMU (dmar) units...\n"); 11743a634bfcSVikram Hegde 11753a634bfcSVikram Hegde immu_subsystems_startup(); 11763a634bfcSVikram Hegde 11773a634bfcSVikram Hegde immu_running = B_TRUE; 11783a634bfcSVikram Hegde } 11793a634bfcSVikram Hegde 11803a634bfcSVikram Hegde /* 11813a634bfcSVikram Hegde * Hook to notify IOMMU code of device tree changes 11823a634bfcSVikram Hegde */ 11833a634bfcSVikram Hegde void 11843a634bfcSVikram Hegde immu_device_tree_changed(void) 11853a634bfcSVikram Hegde { 11863a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 11873a634bfcSVikram Hegde return; 11883a634bfcSVikram Hegde } 11893a634bfcSVikram Hegde 11903a634bfcSVikram Hegde ddi_err(DER_WARN, NULL, "Intel IOMMU currently " 11913a634bfcSVikram Hegde "does not use device tree updates"); 11923a634bfcSVikram Hegde } 11933a634bfcSVikram Hegde 11943a634bfcSVikram Hegde /* 11953a634bfcSVikram Hegde * Hook to notify IOMMU code of memory changes 11963a634bfcSVikram Hegde */ 11973a634bfcSVikram Hegde void 11983a634bfcSVikram Hegde immu_physmem_update(uint64_t addr, uint64_t size) 11993a634bfcSVikram Hegde { 12003a634bfcSVikram Hegde if (immu_setup == B_FALSE) { 12013a634bfcSVikram Hegde return; 12023a634bfcSVikram Hegde } 12033a634bfcSVikram Hegde immu_dvma_physmem_update(addr, size); 12043a634bfcSVikram Hegde } 12053a634bfcSVikram Hegde 12063a634bfcSVikram Hegde /* 12073a634bfcSVikram Hegde * immu_quiesce() 12083a634bfcSVikram Hegde * quiesce all units that are running 12093a634bfcSVikram Hegde */ 12103a634bfcSVikram Hegde int 12113a634bfcSVikram Hegde immu_quiesce(void) 12123a634bfcSVikram Hegde { 12133a634bfcSVikram Hegde immu_t *immu; 12143a634bfcSVikram Hegde int ret = DDI_SUCCESS; 12153a634bfcSVikram Hegde 12163a634bfcSVikram Hegde mutex_enter(&immu_lock); 12173a634bfcSVikram Hegde 1218*96992ee7SEthindra Ramamurthy if (immu_running == B_FALSE) { 1219*96992ee7SEthindra Ramamurthy mutex_exit(&immu_lock); 12203a634bfcSVikram Hegde return (DDI_SUCCESS); 1221*96992ee7SEthindra Ramamurthy } 12223a634bfcSVikram Hegde 12233a634bfcSVikram Hegde immu = list_head(&immu_list); 12243a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 12253a634bfcSVikram Hegde 12263a634bfcSVikram Hegde /* if immu is not running, we dont quiesce */ 12273a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) 12283a634bfcSVikram Hegde continue; 12293a634bfcSVikram Hegde 12303a634bfcSVikram Hegde /* flush caches */ 12313a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 123250200e77SFrank Van Der Linden immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait); 123350200e77SFrank Van Der Linden immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait); 12343a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 12353a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 12363a634bfcSVikram Hegde 12373a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 12383a634bfcSVikram Hegde 12393a634bfcSVikram Hegde /* 12403a634bfcSVikram Hegde * Set IOMMU unit's regs to do 12413a634bfcSVikram Hegde * the actual shutdown. 12423a634bfcSVikram Hegde */ 12433a634bfcSVikram Hegde immu_regs_shutdown(immu); 12443a634bfcSVikram Hegde immu_regs_suspend(immu); 12453a634bfcSVikram Hegde 12463a634bfcSVikram Hegde /* if immu is still running, we failed */ 12473a634bfcSVikram Hegde if (immu->immu_regs_running == B_TRUE) 12483a634bfcSVikram Hegde ret = DDI_FAILURE; 12493a634bfcSVikram Hegde else 12503a634bfcSVikram Hegde immu->immu_regs_quiesced = B_TRUE; 12513a634bfcSVikram Hegde 12523a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 12533a634bfcSVikram Hegde } 12543a634bfcSVikram Hegde 12553a634bfcSVikram Hegde if (ret == DDI_SUCCESS) { 12563a634bfcSVikram Hegde immu_running = B_FALSE; 12573a634bfcSVikram Hegde immu_quiesced = B_TRUE; 12583a634bfcSVikram Hegde } 1259*96992ee7SEthindra Ramamurthy mutex_exit(&immu_lock); 12603a634bfcSVikram Hegde 12613a634bfcSVikram Hegde return (ret); 12623a634bfcSVikram Hegde } 12633a634bfcSVikram Hegde 12643a634bfcSVikram Hegde /* 12653a634bfcSVikram Hegde * immu_unquiesce() 12663a634bfcSVikram Hegde * unquiesce all units 12673a634bfcSVikram Hegde */ 12683a634bfcSVikram Hegde int 12693a634bfcSVikram Hegde immu_unquiesce(void) 12703a634bfcSVikram Hegde { 12713a634bfcSVikram Hegde immu_t *immu; 12723a634bfcSVikram Hegde int ret = DDI_SUCCESS; 12733a634bfcSVikram Hegde 12743a634bfcSVikram Hegde mutex_enter(&immu_lock); 12753a634bfcSVikram Hegde 1276*96992ee7SEthindra Ramamurthy if (immu_quiesced == B_FALSE) { 1277*96992ee7SEthindra Ramamurthy mutex_exit(&immu_lock); 12783a634bfcSVikram Hegde return (DDI_SUCCESS); 1279*96992ee7SEthindra Ramamurthy } 12803a634bfcSVikram Hegde 12813a634bfcSVikram Hegde immu = list_head(&immu_list); 12823a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 12833a634bfcSVikram Hegde 12843a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 12853a634bfcSVikram Hegde 12863a634bfcSVikram Hegde /* if immu was not quiesced, i.e was not running before */ 1287e03dceedSVikram Hegde if (immu->immu_regs_quiesced == B_FALSE) { 1288e03dceedSVikram Hegde mutex_exit(&(immu->immu_lock)); 12893a634bfcSVikram Hegde continue; 1290e03dceedSVikram Hegde } 12913a634bfcSVikram Hegde 12923a634bfcSVikram Hegde if (immu_regs_resume(immu) != DDI_SUCCESS) { 12933a634bfcSVikram Hegde ret = DDI_FAILURE; 1294e03dceedSVikram Hegde mutex_exit(&(immu->immu_lock)); 12953a634bfcSVikram Hegde continue; 12963a634bfcSVikram Hegde } 12973a634bfcSVikram Hegde 12983a634bfcSVikram Hegde /* flush caches before unquiesce */ 12993a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 130050200e77SFrank Van Der Linden immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait); 130150200e77SFrank Van Der Linden immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait); 13023a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 13033a634bfcSVikram Hegde 13043a634bfcSVikram Hegde /* 13053a634bfcSVikram Hegde * Set IOMMU unit's regs to do 13063a634bfcSVikram Hegde * the actual startup. This will 13073a634bfcSVikram Hegde * set immu->immu_regs_running field 13083a634bfcSVikram Hegde * if the unit is successfully 13093a634bfcSVikram Hegde * started 13103a634bfcSVikram Hegde */ 13113a634bfcSVikram Hegde immu_regs_startup(immu); 13123a634bfcSVikram Hegde 13133a634bfcSVikram Hegde if (immu->immu_regs_running == B_FALSE) { 13143a634bfcSVikram Hegde ret = DDI_FAILURE; 13153a634bfcSVikram Hegde } else { 13163a634bfcSVikram Hegde immu_quiesced = B_TRUE; 13173a634bfcSVikram Hegde immu_running = B_TRUE; 13183a634bfcSVikram Hegde immu->immu_regs_quiesced = B_FALSE; 13193a634bfcSVikram Hegde } 13203a634bfcSVikram Hegde 13213a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 13223a634bfcSVikram Hegde } 13233a634bfcSVikram Hegde 13243a634bfcSVikram Hegde mutex_exit(&immu_lock); 13253a634bfcSVikram Hegde 13263a634bfcSVikram Hegde return (ret); 13273a634bfcSVikram Hegde } 13283a634bfcSVikram Hegde 132950200e77SFrank Van Der Linden void 133050200e77SFrank Van Der Linden immu_init_inv_wait(immu_inv_wait_t *iwp, const char *name, boolean_t sync) 133150200e77SFrank Van Der Linden { 133250200e77SFrank Van Der Linden caddr_t vaddr; 133350200e77SFrank Van Der Linden uint64_t paddr; 133450200e77SFrank Van Der Linden 133550200e77SFrank Van Der Linden iwp->iwp_sync = sync; 133650200e77SFrank Van Der Linden 133750200e77SFrank Van Der Linden vaddr = (caddr_t)&iwp->iwp_vstatus; 133850200e77SFrank Van Der Linden paddr = pfn_to_pa(hat_getpfnum(kas.a_hat, vaddr)); 133950200e77SFrank Van Der Linden paddr += ((uintptr_t)vaddr) & MMU_PAGEOFFSET; 134050200e77SFrank Van Der Linden 134150200e77SFrank Van Der Linden iwp->iwp_pstatus = paddr; 134250200e77SFrank Van Der Linden iwp->iwp_name = name; 134350200e77SFrank Van Der Linden } 134450200e77SFrank Van Der Linden 13453a634bfcSVikram Hegde /* ############## END Intel IOMMU entry points ################## */ 1346