111600SVikram.Hegde@Sun.COM /* 211600SVikram.Hegde@Sun.COM * CDDL HEADER START 311600SVikram.Hegde@Sun.COM * 411600SVikram.Hegde@Sun.COM * The contents of this file are subject to the terms of the 511600SVikram.Hegde@Sun.COM * Common Development and Distribution License (the "License"). 611600SVikram.Hegde@Sun.COM * You may not use this file except in compliance with the License. 711600SVikram.Hegde@Sun.COM * 811600SVikram.Hegde@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 911600SVikram.Hegde@Sun.COM * or http://www.opensolaris.org/os/licensing. 1011600SVikram.Hegde@Sun.COM * See the License for the specific language governing permissions 1111600SVikram.Hegde@Sun.COM * and limitations under the License. 1211600SVikram.Hegde@Sun.COM * 1311600SVikram.Hegde@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 1411600SVikram.Hegde@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1511600SVikram.Hegde@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 1611600SVikram.Hegde@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 1711600SVikram.Hegde@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 1811600SVikram.Hegde@Sun.COM * 1911600SVikram.Hegde@Sun.COM * CDDL HEADER END 2011600SVikram.Hegde@Sun.COM */ 2111600SVikram.Hegde@Sun.COM /* 2211600SVikram.Hegde@Sun.COM * Portions Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2311600SVikram.Hegde@Sun.COM * Use is subject to license terms. 2411600SVikram.Hegde@Sun.COM */ 2511600SVikram.Hegde@Sun.COM /* 2611600SVikram.Hegde@Sun.COM * Copyright (c) 2009, Intel Corporation. 2711600SVikram.Hegde@Sun.COM * All rights reserved. 2811600SVikram.Hegde@Sun.COM */ 2911600SVikram.Hegde@Sun.COM 3011600SVikram.Hegde@Sun.COM /* 3111600SVikram.Hegde@Sun.COM * Intel IOMMU implementation 3211600SVikram.Hegde@Sun.COM * This file contains Intel IOMMU code exported 3311600SVikram.Hegde@Sun.COM * to the rest of the system and code that deals 3411600SVikram.Hegde@Sun.COM * with the Intel IOMMU as a whole. 3511600SVikram.Hegde@Sun.COM */ 3611600SVikram.Hegde@Sun.COM 3711600SVikram.Hegde@Sun.COM #include <sys/conf.h> 3811600SVikram.Hegde@Sun.COM #include <sys/modctl.h> 3911600SVikram.Hegde@Sun.COM #include <sys/pci.h> 4011600SVikram.Hegde@Sun.COM #include <sys/pci_impl.h> 4111600SVikram.Hegde@Sun.COM #include <sys/sysmacros.h> 4211600SVikram.Hegde@Sun.COM #include <sys/ddi.h> 4311600SVikram.Hegde@Sun.COM #include <sys/ddidmareq.h> 4411600SVikram.Hegde@Sun.COM #include <sys/ddi_impldefs.h> 4511600SVikram.Hegde@Sun.COM #include <sys/ddifm.h> 4611600SVikram.Hegde@Sun.COM #include <sys/sunndi.h> 4711600SVikram.Hegde@Sun.COM #include <sys/debug.h> 4811600SVikram.Hegde@Sun.COM #include <sys/fm/protocol.h> 4911600SVikram.Hegde@Sun.COM #include <sys/note.h> 5011600SVikram.Hegde@Sun.COM #include <sys/apic.h> 5111600SVikram.Hegde@Sun.COM #include <vm/hat_i86.h> 5211600SVikram.Hegde@Sun.COM #include <sys/smp_impldefs.h> 5311600SVikram.Hegde@Sun.COM #include <sys/spl.h> 5411600SVikram.Hegde@Sun.COM #include <sys/archsystm.h> 5511600SVikram.Hegde@Sun.COM #include <sys/x86_archext.h> 5611600SVikram.Hegde@Sun.COM #include <sys/rootnex.h> 5711600SVikram.Hegde@Sun.COM #include <sys/avl.h> 5811600SVikram.Hegde@Sun.COM #include <sys/bootconf.h> 5911600SVikram.Hegde@Sun.COM #include <sys/bootinfo.h> 6011600SVikram.Hegde@Sun.COM #include <sys/atomic.h> 6111600SVikram.Hegde@Sun.COM #include <sys/immu.h> 6211600SVikram.Hegde@Sun.COM /* ########################### Globals and tunables ######################## */ 6311600SVikram.Hegde@Sun.COM /* 6411600SVikram.Hegde@Sun.COM * Global switches (boolean) that can be toggled either via boot options 6511600SVikram.Hegde@Sun.COM * or via /etc/system or kmdb 6611600SVikram.Hegde@Sun.COM */ 6711600SVikram.Hegde@Sun.COM 6811600SVikram.Hegde@Sun.COM /* Various features */ 69*11749SVikram.Hegde@Sun.COM boolean_t immu_enable = B_TRUE; 7011600SVikram.Hegde@Sun.COM boolean_t immu_dvma_enable = B_TRUE; 7111600SVikram.Hegde@Sun.COM 7211600SVikram.Hegde@Sun.COM /* accessed in other files so not static */ 7311600SVikram.Hegde@Sun.COM boolean_t immu_gfxdvma_enable = B_TRUE; 7411600SVikram.Hegde@Sun.COM boolean_t immu_intrmap_enable = B_FALSE; 7511600SVikram.Hegde@Sun.COM boolean_t immu_qinv_enable = B_FALSE; 7611600SVikram.Hegde@Sun.COM 7711600SVikram.Hegde@Sun.COM /* various quirks that need working around */ 7811600SVikram.Hegde@Sun.COM 7911600SVikram.Hegde@Sun.COM /* XXX We always map page 0 read/write for now */ 8011600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbpage0 = B_TRUE; 8111600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbrmrr = B_TRUE; 8211600SVikram.Hegde@Sun.COM boolean_t immu_quirk_usbfullpa; 8311600SVikram.Hegde@Sun.COM boolean_t immu_quirk_mobile4; 8411600SVikram.Hegde@Sun.COM 8511600SVikram.Hegde@Sun.COM /* debug messages */ 8611600SVikram.Hegde@Sun.COM boolean_t immu_dmar_print; 8711600SVikram.Hegde@Sun.COM 8811658SVikram.Hegde@Sun.COM /* Tunables */ 8911658SVikram.Hegde@Sun.COM int64_t immu_flush_gran = 5; 9011658SVikram.Hegde@Sun.COM 9111600SVikram.Hegde@Sun.COM /* ############ END OPTIONS section ################ */ 9211600SVikram.Hegde@Sun.COM 9311600SVikram.Hegde@Sun.COM /* 9411600SVikram.Hegde@Sun.COM * Global used internally by Intel IOMMU code 9511600SVikram.Hegde@Sun.COM */ 9611600SVikram.Hegde@Sun.COM dev_info_t *root_devinfo; 9711600SVikram.Hegde@Sun.COM kmutex_t immu_lock; 9811600SVikram.Hegde@Sun.COM list_t immu_list; 9911658SVikram.Hegde@Sun.COM void *immu_pgtable_cache; 10011600SVikram.Hegde@Sun.COM boolean_t immu_setup; 10111600SVikram.Hegde@Sun.COM boolean_t immu_running; 10211600SVikram.Hegde@Sun.COM boolean_t immu_quiesced; 10311600SVikram.Hegde@Sun.COM 10411600SVikram.Hegde@Sun.COM /* ######################## END Globals and tunables ###################### */ 10511600SVikram.Hegde@Sun.COM /* Globals used only in this file */ 10611600SVikram.Hegde@Sun.COM static char **black_array; 10711600SVikram.Hegde@Sun.COM static uint_t nblacks; 10811600SVikram.Hegde@Sun.COM /* ###################### Utility routines ############################# */ 10911600SVikram.Hegde@Sun.COM 11011600SVikram.Hegde@Sun.COM /* 11111600SVikram.Hegde@Sun.COM * Check if the device has mobile 4 chipset 11211600SVikram.Hegde@Sun.COM */ 11311600SVikram.Hegde@Sun.COM static int 11411600SVikram.Hegde@Sun.COM check_mobile4(dev_info_t *dip, void *arg) 11511600SVikram.Hegde@Sun.COM { 11611600SVikram.Hegde@Sun.COM _NOTE(ARGUNUSED(arg)); 11711600SVikram.Hegde@Sun.COM int vendor, device; 11811600SVikram.Hegde@Sun.COM int *ip = (int *)arg; 11911600SVikram.Hegde@Sun.COM 12011600SVikram.Hegde@Sun.COM ASSERT(arg); 12111600SVikram.Hegde@Sun.COM 12211600SVikram.Hegde@Sun.COM vendor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 12311600SVikram.Hegde@Sun.COM "vendor-id", -1); 12411600SVikram.Hegde@Sun.COM device = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 12511600SVikram.Hegde@Sun.COM "device-id", -1); 12611600SVikram.Hegde@Sun.COM 12711600SVikram.Hegde@Sun.COM if (vendor == 0x8086 && device == 0x2a40) { 12811600SVikram.Hegde@Sun.COM *ip = B_TRUE; 12911600SVikram.Hegde@Sun.COM ddi_err(DER_NOTE, dip, "IMMU: Mobile 4 chipset detected. " 13011600SVikram.Hegde@Sun.COM "Force setting IOMMU write buffer"); 13111600SVikram.Hegde@Sun.COM return (DDI_WALK_TERMINATE); 13211600SVikram.Hegde@Sun.COM } else { 13311600SVikram.Hegde@Sun.COM return (DDI_WALK_CONTINUE); 13411600SVikram.Hegde@Sun.COM } 13511600SVikram.Hegde@Sun.COM } 13611600SVikram.Hegde@Sun.COM 13711600SVikram.Hegde@Sun.COM static void 13811600SVikram.Hegde@Sun.COM map_bios_rsvd_mem(dev_info_t *dip) 13911600SVikram.Hegde@Sun.COM { 14011600SVikram.Hegde@Sun.COM struct memlist *mp; 14111600SVikram.Hegde@Sun.COM int e; 14211600SVikram.Hegde@Sun.COM 14311600SVikram.Hegde@Sun.COM memlist_read_lock(); 14411600SVikram.Hegde@Sun.COM 14511600SVikram.Hegde@Sun.COM mp = bios_rsvd; 14611600SVikram.Hegde@Sun.COM while (mp != NULL) { 14711658SVikram.Hegde@Sun.COM memrng_t mrng = {0}; 14811600SVikram.Hegde@Sun.COM 14911600SVikram.Hegde@Sun.COM ddi_err(DER_LOG, dip, "IMMU: Mapping BIOS rsvd range " 15011600SVikram.Hegde@Sun.COM "[0x%" PRIx64 " - 0x%"PRIx64 "]\n", mp->ml_address, 15111600SVikram.Hegde@Sun.COM mp->ml_address + mp->ml_size); 15211600SVikram.Hegde@Sun.COM 15311658SVikram.Hegde@Sun.COM mrng.mrng_start = IMMU_ROUNDOWN(mp->ml_address); 15411658SVikram.Hegde@Sun.COM mrng.mrng_npages = IMMU_ROUNDUP(mp->ml_size) / IMMU_PAGESIZE; 15511600SVikram.Hegde@Sun.COM 15611658SVikram.Hegde@Sun.COM e = immu_dvma_map(NULL, NULL, &mrng, 0, dip, IMMU_FLAGS_MEMRNG); 15711600SVikram.Hegde@Sun.COM ASSERT(e == DDI_DMA_MAPPED || e == DDI_DMA_USE_PHYSICAL); 15811600SVikram.Hegde@Sun.COM 15911600SVikram.Hegde@Sun.COM mp = mp->ml_next; 16011600SVikram.Hegde@Sun.COM } 16111600SVikram.Hegde@Sun.COM 16211600SVikram.Hegde@Sun.COM memlist_read_unlock(); 16311600SVikram.Hegde@Sun.COM } 16411600SVikram.Hegde@Sun.COM 16511658SVikram.Hegde@Sun.COM 16611658SVikram.Hegde@Sun.COM /* 16711658SVikram.Hegde@Sun.COM * Check if the driver requests physical mapping 16811658SVikram.Hegde@Sun.COM */ 16911658SVikram.Hegde@Sun.COM /*ARGSUSED*/ 17011658SVikram.Hegde@Sun.COM static void 17111658SVikram.Hegde@Sun.COM check_physical(dev_info_t *dip, void *arg) 17211658SVikram.Hegde@Sun.COM { 17311658SVikram.Hegde@Sun.COM char *val; 17411658SVikram.Hegde@Sun.COM 17511658SVikram.Hegde@Sun.COM /* 17611658SVikram.Hegde@Sun.COM * Check for the DVMA unity mapping property on the device 17711658SVikram.Hegde@Sun.COM */ 17811658SVikram.Hegde@Sun.COM val = NULL; 17911658SVikram.Hegde@Sun.COM if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 18011658SVikram.Hegde@Sun.COM DDI_PROP_DONTPASS, DDI_DVMA_MAPTYPE_PROP, &val) == DDI_SUCCESS) { 18111658SVikram.Hegde@Sun.COM ASSERT(val); 18211658SVikram.Hegde@Sun.COM if (strcmp(val, DDI_DVMA_MAPTYPE_UNITY) != 0) { 18311658SVikram.Hegde@Sun.COM ddi_err(DER_WARN, dip, "%s value \"%s\" is not valid", 18411658SVikram.Hegde@Sun.COM DDI_DVMA_MAPTYPE_PROP, val); 18511658SVikram.Hegde@Sun.COM } else { 18611658SVikram.Hegde@Sun.COM int e; 18711658SVikram.Hegde@Sun.COM 18811658SVikram.Hegde@Sun.COM ddi_err(DER_NOTE, dip, 18911658SVikram.Hegde@Sun.COM "Using unity DVMA mapping for device"); 19011658SVikram.Hegde@Sun.COM e = immu_dvma_map(NULL, NULL, NULL, 0, dip, 19111658SVikram.Hegde@Sun.COM IMMU_FLAGS_UNITY); 19211658SVikram.Hegde@Sun.COM /* for unity mode, map will return USE_PHYSICAL */ 19311658SVikram.Hegde@Sun.COM ASSERT(e == DDI_DMA_USE_PHYSICAL); 19411658SVikram.Hegde@Sun.COM } 19511658SVikram.Hegde@Sun.COM ddi_prop_free(val); 19611658SVikram.Hegde@Sun.COM } 19711658SVikram.Hegde@Sun.COM } 19811658SVikram.Hegde@Sun.COM 19911600SVikram.Hegde@Sun.COM /* 20011600SVikram.Hegde@Sun.COM * Check if the device is USB controller 20111600SVikram.Hegde@Sun.COM */ 20211600SVikram.Hegde@Sun.COM /*ARGSUSED*/ 20311600SVikram.Hegde@Sun.COM static void 20411600SVikram.Hegde@Sun.COM check_usb(dev_info_t *dip, void *arg) 20511600SVikram.Hegde@Sun.COM { 20611600SVikram.Hegde@Sun.COM const char *drv = ddi_driver_name(dip); 20711600SVikram.Hegde@Sun.COM 20811600SVikram.Hegde@Sun.COM if (drv == NULL || 20911600SVikram.Hegde@Sun.COM (strcmp(drv, "uhci") != 0 && strcmp(drv, "ohci") != 0 && 21011600SVikram.Hegde@Sun.COM strcmp(drv, "ehci") != 0)) { 21111600SVikram.Hegde@Sun.COM return; 21211600SVikram.Hegde@Sun.COM } 21311600SVikram.Hegde@Sun.COM 21411600SVikram.Hegde@Sun.COM /* This must come first since it does unity mapping */ 21511600SVikram.Hegde@Sun.COM if (immu_quirk_usbfullpa == B_TRUE) { 21611600SVikram.Hegde@Sun.COM int e; 21711600SVikram.Hegde@Sun.COM ddi_err(DER_NOTE, dip, "Applying USB FULL PA quirk"); 21811600SVikram.Hegde@Sun.COM e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY); 21911600SVikram.Hegde@Sun.COM /* for unity mode, map will return USE_PHYSICAL */ 22011600SVikram.Hegde@Sun.COM ASSERT(e == DDI_DMA_USE_PHYSICAL); 22111600SVikram.Hegde@Sun.COM } 22211600SVikram.Hegde@Sun.COM 22311600SVikram.Hegde@Sun.COM if (immu_quirk_usbrmrr == B_TRUE) { 22411600SVikram.Hegde@Sun.COM ddi_err(DER_LOG, dip, "Applying USB RMRR quirk"); 22511600SVikram.Hegde@Sun.COM map_bios_rsvd_mem(dip); 22611600SVikram.Hegde@Sun.COM } 22711600SVikram.Hegde@Sun.COM } 22811600SVikram.Hegde@Sun.COM 22911600SVikram.Hegde@Sun.COM /* 23011600SVikram.Hegde@Sun.COM * Check if the device is a LPC device 23111600SVikram.Hegde@Sun.COM */ 23211600SVikram.Hegde@Sun.COM /*ARGSUSED*/ 23311600SVikram.Hegde@Sun.COM static void 23411600SVikram.Hegde@Sun.COM check_lpc(dev_info_t *dip, void *arg) 23511600SVikram.Hegde@Sun.COM { 23611600SVikram.Hegde@Sun.COM immu_devi_t *immu_devi; 23711600SVikram.Hegde@Sun.COM 23811600SVikram.Hegde@Sun.COM immu_devi = immu_devi_get(dip); 23911600SVikram.Hegde@Sun.COM ASSERT(immu_devi); 24011600SVikram.Hegde@Sun.COM if (immu_devi->imd_lpc == B_TRUE) { 24111600SVikram.Hegde@Sun.COM ddi_err(DER_LOG, dip, "IMMU: Found LPC device"); 24211600SVikram.Hegde@Sun.COM /* This will put the immu_devi on the LPC "specials" list */ 24311600SVikram.Hegde@Sun.COM (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 24411600SVikram.Hegde@Sun.COM } 24511600SVikram.Hegde@Sun.COM } 24611600SVikram.Hegde@Sun.COM 24711600SVikram.Hegde@Sun.COM /* 24811600SVikram.Hegde@Sun.COM * Check if the device is a GFX device 24911600SVikram.Hegde@Sun.COM */ 25011600SVikram.Hegde@Sun.COM /*ARGSUSED*/ 25111600SVikram.Hegde@Sun.COM static void 25211600SVikram.Hegde@Sun.COM check_gfx(dev_info_t *dip, void *arg) 25311600SVikram.Hegde@Sun.COM { 25411600SVikram.Hegde@Sun.COM immu_devi_t *immu_devi; 25511600SVikram.Hegde@Sun.COM int e; 25611600SVikram.Hegde@Sun.COM 25711600SVikram.Hegde@Sun.COM immu_devi = immu_devi_get(dip); 25811600SVikram.Hegde@Sun.COM ASSERT(immu_devi); 25911600SVikram.Hegde@Sun.COM if (immu_devi->imd_display == B_TRUE) { 26011600SVikram.Hegde@Sun.COM ddi_err(DER_LOG, dip, "IMMU: Found GFX device"); 26111600SVikram.Hegde@Sun.COM /* This will put the immu_devi on the GFX "specials" list */ 26211600SVikram.Hegde@Sun.COM (void) immu_dvma_get_immu(dip, IMMU_FLAGS_SLEEP); 26311600SVikram.Hegde@Sun.COM e = immu_dvma_map(NULL, NULL, NULL, 0, dip, IMMU_FLAGS_UNITY); 26411600SVikram.Hegde@Sun.COM /* for unity mode, map will return USE_PHYSICAL */ 26511600SVikram.Hegde@Sun.COM ASSERT(e == DDI_DMA_USE_PHYSICAL); 26611600SVikram.Hegde@Sun.COM } 26711600SVikram.Hegde@Sun.COM } 26811600SVikram.Hegde@Sun.COM 26911600SVikram.Hegde@Sun.COM static void 27011600SVikram.Hegde@Sun.COM walk_tree(int (*f)(dev_info_t *, void *), void *arg) 27111600SVikram.Hegde@Sun.COM { 27211600SVikram.Hegde@Sun.COM int count; 27311600SVikram.Hegde@Sun.COM 27411600SVikram.Hegde@Sun.COM ndi_devi_enter(root_devinfo, &count); 27511600SVikram.Hegde@Sun.COM ddi_walk_devs(ddi_get_child(root_devinfo), f, arg); 27611600SVikram.Hegde@Sun.COM ndi_devi_exit(root_devinfo, count); 27711600SVikram.Hegde@Sun.COM } 27811600SVikram.Hegde@Sun.COM 27911600SVikram.Hegde@Sun.COM static int 28011600SVikram.Hegde@Sun.COM check_pre_setup_quirks(dev_info_t *dip, void *arg) 28111600SVikram.Hegde@Sun.COM { 28211600SVikram.Hegde@Sun.COM /* just 1 check right now */ 28311600SVikram.Hegde@Sun.COM return (check_mobile4(dip, arg)); 28411600SVikram.Hegde@Sun.COM } 28511600SVikram.Hegde@Sun.COM 28611600SVikram.Hegde@Sun.COM static int 28711600SVikram.Hegde@Sun.COM check_pre_startup_quirks(dev_info_t *dip, void *arg) 28811600SVikram.Hegde@Sun.COM { 28911600SVikram.Hegde@Sun.COM if (immu_devi_set(dip, IMMU_FLAGS_SLEEP) != DDI_SUCCESS) { 29011600SVikram.Hegde@Sun.COM ddi_err(DER_PANIC, dip, "Failed to get immu_devi"); 29111600SVikram.Hegde@Sun.COM } 29211600SVikram.Hegde@Sun.COM 29311600SVikram.Hegde@Sun.COM check_gfx(dip, arg); 29411600SVikram.Hegde@Sun.COM 29511600SVikram.Hegde@Sun.COM check_lpc(dip, arg); 29611600SVikram.Hegde@Sun.COM 29711600SVikram.Hegde@Sun.COM check_usb(dip, arg); 29811600SVikram.Hegde@Sun.COM 29911658SVikram.Hegde@Sun.COM check_physical(dip, arg); 30011658SVikram.Hegde@Sun.COM 30111600SVikram.Hegde@Sun.COM return (DDI_WALK_CONTINUE); 30211600SVikram.Hegde@Sun.COM } 30311600SVikram.Hegde@Sun.COM 30411600SVikram.Hegde@Sun.COM static void 30511600SVikram.Hegde@Sun.COM pre_setup_quirks(void) 30611600SVikram.Hegde@Sun.COM { 30711600SVikram.Hegde@Sun.COM walk_tree(check_pre_setup_quirks, &immu_quirk_mobile4); 30811600SVikram.Hegde@Sun.COM } 30911600SVikram.Hegde@Sun.COM 31011600SVikram.Hegde@Sun.COM static void 31111600SVikram.Hegde@Sun.COM pre_startup_quirks(void) 31211600SVikram.Hegde@Sun.COM { 31311600SVikram.Hegde@Sun.COM walk_tree(check_pre_startup_quirks, NULL); 31411600SVikram.Hegde@Sun.COM 31511600SVikram.Hegde@Sun.COM immu_dmar_rmrr_map(); 31611600SVikram.Hegde@Sun.COM } 31711600SVikram.Hegde@Sun.COM 31811600SVikram.Hegde@Sun.COM /* 319*11749SVikram.Hegde@Sun.COM * get_conf_opt() 320*11749SVikram.Hegde@Sun.COM * get a rootnex.conf setting (always a boolean) 321*11749SVikram.Hegde@Sun.COM */ 322*11749SVikram.Hegde@Sun.COM static void 323*11749SVikram.Hegde@Sun.COM get_conf_opt(char *bopt, boolean_t *kvar) 324*11749SVikram.Hegde@Sun.COM { 325*11749SVikram.Hegde@Sun.COM char *val = NULL; 326*11749SVikram.Hegde@Sun.COM 327*11749SVikram.Hegde@Sun.COM ASSERT(bopt); 328*11749SVikram.Hegde@Sun.COM ASSERT(kvar); 329*11749SVikram.Hegde@Sun.COM 330*11749SVikram.Hegde@Sun.COM /* 331*11749SVikram.Hegde@Sun.COM * Check the rootnex.conf property 332*11749SVikram.Hegde@Sun.COM * Fake up a dev_t since searching the global 333*11749SVikram.Hegde@Sun.COM * property list needs it 334*11749SVikram.Hegde@Sun.COM */ 335*11749SVikram.Hegde@Sun.COM if (ddi_prop_lookup_string(makedevice(ddi_name_to_major("rootnex"), 0), 336*11749SVikram.Hegde@Sun.COM root_devinfo, DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, 337*11749SVikram.Hegde@Sun.COM bopt, &val) != DDI_PROP_SUCCESS) { 338*11749SVikram.Hegde@Sun.COM return; 339*11749SVikram.Hegde@Sun.COM } 340*11749SVikram.Hegde@Sun.COM 341*11749SVikram.Hegde@Sun.COM ASSERT(val); 342*11749SVikram.Hegde@Sun.COM if (strcmp(val, "true") == 0) { 343*11749SVikram.Hegde@Sun.COM *kvar = B_TRUE; 344*11749SVikram.Hegde@Sun.COM } else if (strcmp(val, "false") == 0) { 345*11749SVikram.Hegde@Sun.COM *kvar = B_FALSE; 346*11749SVikram.Hegde@Sun.COM } else { 347*11749SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "rootnex.conf switch %s=\"%s\" ", 348*11749SVikram.Hegde@Sun.COM "is not set to true or false. Ignoring option.", 349*11749SVikram.Hegde@Sun.COM bopt, val); 350*11749SVikram.Hegde@Sun.COM } 351*11749SVikram.Hegde@Sun.COM ddi_prop_free(val); 352*11749SVikram.Hegde@Sun.COM } 353*11749SVikram.Hegde@Sun.COM 354*11749SVikram.Hegde@Sun.COM /* 35511600SVikram.Hegde@Sun.COM * get_bootopt() 35611600SVikram.Hegde@Sun.COM * check a boot option (always a boolean) 35711600SVikram.Hegde@Sun.COM */ 35811600SVikram.Hegde@Sun.COM static void 35911600SVikram.Hegde@Sun.COM get_bootopt(char *bopt, boolean_t *kvar) 36011600SVikram.Hegde@Sun.COM { 36111600SVikram.Hegde@Sun.COM char *val = NULL; 36211600SVikram.Hegde@Sun.COM 36311600SVikram.Hegde@Sun.COM ASSERT(bopt); 36411600SVikram.Hegde@Sun.COM ASSERT(kvar); 36511600SVikram.Hegde@Sun.COM 36611600SVikram.Hegde@Sun.COM /* 36711600SVikram.Hegde@Sun.COM * All boot options set at the GRUB menu become 36811600SVikram.Hegde@Sun.COM * properties on the rootnex. 36911600SVikram.Hegde@Sun.COM */ 37011600SVikram.Hegde@Sun.COM if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root_devinfo, 37111600SVikram.Hegde@Sun.COM DDI_PROP_DONTPASS, bopt, &val) == DDI_SUCCESS) { 37211600SVikram.Hegde@Sun.COM ASSERT(val); 37311600SVikram.Hegde@Sun.COM if (strcmp(val, "true") == 0) { 37411600SVikram.Hegde@Sun.COM *kvar = B_TRUE; 37511600SVikram.Hegde@Sun.COM } else if (strcmp(val, "false") == 0) { 37611600SVikram.Hegde@Sun.COM *kvar = B_FALSE; 37711600SVikram.Hegde@Sun.COM } else { 37811600SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "boot option %s=\"%s\" ", 37911600SVikram.Hegde@Sun.COM "is not set to true or false. Ignoring option.", 38011600SVikram.Hegde@Sun.COM bopt, val); 38111600SVikram.Hegde@Sun.COM } 38211600SVikram.Hegde@Sun.COM ddi_prop_free(val); 38311600SVikram.Hegde@Sun.COM } 38411600SVikram.Hegde@Sun.COM } 38511600SVikram.Hegde@Sun.COM 38611600SVikram.Hegde@Sun.COM static void 387*11749SVikram.Hegde@Sun.COM get_conf_tunables(char *bopt, int64_t *ivar) 38811658SVikram.Hegde@Sun.COM { 38911658SVikram.Hegde@Sun.COM int64_t *iarray; 39011658SVikram.Hegde@Sun.COM uint_t n; 39111658SVikram.Hegde@Sun.COM 39211658SVikram.Hegde@Sun.COM /* 39311658SVikram.Hegde@Sun.COM * Check the rootnex.conf property 39411658SVikram.Hegde@Sun.COM * Fake up a dev_t since searching the global 39511658SVikram.Hegde@Sun.COM * property list needs it 39611658SVikram.Hegde@Sun.COM */ 39711658SVikram.Hegde@Sun.COM if (ddi_prop_lookup_int64_array( 39811658SVikram.Hegde@Sun.COM makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 39911658SVikram.Hegde@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, bopt, 40011658SVikram.Hegde@Sun.COM &iarray, &n) != DDI_PROP_SUCCESS) { 40111658SVikram.Hegde@Sun.COM return; 40211658SVikram.Hegde@Sun.COM } 40311658SVikram.Hegde@Sun.COM 40411658SVikram.Hegde@Sun.COM if (n != 1) { 40511658SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "More than one value specified for " 40611658SVikram.Hegde@Sun.COM "%s property. Ignoring and using default", 40711658SVikram.Hegde@Sun.COM "immu-flush-gran"); 40811658SVikram.Hegde@Sun.COM ddi_prop_free(iarray); 40911658SVikram.Hegde@Sun.COM return; 41011658SVikram.Hegde@Sun.COM } 41111658SVikram.Hegde@Sun.COM 41211658SVikram.Hegde@Sun.COM if (iarray[0] < 0) { 41311658SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "Negative value specified for " 41411658SVikram.Hegde@Sun.COM "%s property. Inoring and Using default value", 41511658SVikram.Hegde@Sun.COM "immu-flush-gran"); 41611658SVikram.Hegde@Sun.COM ddi_prop_free(iarray); 41711658SVikram.Hegde@Sun.COM return; 41811658SVikram.Hegde@Sun.COM } 41911658SVikram.Hegde@Sun.COM 42011658SVikram.Hegde@Sun.COM *ivar = iarray[0]; 42111658SVikram.Hegde@Sun.COM 42211658SVikram.Hegde@Sun.COM ddi_prop_free(iarray); 42311658SVikram.Hegde@Sun.COM } 42411658SVikram.Hegde@Sun.COM 42511658SVikram.Hegde@Sun.COM static void 426*11749SVikram.Hegde@Sun.COM read_conf_options(void) 427*11749SVikram.Hegde@Sun.COM { 428*11749SVikram.Hegde@Sun.COM /* enable/disable options */ 429*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-enable", &immu_enable); 430*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-dvma-enable", &immu_dvma_enable); 431*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 432*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-intrmap-enable", &immu_intrmap_enable); 433*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-qinv-enable", &immu_qinv_enable); 434*11749SVikram.Hegde@Sun.COM 435*11749SVikram.Hegde@Sun.COM /* workaround switches */ 436*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 437*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 438*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 439*11749SVikram.Hegde@Sun.COM 440*11749SVikram.Hegde@Sun.COM /* debug printing */ 441*11749SVikram.Hegde@Sun.COM get_conf_opt("immu-dmar-print", &immu_dmar_print); 442*11749SVikram.Hegde@Sun.COM 443*11749SVikram.Hegde@Sun.COM /* get tunables */ 444*11749SVikram.Hegde@Sun.COM get_conf_tunables("immu-flush-gran", &immu_flush_gran); 445*11749SVikram.Hegde@Sun.COM } 446*11749SVikram.Hegde@Sun.COM 447*11749SVikram.Hegde@Sun.COM static void 44811600SVikram.Hegde@Sun.COM read_boot_options(void) 44911600SVikram.Hegde@Sun.COM { 45011600SVikram.Hegde@Sun.COM /* enable/disable options */ 45111600SVikram.Hegde@Sun.COM get_bootopt("immu-enable", &immu_enable); 45211600SVikram.Hegde@Sun.COM get_bootopt("immu-dvma-enable", &immu_dvma_enable); 45311600SVikram.Hegde@Sun.COM get_bootopt("immu-gfxdvma-enable", &immu_gfxdvma_enable); 45411600SVikram.Hegde@Sun.COM get_bootopt("immu-intrmap-enable", &immu_intrmap_enable); 45511600SVikram.Hegde@Sun.COM get_bootopt("immu-qinv-enable", &immu_qinv_enable); 45611600SVikram.Hegde@Sun.COM 45711600SVikram.Hegde@Sun.COM /* workaround switches */ 45811600SVikram.Hegde@Sun.COM get_bootopt("immu-quirk-usbpage0", &immu_quirk_usbpage0); 45911600SVikram.Hegde@Sun.COM get_bootopt("immu-quirk-usbfullpa", &immu_quirk_usbfullpa); 46011600SVikram.Hegde@Sun.COM get_bootopt("immu-quirk-usbrmrr", &immu_quirk_usbrmrr); 46111600SVikram.Hegde@Sun.COM 46211600SVikram.Hegde@Sun.COM /* debug printing */ 46311600SVikram.Hegde@Sun.COM get_bootopt("immu-dmar-print", &immu_dmar_print); 46411600SVikram.Hegde@Sun.COM } 46511600SVikram.Hegde@Sun.COM 46611600SVikram.Hegde@Sun.COM /* 46711600SVikram.Hegde@Sun.COM * Note, this will not catch hardware not enumerated 46811600SVikram.Hegde@Sun.COM * in early boot 46911600SVikram.Hegde@Sun.COM */ 47011600SVikram.Hegde@Sun.COM static boolean_t 47111600SVikram.Hegde@Sun.COM blacklisted_driver(void) 47211600SVikram.Hegde@Sun.COM { 47311600SVikram.Hegde@Sun.COM char **strptr; 47411600SVikram.Hegde@Sun.COM int i; 47511600SVikram.Hegde@Sun.COM major_t maj; 47611600SVikram.Hegde@Sun.COM 47711600SVikram.Hegde@Sun.COM ASSERT((black_array == NULL) ^ (nblacks != 0)); 47811600SVikram.Hegde@Sun.COM 47911600SVikram.Hegde@Sun.COM /* need at least 2 strings */ 48011600SVikram.Hegde@Sun.COM if (nblacks < 2) { 48111600SVikram.Hegde@Sun.COM return (B_FALSE); 48211600SVikram.Hegde@Sun.COM } 48311600SVikram.Hegde@Sun.COM 48411600SVikram.Hegde@Sun.COM for (i = 0; nblacks - i > 1; i++) { 48511658SVikram.Hegde@Sun.COM strptr = &black_array[i]; 48611600SVikram.Hegde@Sun.COM if (strcmp(*strptr++, "DRIVER") == 0) { 48711600SVikram.Hegde@Sun.COM if ((maj = ddi_name_to_major(*strptr++)) 48811600SVikram.Hegde@Sun.COM != DDI_MAJOR_T_NONE) { 48911600SVikram.Hegde@Sun.COM /* is there hardware bound to this drvr */ 49011600SVikram.Hegde@Sun.COM if (devnamesp[maj].dn_head != NULL) { 49111600SVikram.Hegde@Sun.COM return (B_TRUE); 49211600SVikram.Hegde@Sun.COM } 49311600SVikram.Hegde@Sun.COM } 49411600SVikram.Hegde@Sun.COM i += 1; /* for loop adds 1, so add only 1 here */ 49511600SVikram.Hegde@Sun.COM } 49611600SVikram.Hegde@Sun.COM } 49711600SVikram.Hegde@Sun.COM 49811600SVikram.Hegde@Sun.COM return (B_FALSE); 49911600SVikram.Hegde@Sun.COM } 50011600SVikram.Hegde@Sun.COM 50111600SVikram.Hegde@Sun.COM static boolean_t 50211600SVikram.Hegde@Sun.COM blacklisted_smbios(void) 50311600SVikram.Hegde@Sun.COM { 50411600SVikram.Hegde@Sun.COM id_t smid; 50511600SVikram.Hegde@Sun.COM smbios_hdl_t *smhdl; 50611600SVikram.Hegde@Sun.COM smbios_info_t sminf; 50711600SVikram.Hegde@Sun.COM smbios_system_t smsys; 50811600SVikram.Hegde@Sun.COM char *mfg, *product, *version; 50911600SVikram.Hegde@Sun.COM char **strptr; 51011600SVikram.Hegde@Sun.COM int i; 51111600SVikram.Hegde@Sun.COM 51211600SVikram.Hegde@Sun.COM ASSERT((black_array == NULL) ^ (nblacks != 0)); 51311600SVikram.Hegde@Sun.COM 51411600SVikram.Hegde@Sun.COM /* need at least 4 strings for this setting */ 51511600SVikram.Hegde@Sun.COM if (nblacks < 4) { 51611600SVikram.Hegde@Sun.COM return (B_FALSE); 51711600SVikram.Hegde@Sun.COM } 51811600SVikram.Hegde@Sun.COM 51911600SVikram.Hegde@Sun.COM smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL); 52011600SVikram.Hegde@Sun.COM if (smhdl == NULL || 52111600SVikram.Hegde@Sun.COM (smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR || 52211600SVikram.Hegde@Sun.COM smbios_info_common(smhdl, smid, &sminf) == SMB_ERR) { 52311600SVikram.Hegde@Sun.COM return (B_FALSE); 52411600SVikram.Hegde@Sun.COM } 52511600SVikram.Hegde@Sun.COM 52611600SVikram.Hegde@Sun.COM mfg = (char *)sminf.smbi_manufacturer; 52711600SVikram.Hegde@Sun.COM product = (char *)sminf.smbi_product; 52811600SVikram.Hegde@Sun.COM version = (char *)sminf.smbi_version; 52911600SVikram.Hegde@Sun.COM 53011600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, "?System SMBIOS information:\n"); 53111600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, "?Manufacturer = <%s>\n", mfg); 53211600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, "?Product = <%s>\n", product); 53311600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, "?Version = <%s>\n", version); 53411600SVikram.Hegde@Sun.COM 53511600SVikram.Hegde@Sun.COM for (i = 0; nblacks - i > 3; i++) { 53611658SVikram.Hegde@Sun.COM strptr = &black_array[i]; 53711600SVikram.Hegde@Sun.COM if (strcmp(*strptr++, "SMBIOS") == 0) { 53811600SVikram.Hegde@Sun.COM if (strcmp(*strptr++, mfg) == 0 && 53911600SVikram.Hegde@Sun.COM ((char *)strptr == '\0' || 54011600SVikram.Hegde@Sun.COM strcmp(*strptr++, product) == 0) && 54111600SVikram.Hegde@Sun.COM ((char *)strptr == '\0' || 54211600SVikram.Hegde@Sun.COM strcmp(*strptr++, version) == 0)) { 54311600SVikram.Hegde@Sun.COM return (B_TRUE); 54411600SVikram.Hegde@Sun.COM } 54511600SVikram.Hegde@Sun.COM i += 3; 54611600SVikram.Hegde@Sun.COM } 54711600SVikram.Hegde@Sun.COM } 54811600SVikram.Hegde@Sun.COM 54911600SVikram.Hegde@Sun.COM return (B_FALSE); 55011600SVikram.Hegde@Sun.COM } 55111600SVikram.Hegde@Sun.COM 55211600SVikram.Hegde@Sun.COM static boolean_t 55311600SVikram.Hegde@Sun.COM blacklisted_acpi(void) 55411600SVikram.Hegde@Sun.COM { 55511600SVikram.Hegde@Sun.COM ASSERT((black_array == NULL) ^ (nblacks != 0)); 55611600SVikram.Hegde@Sun.COM if (nblacks == 0) { 55711600SVikram.Hegde@Sun.COM return (B_FALSE); 55811600SVikram.Hegde@Sun.COM } 55911600SVikram.Hegde@Sun.COM 56011600SVikram.Hegde@Sun.COM return (immu_dmar_blacklisted(black_array, nblacks)); 56111600SVikram.Hegde@Sun.COM } 56211600SVikram.Hegde@Sun.COM 56311600SVikram.Hegde@Sun.COM /* 56411600SVikram.Hegde@Sun.COM * Check if system is blacklisted by Intel IOMMU driver 56511600SVikram.Hegde@Sun.COM * i.e. should Intel IOMMU be disabled on this system 56611600SVikram.Hegde@Sun.COM * Currently a system can be blacklistd based on the 56711600SVikram.Hegde@Sun.COM * following bases: 56811600SVikram.Hegde@Sun.COM * 56911600SVikram.Hegde@Sun.COM * 1. DMAR ACPI table information. 57011600SVikram.Hegde@Sun.COM * This information includes things like 57111600SVikram.Hegde@Sun.COM * manufacturer and revision number. If rootnex.conf 57211600SVikram.Hegde@Sun.COM * has matching info set in its blacklist property 57311600SVikram.Hegde@Sun.COM * then Intel IOMMu will be disabled 57411600SVikram.Hegde@Sun.COM * 57511600SVikram.Hegde@Sun.COM * 2. SMBIOS information 57611600SVikram.Hegde@Sun.COM * 57711600SVikram.Hegde@Sun.COM * 3. Driver installed - useful if a particular 57811600SVikram.Hegde@Sun.COM * driver or hardware is toxic if Intel IOMMU 57911600SVikram.Hegde@Sun.COM * is turned on. 58011600SVikram.Hegde@Sun.COM */ 58111600SVikram.Hegde@Sun.COM 58211600SVikram.Hegde@Sun.COM static void 58311600SVikram.Hegde@Sun.COM blacklist_setup(void) 58411600SVikram.Hegde@Sun.COM { 58511600SVikram.Hegde@Sun.COM char **string_array; 58611600SVikram.Hegde@Sun.COM uint_t nstrings; 58711600SVikram.Hegde@Sun.COM 58811600SVikram.Hegde@Sun.COM /* 58911600SVikram.Hegde@Sun.COM * Check the rootnex.conf blacklist property. 59011600SVikram.Hegde@Sun.COM * Fake up a dev_t since searching the global 59111600SVikram.Hegde@Sun.COM * property list needs it 59211600SVikram.Hegde@Sun.COM */ 59311600SVikram.Hegde@Sun.COM if (ddi_prop_lookup_string_array( 59411600SVikram.Hegde@Sun.COM makedevice(ddi_name_to_major("rootnex"), 0), root_devinfo, 59511600SVikram.Hegde@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_ROOTNEX_GLOBAL, "immu-blacklist", 59611600SVikram.Hegde@Sun.COM &string_array, &nstrings) != DDI_PROP_SUCCESS) { 59711600SVikram.Hegde@Sun.COM return; 59811600SVikram.Hegde@Sun.COM } 59911600SVikram.Hegde@Sun.COM 60011600SVikram.Hegde@Sun.COM /* smallest blacklist criteria works with multiples of 2 */ 60111600SVikram.Hegde@Sun.COM if (nstrings % 2 != 0) { 60211600SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "Invalid IOMMU blacklist " 60311600SVikram.Hegde@Sun.COM "rootnex.conf: number of strings must be a " 60411600SVikram.Hegde@Sun.COM "multiple of 2"); 60511600SVikram.Hegde@Sun.COM ddi_prop_free(string_array); 60611600SVikram.Hegde@Sun.COM return; 60711600SVikram.Hegde@Sun.COM } 60811600SVikram.Hegde@Sun.COM 60911600SVikram.Hegde@Sun.COM black_array = string_array; 61011600SVikram.Hegde@Sun.COM nblacks = nstrings; 61111600SVikram.Hegde@Sun.COM } 61211600SVikram.Hegde@Sun.COM 61311600SVikram.Hegde@Sun.COM static void 61411600SVikram.Hegde@Sun.COM blacklist_destroy(void) 61511600SVikram.Hegde@Sun.COM { 61611600SVikram.Hegde@Sun.COM if (black_array) { 61711600SVikram.Hegde@Sun.COM ddi_prop_free(black_array); 61811600SVikram.Hegde@Sun.COM black_array = NULL; 61911600SVikram.Hegde@Sun.COM nblacks = 0; 62011600SVikram.Hegde@Sun.COM } 62111600SVikram.Hegde@Sun.COM 62211600SVikram.Hegde@Sun.COM ASSERT(black_array == NULL); 62311600SVikram.Hegde@Sun.COM ASSERT(nblacks == 0); 62411600SVikram.Hegde@Sun.COM } 62511600SVikram.Hegde@Sun.COM 62611600SVikram.Hegde@Sun.COM 62711600SVikram.Hegde@Sun.COM /* 62811600SVikram.Hegde@Sun.COM * Now set all the fields in the order they are defined 62911600SVikram.Hegde@Sun.COM * We do this only as a defensive-coding practice, it is 63011600SVikram.Hegde@Sun.COM * not a correctness issue. 63111600SVikram.Hegde@Sun.COM */ 63211600SVikram.Hegde@Sun.COM static void * 63311600SVikram.Hegde@Sun.COM immu_state_alloc(int seg, void *dmar_unit) 63411600SVikram.Hegde@Sun.COM { 63511600SVikram.Hegde@Sun.COM immu_t *immu; 63611600SVikram.Hegde@Sun.COM 63711600SVikram.Hegde@Sun.COM dmar_unit = immu_dmar_walk_units(seg, dmar_unit); 63811600SVikram.Hegde@Sun.COM if (dmar_unit == NULL) { 63911600SVikram.Hegde@Sun.COM /* No more IOMMUs in this segment */ 64011600SVikram.Hegde@Sun.COM return (NULL); 64111600SVikram.Hegde@Sun.COM } 64211600SVikram.Hegde@Sun.COM 64311600SVikram.Hegde@Sun.COM immu = kmem_zalloc(sizeof (immu_t), KM_SLEEP); 64411600SVikram.Hegde@Sun.COM 64511600SVikram.Hegde@Sun.COM mutex_init(&(immu->immu_lock), NULL, MUTEX_DRIVER, NULL); 64611600SVikram.Hegde@Sun.COM 64711600SVikram.Hegde@Sun.COM mutex_enter(&(immu->immu_lock)); 64811600SVikram.Hegde@Sun.COM 64911600SVikram.Hegde@Sun.COM immu->immu_dmar_unit = dmar_unit; 65011600SVikram.Hegde@Sun.COM immu->immu_name = ddi_strdup(immu_dmar_unit_name(dmar_unit), 65111600SVikram.Hegde@Sun.COM KM_SLEEP); 65211600SVikram.Hegde@Sun.COM immu->immu_dip = immu_dmar_unit_dip(dmar_unit); 65311600SVikram.Hegde@Sun.COM 65411600SVikram.Hegde@Sun.COM /* 65511600SVikram.Hegde@Sun.COM * the immu_intr_lock mutex is grabbed by the IOMMU 65611600SVikram.Hegde@Sun.COM * unit's interrupt handler so we need to use an 65711600SVikram.Hegde@Sun.COM * interrupt cookie for the mutex 65811600SVikram.Hegde@Sun.COM */ 65911600SVikram.Hegde@Sun.COM mutex_init(&(immu->immu_intr_lock), NULL, MUTEX_DRIVER, 66011600SVikram.Hegde@Sun.COM (void *)ipltospl(IMMU_INTR_IPL)); 66111600SVikram.Hegde@Sun.COM 66211600SVikram.Hegde@Sun.COM /* IOMMU regs related */ 66311600SVikram.Hegde@Sun.COM mutex_init(&(immu->immu_regs_lock), NULL, MUTEX_DEFAULT, NULL); 66411658SVikram.Hegde@Sun.COM cv_init(&(immu->immu_regs_cv), NULL, CV_DEFAULT, NULL); 66511658SVikram.Hegde@Sun.COM immu->immu_regs_busy = B_FALSE; 66611600SVikram.Hegde@Sun.COM 66711600SVikram.Hegde@Sun.COM /* DVMA related */ 66811600SVikram.Hegde@Sun.COM immu->immu_dvma_coherent = B_FALSE; 66911600SVikram.Hegde@Sun.COM 67011600SVikram.Hegde@Sun.COM /* DVMA context related */ 67111600SVikram.Hegde@Sun.COM rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL); 67211600SVikram.Hegde@Sun.COM 67311600SVikram.Hegde@Sun.COM /* DVMA domain related */ 67411600SVikram.Hegde@Sun.COM list_create(&(immu->immu_domain_list), sizeof (domain_t), 67511600SVikram.Hegde@Sun.COM offsetof(domain_t, dom_immu_node)); 67611600SVikram.Hegde@Sun.COM 67711600SVikram.Hegde@Sun.COM /* DVMA special device lists */ 67811600SVikram.Hegde@Sun.COM immu->immu_dvma_gfx_only = B_FALSE; 67911600SVikram.Hegde@Sun.COM list_create(&(immu->immu_dvma_lpc_list), sizeof (immu_devi_t), 68011600SVikram.Hegde@Sun.COM offsetof(immu_devi_t, imd_spc_node)); 68111600SVikram.Hegde@Sun.COM list_create(&(immu->immu_dvma_gfx_list), sizeof (immu_devi_t), 68211600SVikram.Hegde@Sun.COM offsetof(immu_devi_t, imd_spc_node)); 68311600SVikram.Hegde@Sun.COM 68411600SVikram.Hegde@Sun.COM /* interrupt remapping related */ 68511600SVikram.Hegde@Sun.COM mutex_init(&(immu->immu_intrmap_lock), NULL, MUTEX_DEFAULT, NULL); 68611600SVikram.Hegde@Sun.COM 68711600SVikram.Hegde@Sun.COM /* qinv related */ 68811600SVikram.Hegde@Sun.COM mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DEFAULT, NULL); 68911600SVikram.Hegde@Sun.COM 69011600SVikram.Hegde@Sun.COM /* 69111600SVikram.Hegde@Sun.COM * insert this immu unit into the system-wide list 69211600SVikram.Hegde@Sun.COM */ 69311600SVikram.Hegde@Sun.COM list_insert_tail(&immu_list, immu); 69411600SVikram.Hegde@Sun.COM 69511600SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 69611600SVikram.Hegde@Sun.COM 69711600SVikram.Hegde@Sun.COM ddi_err(DER_LOG, immu->immu_dip, "IMMU: unit setup"); 69811600SVikram.Hegde@Sun.COM 69911600SVikram.Hegde@Sun.COM immu_dmar_set_immu(dmar_unit, immu); 70011600SVikram.Hegde@Sun.COM 70111600SVikram.Hegde@Sun.COM return (dmar_unit); 70211600SVikram.Hegde@Sun.COM } 70311600SVikram.Hegde@Sun.COM 70411600SVikram.Hegde@Sun.COM static void 70511600SVikram.Hegde@Sun.COM immu_subsystems_setup(void) 70611600SVikram.Hegde@Sun.COM { 70711600SVikram.Hegde@Sun.COM int seg; 70811600SVikram.Hegde@Sun.COM void *unit_hdl; 70911600SVikram.Hegde@Sun.COM 71011600SVikram.Hegde@Sun.COM ddi_err(DER_VERB, NULL, 71111600SVikram.Hegde@Sun.COM "Creating state structures for Intel IOMMU units\n"); 71211600SVikram.Hegde@Sun.COM 71311600SVikram.Hegde@Sun.COM ASSERT(immu_setup == B_FALSE); 71411600SVikram.Hegde@Sun.COM ASSERT(immu_running == B_FALSE); 71511600SVikram.Hegde@Sun.COM 71611600SVikram.Hegde@Sun.COM mutex_init(&immu_lock, NULL, MUTEX_DEFAULT, NULL); 71711600SVikram.Hegde@Sun.COM list_create(&immu_list, sizeof (immu_t), offsetof(immu_t, immu_node)); 71811600SVikram.Hegde@Sun.COM 71911600SVikram.Hegde@Sun.COM mutex_enter(&immu_lock); 72011600SVikram.Hegde@Sun.COM 72111658SVikram.Hegde@Sun.COM ASSERT(immu_pgtable_cache == NULL); 72211658SVikram.Hegde@Sun.COM 72311658SVikram.Hegde@Sun.COM immu_pgtable_cache = kmem_cache_create("immu_pgtable_cache", 72411658SVikram.Hegde@Sun.COM sizeof (pgtable_t), 0, 72511658SVikram.Hegde@Sun.COM pgtable_ctor, pgtable_dtor, NULL, NULL, NULL, 0); 72611658SVikram.Hegde@Sun.COM 72711600SVikram.Hegde@Sun.COM unit_hdl = NULL; 72811600SVikram.Hegde@Sun.COM for (seg = 0; seg < IMMU_MAXSEG; seg++) { 72911600SVikram.Hegde@Sun.COM while (unit_hdl = immu_state_alloc(seg, unit_hdl)) { 73011600SVikram.Hegde@Sun.COM ; 73111600SVikram.Hegde@Sun.COM } 73211600SVikram.Hegde@Sun.COM } 73311600SVikram.Hegde@Sun.COM 73411600SVikram.Hegde@Sun.COM immu_regs_setup(&immu_list); /* subsequent code needs this first */ 73511600SVikram.Hegde@Sun.COM immu_dvma_setup(&immu_list); 73611600SVikram.Hegde@Sun.COM immu_intrmap_setup(&immu_list); 73711600SVikram.Hegde@Sun.COM immu_qinv_setup(&immu_list); 73811600SVikram.Hegde@Sun.COM 73911600SVikram.Hegde@Sun.COM mutex_exit(&immu_lock); 74011600SVikram.Hegde@Sun.COM } 74111600SVikram.Hegde@Sun.COM 74211600SVikram.Hegde@Sun.COM /* 74311600SVikram.Hegde@Sun.COM * immu_subsystems_startup() 74411600SVikram.Hegde@Sun.COM * startup all units that were setup 74511600SVikram.Hegde@Sun.COM */ 74611600SVikram.Hegde@Sun.COM static void 74711600SVikram.Hegde@Sun.COM immu_subsystems_startup(void) 74811600SVikram.Hegde@Sun.COM { 74911600SVikram.Hegde@Sun.COM immu_t *immu; 75011600SVikram.Hegde@Sun.COM 75111600SVikram.Hegde@Sun.COM mutex_enter(&immu_lock); 75211600SVikram.Hegde@Sun.COM 75311600SVikram.Hegde@Sun.COM ASSERT(immu_setup == B_TRUE); 75411600SVikram.Hegde@Sun.COM ASSERT(immu_running == B_FALSE); 75511600SVikram.Hegde@Sun.COM 75611600SVikram.Hegde@Sun.COM immu_dmar_startup(); 75711600SVikram.Hegde@Sun.COM 75811600SVikram.Hegde@Sun.COM immu = list_head(&immu_list); 75911600SVikram.Hegde@Sun.COM for (; immu; immu = list_next(&immu_list, immu)) { 76011600SVikram.Hegde@Sun.COM 76111600SVikram.Hegde@Sun.COM mutex_enter(&(immu->immu_lock)); 76211600SVikram.Hegde@Sun.COM 76311600SVikram.Hegde@Sun.COM immu_intr_register(immu); 76411600SVikram.Hegde@Sun.COM immu_dvma_startup(immu); 76511600SVikram.Hegde@Sun.COM immu_intrmap_startup(immu); 76611600SVikram.Hegde@Sun.COM immu_qinv_startup(immu); 76711600SVikram.Hegde@Sun.COM 76811600SVikram.Hegde@Sun.COM /* 76911600SVikram.Hegde@Sun.COM * Set IOMMU unit's regs to do 77011600SVikram.Hegde@Sun.COM * the actual startup. This will 77111600SVikram.Hegde@Sun.COM * set immu->immu_running field 77211600SVikram.Hegde@Sun.COM * if the unit is successfully 77311600SVikram.Hegde@Sun.COM * started 77411600SVikram.Hegde@Sun.COM */ 77511600SVikram.Hegde@Sun.COM immu_regs_startup(immu); 77611600SVikram.Hegde@Sun.COM 77711600SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 77811600SVikram.Hegde@Sun.COM } 77911600SVikram.Hegde@Sun.COM 78011600SVikram.Hegde@Sun.COM mutex_exit(&immu_lock); 78111600SVikram.Hegde@Sun.COM } 78211600SVikram.Hegde@Sun.COM 78311600SVikram.Hegde@Sun.COM /* ################## Intel IOMMU internal interfaces ###################### */ 78411600SVikram.Hegde@Sun.COM 78511600SVikram.Hegde@Sun.COM /* 78611600SVikram.Hegde@Sun.COM * Internal interfaces for IOMMU code (i.e. not exported to rootnex 78711600SVikram.Hegde@Sun.COM * or rest of system) 78811600SVikram.Hegde@Sun.COM */ 78911600SVikram.Hegde@Sun.COM 79011600SVikram.Hegde@Sun.COM /* 79111600SVikram.Hegde@Sun.COM * ddip can be NULL, in which case we walk up until we find the root dip 79211600SVikram.Hegde@Sun.COM * NOTE: We never visit the root dip since its not a hardware node 79311600SVikram.Hegde@Sun.COM */ 79411600SVikram.Hegde@Sun.COM int 79511600SVikram.Hegde@Sun.COM immu_walk_ancestor( 79611600SVikram.Hegde@Sun.COM dev_info_t *rdip, 79711600SVikram.Hegde@Sun.COM dev_info_t *ddip, 79811600SVikram.Hegde@Sun.COM int (*func)(dev_info_t *, void *arg), 79911600SVikram.Hegde@Sun.COM void *arg, 80011600SVikram.Hegde@Sun.COM int *lvlp, 80111600SVikram.Hegde@Sun.COM immu_flags_t immu_flags) 80211600SVikram.Hegde@Sun.COM { 80311600SVikram.Hegde@Sun.COM dev_info_t *pdip; 80411600SVikram.Hegde@Sun.COM int level; 80511600SVikram.Hegde@Sun.COM int error = DDI_SUCCESS; 80611600SVikram.Hegde@Sun.COM 80711600SVikram.Hegde@Sun.COM ASSERT(root_devinfo); 80811600SVikram.Hegde@Sun.COM ASSERT(rdip); 80911600SVikram.Hegde@Sun.COM ASSERT(rdip != root_devinfo); 81011600SVikram.Hegde@Sun.COM ASSERT(func); 81111600SVikram.Hegde@Sun.COM 81211600SVikram.Hegde@Sun.COM /* ddip and immu can be NULL */ 81311600SVikram.Hegde@Sun.COM 81411600SVikram.Hegde@Sun.COM /* Hold rdip so that branch is not detached */ 81511600SVikram.Hegde@Sun.COM ndi_hold_devi(rdip); 81611600SVikram.Hegde@Sun.COM for (pdip = rdip, level = 1; pdip && pdip != root_devinfo; 81711600SVikram.Hegde@Sun.COM pdip = ddi_get_parent(pdip), level++) { 81811600SVikram.Hegde@Sun.COM 81911600SVikram.Hegde@Sun.COM if (immu_devi_set(pdip, immu_flags) != DDI_SUCCESS) { 82011600SVikram.Hegde@Sun.COM error = DDI_FAILURE; 82111600SVikram.Hegde@Sun.COM break; 82211600SVikram.Hegde@Sun.COM } 82311600SVikram.Hegde@Sun.COM if (func(pdip, arg) == DDI_WALK_TERMINATE) { 82411600SVikram.Hegde@Sun.COM break; 82511600SVikram.Hegde@Sun.COM } 82611600SVikram.Hegde@Sun.COM if (immu_flags & IMMU_FLAGS_DONTPASS) { 82711600SVikram.Hegde@Sun.COM break; 82811600SVikram.Hegde@Sun.COM } 82911600SVikram.Hegde@Sun.COM if (pdip == ddip) { 83011600SVikram.Hegde@Sun.COM break; 83111600SVikram.Hegde@Sun.COM } 83211600SVikram.Hegde@Sun.COM } 83311600SVikram.Hegde@Sun.COM 83411600SVikram.Hegde@Sun.COM ndi_rele_devi(rdip); 83511600SVikram.Hegde@Sun.COM 83611600SVikram.Hegde@Sun.COM if (lvlp) 83711600SVikram.Hegde@Sun.COM *lvlp = level; 83811600SVikram.Hegde@Sun.COM 83911600SVikram.Hegde@Sun.COM return (error); 84011600SVikram.Hegde@Sun.COM } 84111600SVikram.Hegde@Sun.COM 84211600SVikram.Hegde@Sun.COM /* ######################## Intel IOMMU entry points ####################### */ 84311600SVikram.Hegde@Sun.COM /* 84411600SVikram.Hegde@Sun.COM * immu_init() 84511600SVikram.Hegde@Sun.COM * called from rootnex_attach(). setup but don't startup the Intel IOMMU 84611600SVikram.Hegde@Sun.COM * This is the first function called in Intel IOMMU code 84711600SVikram.Hegde@Sun.COM */ 84811600SVikram.Hegde@Sun.COM void 84911600SVikram.Hegde@Sun.COM immu_init(void) 85011600SVikram.Hegde@Sun.COM { 85111600SVikram.Hegde@Sun.COM char *phony_reg = "A thing of beauty is a joy forever"; 85211600SVikram.Hegde@Sun.COM 85311600SVikram.Hegde@Sun.COM /* Set some global shorthands that are needed by all of IOMMU code */ 85411600SVikram.Hegde@Sun.COM ASSERT(root_devinfo == NULL); 85511600SVikram.Hegde@Sun.COM root_devinfo = ddi_root_node(); 85611600SVikram.Hegde@Sun.COM 85711600SVikram.Hegde@Sun.COM /* 85811600SVikram.Hegde@Sun.COM * Intel IOMMU only supported only if MMU(CPU) page size is == 85911600SVikram.Hegde@Sun.COM * IOMMU pages size. 86011600SVikram.Hegde@Sun.COM */ 86111600SVikram.Hegde@Sun.COM /*LINTED*/ 86211600SVikram.Hegde@Sun.COM if (MMU_PAGESIZE != IMMU_PAGESIZE) { 86311600SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, 86411600SVikram.Hegde@Sun.COM "MMU page size (%d) is not equal to\n" 86511600SVikram.Hegde@Sun.COM "IOMMU page size (%d). " 86611600SVikram.Hegde@Sun.COM "Disabling Intel IOMMU. ", 86711600SVikram.Hegde@Sun.COM MMU_PAGESIZE, IMMU_PAGESIZE); 86811600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 86911600SVikram.Hegde@Sun.COM return; 87011600SVikram.Hegde@Sun.COM } 87111600SVikram.Hegde@Sun.COM 87211600SVikram.Hegde@Sun.COM /* 873*11749SVikram.Hegde@Sun.COM * Read rootnex.conf options. Do this before 874*11749SVikram.Hegde@Sun.COM * boot options so boot options can override .conf options. 875*11749SVikram.Hegde@Sun.COM */ 876*11749SVikram.Hegde@Sun.COM read_conf_options(); 877*11749SVikram.Hegde@Sun.COM 878*11749SVikram.Hegde@Sun.COM /* 87911600SVikram.Hegde@Sun.COM * retrieve the Intel IOMMU boot options. 88011600SVikram.Hegde@Sun.COM * Do this before parsing immu ACPI table 88111600SVikram.Hegde@Sun.COM * as a boot option could potentially affect 88211600SVikram.Hegde@Sun.COM * ACPI parsing. 88311600SVikram.Hegde@Sun.COM */ 88411600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, "?Reading Intel IOMMU boot options\n"); 88511600SVikram.Hegde@Sun.COM read_boot_options(); 88611600SVikram.Hegde@Sun.COM 88711600SVikram.Hegde@Sun.COM /* 88811600SVikram.Hegde@Sun.COM * Check the IOMMU enable boot-option first. 88911600SVikram.Hegde@Sun.COM * This is so that we can skip parsing the ACPI table 89011600SVikram.Hegde@Sun.COM * if necessary because that may cause problems in 89111600SVikram.Hegde@Sun.COM * systems with buggy BIOS or ACPI tables 89211600SVikram.Hegde@Sun.COM */ 89311600SVikram.Hegde@Sun.COM if (immu_enable == B_FALSE) { 89411600SVikram.Hegde@Sun.COM return; 89511600SVikram.Hegde@Sun.COM } 89611600SVikram.Hegde@Sun.COM 89711600SVikram.Hegde@Sun.COM /* 89811600SVikram.Hegde@Sun.COM * Next, check if the system even has an Intel IOMMU 89911600SVikram.Hegde@Sun.COM * We use the presence or absence of the IOMMU ACPI 90011600SVikram.Hegde@Sun.COM * table to detect Intel IOMMU. 90111600SVikram.Hegde@Sun.COM */ 90211600SVikram.Hegde@Sun.COM if (immu_dmar_setup() != DDI_SUCCESS) { 90311600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 90411600SVikram.Hegde@Sun.COM return; 90511600SVikram.Hegde@Sun.COM } 90611600SVikram.Hegde@Sun.COM 90711600SVikram.Hegde@Sun.COM /* 90811600SVikram.Hegde@Sun.COM * Check blacklists 90911600SVikram.Hegde@Sun.COM */ 91011600SVikram.Hegde@Sun.COM blacklist_setup(); 91111600SVikram.Hegde@Sun.COM 91211600SVikram.Hegde@Sun.COM if (blacklisted_smbios() == B_TRUE) { 91311600SVikram.Hegde@Sun.COM blacklist_destroy(); 91411600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 91511600SVikram.Hegde@Sun.COM return; 91611600SVikram.Hegde@Sun.COM } 91711600SVikram.Hegde@Sun.COM 91811600SVikram.Hegde@Sun.COM if (blacklisted_driver() == B_TRUE) { 91911600SVikram.Hegde@Sun.COM blacklist_destroy(); 92011600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 92111600SVikram.Hegde@Sun.COM return; 92211600SVikram.Hegde@Sun.COM } 92311600SVikram.Hegde@Sun.COM 92411600SVikram.Hegde@Sun.COM /* 92511600SVikram.Hegde@Sun.COM * Read the "raw" DMAR ACPI table to get information 92611600SVikram.Hegde@Sun.COM * and convert into a form we can use. 92711600SVikram.Hegde@Sun.COM */ 92811600SVikram.Hegde@Sun.COM if (immu_dmar_parse() != DDI_SUCCESS) { 92911600SVikram.Hegde@Sun.COM blacklist_destroy(); 93011600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 93111600SVikram.Hegde@Sun.COM return; 93211600SVikram.Hegde@Sun.COM } 93311600SVikram.Hegde@Sun.COM 93411600SVikram.Hegde@Sun.COM /* 93511600SVikram.Hegde@Sun.COM * now that we have processed the ACPI table 93611600SVikram.Hegde@Sun.COM * check if we need to blacklist this system 93711600SVikram.Hegde@Sun.COM * based on ACPI info 93811600SVikram.Hegde@Sun.COM */ 93911600SVikram.Hegde@Sun.COM if (blacklisted_acpi() == B_TRUE) { 94011600SVikram.Hegde@Sun.COM immu_dmar_destroy(); 94111600SVikram.Hegde@Sun.COM blacklist_destroy(); 94211600SVikram.Hegde@Sun.COM immu_enable = B_FALSE; 94311600SVikram.Hegde@Sun.COM return; 94411600SVikram.Hegde@Sun.COM } 94511600SVikram.Hegde@Sun.COM 94611600SVikram.Hegde@Sun.COM blacklist_destroy(); 94711600SVikram.Hegde@Sun.COM 94811600SVikram.Hegde@Sun.COM /* 94911600SVikram.Hegde@Sun.COM * Check if system has HW quirks. 95011600SVikram.Hegde@Sun.COM */ 95111600SVikram.Hegde@Sun.COM pre_setup_quirks(); 95211600SVikram.Hegde@Sun.COM 95311600SVikram.Hegde@Sun.COM /* Now do the rest of the setup */ 95411600SVikram.Hegde@Sun.COM immu_subsystems_setup(); 95511600SVikram.Hegde@Sun.COM 95611600SVikram.Hegde@Sun.COM /* 95711600SVikram.Hegde@Sun.COM * Now that the IMMU is setup, create a phony 95811600SVikram.Hegde@Sun.COM * reg prop so that suspend/resume works 95911600SVikram.Hegde@Sun.COM */ 96011600SVikram.Hegde@Sun.COM if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, root_devinfo, "reg", 96111600SVikram.Hegde@Sun.COM (uchar_t *)phony_reg, strlen(phony_reg) + 1) != DDI_PROP_SUCCESS) { 96211600SVikram.Hegde@Sun.COM ddi_err(DER_PANIC, NULL, "Failed to create reg prop for " 96311600SVikram.Hegde@Sun.COM "rootnex node"); 96411600SVikram.Hegde@Sun.COM /*NOTREACHED*/ 96511600SVikram.Hegde@Sun.COM } 96611600SVikram.Hegde@Sun.COM 96711600SVikram.Hegde@Sun.COM immu_setup = B_TRUE; 96811600SVikram.Hegde@Sun.COM } 96911600SVikram.Hegde@Sun.COM 97011600SVikram.Hegde@Sun.COM /* 97111600SVikram.Hegde@Sun.COM * immu_startup() 97211600SVikram.Hegde@Sun.COM * called directly by boot code to startup 97311600SVikram.Hegde@Sun.COM * all units of the IOMMU 97411600SVikram.Hegde@Sun.COM */ 97511600SVikram.Hegde@Sun.COM void 97611600SVikram.Hegde@Sun.COM immu_startup(void) 97711600SVikram.Hegde@Sun.COM { 97811600SVikram.Hegde@Sun.COM /* 97911600SVikram.Hegde@Sun.COM * If IOMMU is disabled, do nothing 98011600SVikram.Hegde@Sun.COM */ 98111600SVikram.Hegde@Sun.COM if (immu_enable == B_FALSE) { 98211600SVikram.Hegde@Sun.COM return; 98311600SVikram.Hegde@Sun.COM } 98411600SVikram.Hegde@Sun.COM 98511600SVikram.Hegde@Sun.COM if (immu_setup == B_FALSE) { 98611600SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "Intel IOMMU not setup, " 98711600SVikram.Hegde@Sun.COM "skipping IOMU startup"); 98811600SVikram.Hegde@Sun.COM return; 98911600SVikram.Hegde@Sun.COM } 99011600SVikram.Hegde@Sun.COM 99111600SVikram.Hegde@Sun.COM pre_startup_quirks(); 99211600SVikram.Hegde@Sun.COM 99311600SVikram.Hegde@Sun.COM ddi_err(DER_CONT, NULL, 99411600SVikram.Hegde@Sun.COM "?Starting Intel IOMMU (dmar) units...\n"); 99511600SVikram.Hegde@Sun.COM 99611600SVikram.Hegde@Sun.COM immu_subsystems_startup(); 99711600SVikram.Hegde@Sun.COM 99811600SVikram.Hegde@Sun.COM immu_running = B_TRUE; 99911600SVikram.Hegde@Sun.COM } 100011600SVikram.Hegde@Sun.COM 100111600SVikram.Hegde@Sun.COM /* 100211600SVikram.Hegde@Sun.COM * immu_map_sgl() 100311600SVikram.Hegde@Sun.COM * called from rootnex_coredma_bindhdl() when Intel 100411600SVikram.Hegde@Sun.COM * IOMMU is enabled to build DVMA cookies and map them. 100511600SVikram.Hegde@Sun.COM */ 100611600SVikram.Hegde@Sun.COM int 100711600SVikram.Hegde@Sun.COM immu_map_sgl(ddi_dma_impl_t *hp, struct ddi_dma_req *dmareq, 100811600SVikram.Hegde@Sun.COM int prealloc_count, dev_info_t *rdip) 100911600SVikram.Hegde@Sun.COM { 101011600SVikram.Hegde@Sun.COM if (immu_running == B_FALSE) { 101111600SVikram.Hegde@Sun.COM return (DDI_DMA_USE_PHYSICAL); 101211600SVikram.Hegde@Sun.COM } 101311600SVikram.Hegde@Sun.COM 101411600SVikram.Hegde@Sun.COM return (immu_dvma_map(hp, dmareq, NULL, prealloc_count, rdip, 101511600SVikram.Hegde@Sun.COM IMMU_FLAGS_DMAHDL)); 101611600SVikram.Hegde@Sun.COM } 101711600SVikram.Hegde@Sun.COM 101811600SVikram.Hegde@Sun.COM /* 101911600SVikram.Hegde@Sun.COM * immu_unmap_sgl() 102011600SVikram.Hegde@Sun.COM * called from rootnex_coredma_unbindhdl(), to unmap DVMA 102111600SVikram.Hegde@Sun.COM * cookies and free them 102211600SVikram.Hegde@Sun.COM */ 102311600SVikram.Hegde@Sun.COM int 102411600SVikram.Hegde@Sun.COM immu_unmap_sgl(ddi_dma_impl_t *hp, dev_info_t *rdip) 102511600SVikram.Hegde@Sun.COM { 102611600SVikram.Hegde@Sun.COM if (immu_running == B_FALSE) { 102711600SVikram.Hegde@Sun.COM return (DDI_DMA_USE_PHYSICAL); 102811600SVikram.Hegde@Sun.COM } 102911600SVikram.Hegde@Sun.COM 103011600SVikram.Hegde@Sun.COM return (immu_dvma_unmap(hp, rdip)); 103111600SVikram.Hegde@Sun.COM } 103211600SVikram.Hegde@Sun.COM 103311600SVikram.Hegde@Sun.COM /* 103411600SVikram.Hegde@Sun.COM * Hook to notify IOMMU code of device tree changes 103511600SVikram.Hegde@Sun.COM */ 103611600SVikram.Hegde@Sun.COM void 103711600SVikram.Hegde@Sun.COM immu_device_tree_changed(void) 103811600SVikram.Hegde@Sun.COM { 103911600SVikram.Hegde@Sun.COM if (immu_setup == B_FALSE) { 104011600SVikram.Hegde@Sun.COM return; 104111600SVikram.Hegde@Sun.COM } 104211600SVikram.Hegde@Sun.COM 104311600SVikram.Hegde@Sun.COM ddi_err(DER_WARN, NULL, "Intel IOMMU currently " 104411600SVikram.Hegde@Sun.COM "does not use device tree updates"); 104511600SVikram.Hegde@Sun.COM } 104611600SVikram.Hegde@Sun.COM 104711600SVikram.Hegde@Sun.COM /* 104811600SVikram.Hegde@Sun.COM * Hook to notify IOMMU code of memory changes 104911600SVikram.Hegde@Sun.COM */ 105011600SVikram.Hegde@Sun.COM void 105111600SVikram.Hegde@Sun.COM immu_physmem_update(uint64_t addr, uint64_t size) 105211600SVikram.Hegde@Sun.COM { 105311600SVikram.Hegde@Sun.COM if (immu_setup == B_FALSE) { 105411600SVikram.Hegde@Sun.COM return; 105511600SVikram.Hegde@Sun.COM } 105611600SVikram.Hegde@Sun.COM immu_dvma_physmem_update(addr, size); 105711600SVikram.Hegde@Sun.COM } 105811600SVikram.Hegde@Sun.COM 105911600SVikram.Hegde@Sun.COM /* 106011600SVikram.Hegde@Sun.COM * immu_quiesce() 106111600SVikram.Hegde@Sun.COM * quiesce all units that are running 106211600SVikram.Hegde@Sun.COM */ 106311600SVikram.Hegde@Sun.COM int 106411600SVikram.Hegde@Sun.COM immu_quiesce(void) 106511600SVikram.Hegde@Sun.COM { 106611600SVikram.Hegde@Sun.COM immu_t *immu; 106711600SVikram.Hegde@Sun.COM int ret = DDI_SUCCESS; 106811600SVikram.Hegde@Sun.COM 106911600SVikram.Hegde@Sun.COM mutex_enter(&immu_lock); 107011600SVikram.Hegde@Sun.COM 107111600SVikram.Hegde@Sun.COM if (immu_running == B_FALSE) 107211600SVikram.Hegde@Sun.COM return (DDI_SUCCESS); 107311600SVikram.Hegde@Sun.COM 107411600SVikram.Hegde@Sun.COM ASSERT(immu_setup == B_TRUE); 107511600SVikram.Hegde@Sun.COM 107611600SVikram.Hegde@Sun.COM immu = list_head(&immu_list); 107711600SVikram.Hegde@Sun.COM for (; immu; immu = list_next(&immu_list, immu)) { 107811600SVikram.Hegde@Sun.COM 107911600SVikram.Hegde@Sun.COM /* if immu is not running, we dont quiesce */ 108011600SVikram.Hegde@Sun.COM if (immu->immu_regs_running == B_FALSE) 108111600SVikram.Hegde@Sun.COM continue; 108211600SVikram.Hegde@Sun.COM 108311600SVikram.Hegde@Sun.COM /* flush caches */ 108411600SVikram.Hegde@Sun.COM rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 108511600SVikram.Hegde@Sun.COM immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 108611600SVikram.Hegde@Sun.COM rw_exit(&(immu->immu_ctx_rwlock)); 108711600SVikram.Hegde@Sun.COM immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 108811600SVikram.Hegde@Sun.COM immu_regs_wbf_flush(immu); 108911600SVikram.Hegde@Sun.COM 109011600SVikram.Hegde@Sun.COM mutex_enter(&(immu->immu_lock)); 109111600SVikram.Hegde@Sun.COM 109211600SVikram.Hegde@Sun.COM /* 109311600SVikram.Hegde@Sun.COM * Set IOMMU unit's regs to do 109411600SVikram.Hegde@Sun.COM * the actual shutdown. 109511600SVikram.Hegde@Sun.COM */ 109611600SVikram.Hegde@Sun.COM immu_regs_shutdown(immu); 109711600SVikram.Hegde@Sun.COM immu_regs_suspend(immu); 109811600SVikram.Hegde@Sun.COM 109911600SVikram.Hegde@Sun.COM /* if immu is still running, we failed */ 110011600SVikram.Hegde@Sun.COM if (immu->immu_regs_running == B_TRUE) 110111600SVikram.Hegde@Sun.COM ret = DDI_FAILURE; 110211600SVikram.Hegde@Sun.COM else 110311600SVikram.Hegde@Sun.COM immu->immu_regs_quiesced = B_TRUE; 110411600SVikram.Hegde@Sun.COM 110511600SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 110611600SVikram.Hegde@Sun.COM } 110711600SVikram.Hegde@Sun.COM mutex_exit(&immu_lock); 110811600SVikram.Hegde@Sun.COM 110911600SVikram.Hegde@Sun.COM if (ret == DDI_SUCCESS) { 111011600SVikram.Hegde@Sun.COM immu_running = B_FALSE; 111111600SVikram.Hegde@Sun.COM immu_quiesced = B_TRUE; 111211600SVikram.Hegde@Sun.COM } 111311600SVikram.Hegde@Sun.COM 111411600SVikram.Hegde@Sun.COM return (ret); 111511600SVikram.Hegde@Sun.COM } 111611600SVikram.Hegde@Sun.COM 111711600SVikram.Hegde@Sun.COM /* 111811600SVikram.Hegde@Sun.COM * immu_unquiesce() 111911600SVikram.Hegde@Sun.COM * unquiesce all units 112011600SVikram.Hegde@Sun.COM */ 112111600SVikram.Hegde@Sun.COM int 112211600SVikram.Hegde@Sun.COM immu_unquiesce(void) 112311600SVikram.Hegde@Sun.COM { 112411600SVikram.Hegde@Sun.COM immu_t *immu; 112511600SVikram.Hegde@Sun.COM int ret = DDI_SUCCESS; 112611600SVikram.Hegde@Sun.COM 112711600SVikram.Hegde@Sun.COM mutex_enter(&immu_lock); 112811600SVikram.Hegde@Sun.COM 112911600SVikram.Hegde@Sun.COM if (immu_quiesced == B_FALSE) 113011600SVikram.Hegde@Sun.COM return (DDI_SUCCESS); 113111600SVikram.Hegde@Sun.COM 113211600SVikram.Hegde@Sun.COM ASSERT(immu_setup == B_TRUE); 113311600SVikram.Hegde@Sun.COM ASSERT(immu_running == B_FALSE); 113411600SVikram.Hegde@Sun.COM 113511600SVikram.Hegde@Sun.COM immu = list_head(&immu_list); 113611600SVikram.Hegde@Sun.COM for (; immu; immu = list_next(&immu_list, immu)) { 113711600SVikram.Hegde@Sun.COM 113811600SVikram.Hegde@Sun.COM mutex_enter(&(immu->immu_lock)); 113911600SVikram.Hegde@Sun.COM 114011600SVikram.Hegde@Sun.COM /* if immu was not quiesced, i.e was not running before */ 114111658SVikram.Hegde@Sun.COM if (immu->immu_regs_quiesced == B_FALSE) { 114211658SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 114311600SVikram.Hegde@Sun.COM continue; 114411658SVikram.Hegde@Sun.COM } 114511600SVikram.Hegde@Sun.COM 114611600SVikram.Hegde@Sun.COM if (immu_regs_resume(immu) != DDI_SUCCESS) { 114711600SVikram.Hegde@Sun.COM ret = DDI_FAILURE; 114811658SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 114911600SVikram.Hegde@Sun.COM continue; 115011600SVikram.Hegde@Sun.COM } 115111600SVikram.Hegde@Sun.COM 115211600SVikram.Hegde@Sun.COM /* flush caches before unquiesce */ 115311600SVikram.Hegde@Sun.COM rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 115411600SVikram.Hegde@Sun.COM immu_regs_context_flush(immu, 0, 0, 0, CONTEXT_GLOBAL); 115511600SVikram.Hegde@Sun.COM rw_exit(&(immu->immu_ctx_rwlock)); 115611600SVikram.Hegde@Sun.COM immu_regs_iotlb_flush(immu, 0, 0, 0, 0, IOTLB_GLOBAL); 115711600SVikram.Hegde@Sun.COM 115811600SVikram.Hegde@Sun.COM /* 115911600SVikram.Hegde@Sun.COM * Set IOMMU unit's regs to do 116011600SVikram.Hegde@Sun.COM * the actual startup. This will 116111600SVikram.Hegde@Sun.COM * set immu->immu_regs_running field 116211600SVikram.Hegde@Sun.COM * if the unit is successfully 116311600SVikram.Hegde@Sun.COM * started 116411600SVikram.Hegde@Sun.COM */ 116511600SVikram.Hegde@Sun.COM immu_regs_startup(immu); 116611600SVikram.Hegde@Sun.COM 116711600SVikram.Hegde@Sun.COM if (immu->immu_regs_running == B_FALSE) { 116811600SVikram.Hegde@Sun.COM ret = DDI_FAILURE; 116911600SVikram.Hegde@Sun.COM } else { 117011600SVikram.Hegde@Sun.COM immu_quiesced = B_TRUE; 117111600SVikram.Hegde@Sun.COM immu_running = B_TRUE; 117211600SVikram.Hegde@Sun.COM immu->immu_regs_quiesced = B_FALSE; 117311600SVikram.Hegde@Sun.COM } 117411600SVikram.Hegde@Sun.COM 117511600SVikram.Hegde@Sun.COM mutex_exit(&(immu->immu_lock)); 117611600SVikram.Hegde@Sun.COM } 117711600SVikram.Hegde@Sun.COM 117811600SVikram.Hegde@Sun.COM mutex_exit(&immu_lock); 117911600SVikram.Hegde@Sun.COM 118011600SVikram.Hegde@Sun.COM return (ret); 118111600SVikram.Hegde@Sun.COM } 118211600SVikram.Hegde@Sun.COM 118311600SVikram.Hegde@Sun.COM /* ############## END Intel IOMMU entry points ################## */ 1184