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 */ 29*cd21e7c5SGarrett D'Amore /* 30*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 31*cd21e7c5SGarrett D'Amore */ 323a634bfcSVikram Hegde 333a634bfcSVikram Hegde /* 343a634bfcSVikram Hegde * DVMA code 353a634bfcSVikram Hegde * This file contains Intel IOMMU code that deals with DVMA 363a634bfcSVikram Hegde * i.e. DMA remapping. 373a634bfcSVikram Hegde */ 383a634bfcSVikram Hegde 393a634bfcSVikram Hegde #include <sys/sysmacros.h> 403a634bfcSVikram Hegde #include <sys/pcie.h> 413a634bfcSVikram Hegde #include <sys/pci_cfgspace.h> 423a634bfcSVikram Hegde #include <vm/hat_i86.h> 433a634bfcSVikram Hegde #include <sys/memlist.h> 443a634bfcSVikram Hegde #include <sys/acpi/acpi.h> 453a634bfcSVikram Hegde #include <sys/acpica.h> 463a634bfcSVikram Hegde #include <sys/modhash.h> 473a634bfcSVikram Hegde #include <sys/immu.h> 4850200e77SFrank Van Der Linden #include <sys/x86_archext.h> 4950200e77SFrank Van Der Linden #include <sys/archsystm.h> 503a634bfcSVikram Hegde 513a634bfcSVikram Hegde #undef TEST 523a634bfcSVikram Hegde 533a634bfcSVikram Hegde /* 543a634bfcSVikram Hegde * Macros based on PCI spec 553a634bfcSVikram Hegde */ 563a634bfcSVikram Hegde #define IMMU_PCI_REV2CLASS(r) ((r) >> 8) /* classcode from revid */ 573a634bfcSVikram Hegde #define IMMU_PCI_CLASS2BASE(c) ((c) >> 16) /* baseclass from classcode */ 583a634bfcSVikram Hegde #define IMMU_PCI_CLASS2SUB(c) (((c) >> 8) & 0xff); /* classcode */ 593a634bfcSVikram Hegde 603a634bfcSVikram Hegde #define IMMU_CONTIG_PADDR(d, p) \ 613a634bfcSVikram Hegde ((d).dck_paddr && ((d).dck_paddr + IMMU_PAGESIZE) == (p)) 623a634bfcSVikram Hegde 633a634bfcSVikram Hegde typedef struct dvma_arg { 643a634bfcSVikram Hegde immu_t *dva_immu; 653a634bfcSVikram Hegde dev_info_t *dva_rdip; 663a634bfcSVikram Hegde dev_info_t *dva_ddip; 673a634bfcSVikram Hegde domain_t *dva_domain; 683a634bfcSVikram Hegde int dva_level; 693a634bfcSVikram Hegde immu_flags_t dva_flags; 703a634bfcSVikram Hegde list_t *dva_list; 713a634bfcSVikram Hegde int dva_error; 723a634bfcSVikram Hegde } dvma_arg_t; 733a634bfcSVikram Hegde 743a634bfcSVikram Hegde static domain_t *domain_create(immu_t *immu, dev_info_t *ddip, 753a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags); 763a634bfcSVikram Hegde static immu_devi_t *create_immu_devi(dev_info_t *rdip, int bus, 773a634bfcSVikram Hegde int dev, int func, immu_flags_t immu_flags); 783a634bfcSVikram Hegde static void destroy_immu_devi(immu_devi_t *immu_devi); 7950200e77SFrank Van Der Linden static boolean_t dvma_map(domain_t *domain, uint64_t sdvma, 8050200e77SFrank Van Der Linden uint64_t nvpages, immu_dcookie_t *dcookies, int dcount, dev_info_t *rdip, 813a634bfcSVikram Hegde immu_flags_t immu_flags); 823a634bfcSVikram Hegde 83e03dceedSVikram Hegde /* Extern globals */ 84e03dceedSVikram Hegde extern struct memlist *phys_install; 853a634bfcSVikram Hegde 8650200e77SFrank Van Der Linden /* 8750200e77SFrank Van Der Linden * iommulib interface functions. 8850200e77SFrank Van Der Linden */ 8950200e77SFrank Van Der Linden static int immu_probe(iommulib_handle_t unitp, dev_info_t *dip); 9050200e77SFrank Van Der Linden static int immu_allochdl(iommulib_handle_t handle, 9150200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, 9250200e77SFrank Van Der Linden int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep); 9350200e77SFrank Van Der Linden static int immu_freehdl(iommulib_handle_t handle, 9450200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle); 9550200e77SFrank Van Der Linden static int immu_bindhdl(iommulib_handle_t handle, dev_info_t *dip, 9650200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, struct ddi_dma_req *dma_req, 9750200e77SFrank Van Der Linden ddi_dma_cookie_t *cookiep, uint_t *ccountp); 9850200e77SFrank Van Der Linden static int immu_unbindhdl(iommulib_handle_t handle, 9950200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle); 10050200e77SFrank Van Der Linden static int immu_sync(iommulib_handle_t handle, dev_info_t *dip, 10150200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off, size_t len, 10250200e77SFrank Van Der Linden uint_t cachefl); 10350200e77SFrank Van Der Linden static int immu_win(iommulib_handle_t handle, dev_info_t *dip, 10450200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win, 10550200e77SFrank Van Der Linden off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp); 10650200e77SFrank Van Der Linden static int immu_mapobject(iommulib_handle_t handle, dev_info_t *dip, 10750200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, 10850200e77SFrank Van Der Linden struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao); 10950200e77SFrank Van Der Linden static int immu_unmapobject(iommulib_handle_t handle, dev_info_t *dip, 11050200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao); 1113a634bfcSVikram Hegde 1123a634bfcSVikram Hegde /* static Globals */ 1133a634bfcSVikram Hegde 1143a634bfcSVikram Hegde /* 1153a634bfcSVikram Hegde * Used to setup DMA objects (memory regions) 1163a634bfcSVikram Hegde * for DMA reads by IOMMU units 1173a634bfcSVikram Hegde */ 1183a634bfcSVikram Hegde static ddi_dma_attr_t immu_dma_attr = { 1193a634bfcSVikram Hegde DMA_ATTR_V0, 1203a634bfcSVikram Hegde 0U, 121d2256d26SFrank Van Der Linden 0xffffffffffffffffULL, 1223a634bfcSVikram Hegde 0xffffffffU, 1233a634bfcSVikram Hegde MMU_PAGESIZE, /* MMU page aligned */ 1243a634bfcSVikram Hegde 0x1, 1253a634bfcSVikram Hegde 0x1, 1263a634bfcSVikram Hegde 0xffffffffU, 127d2256d26SFrank Van Der Linden 0xffffffffffffffffULL, 1283a634bfcSVikram Hegde 1, 1293a634bfcSVikram Hegde 4, 1303a634bfcSVikram Hegde 0 1313a634bfcSVikram Hegde }; 1323a634bfcSVikram Hegde 1333a634bfcSVikram Hegde static ddi_device_acc_attr_t immu_acc_attr = { 1343a634bfcSVikram Hegde DDI_DEVICE_ATTR_V0, 1353a634bfcSVikram Hegde DDI_NEVERSWAP_ACC, 1363a634bfcSVikram Hegde DDI_STRICTORDER_ACC 1373a634bfcSVikram Hegde }; 1383a634bfcSVikram Hegde 13950200e77SFrank Van Der Linden struct iommulib_ops immulib_ops = { 14050200e77SFrank Van Der Linden IOMMU_OPS_VERSION, 14150200e77SFrank Van Der Linden INTEL_IOMMU, 14250200e77SFrank Van Der Linden "Intel IOMMU", 14350200e77SFrank Van Der Linden NULL, 14450200e77SFrank Van Der Linden immu_probe, 14550200e77SFrank Van Der Linden immu_allochdl, 14650200e77SFrank Van Der Linden immu_freehdl, 14750200e77SFrank Van Der Linden immu_bindhdl, 14850200e77SFrank Van Der Linden immu_unbindhdl, 14950200e77SFrank Van Der Linden immu_sync, 15050200e77SFrank Van Der Linden immu_win, 15150200e77SFrank Van Der Linden immu_mapobject, 15250200e77SFrank Van Der Linden immu_unmapobject, 15350200e77SFrank Van Der Linden }; 15450200e77SFrank Van Der Linden 15550200e77SFrank Van Der Linden /* 15650200e77SFrank Van Der Linden * Fake physical address range used to set up initial prealloc mappings. 15750200e77SFrank Van Der Linden * This memory is never actually accessed. It is mapped read-only, 15850200e77SFrank Van Der Linden * and is overwritten as soon as the first DMA bind operation is 15950200e77SFrank Van Der Linden * performed. Since 0 is a special case, just start at the 2nd 16050200e77SFrank Van Der Linden * physical page. 16150200e77SFrank Van Der Linden */ 16250200e77SFrank Van Der Linden 16350200e77SFrank Van Der Linden static immu_dcookie_t immu_precookie = { MMU_PAGESIZE, IMMU_NPREPTES }; 1643a634bfcSVikram Hegde 1653a634bfcSVikram Hegde /* globals private to this file */ 1663a634bfcSVikram Hegde static kmutex_t immu_domain_lock; 1673a634bfcSVikram Hegde static list_t immu_unity_domain_list; 1683a634bfcSVikram Hegde static list_t immu_xlate_domain_list; 1693a634bfcSVikram Hegde 1703a634bfcSVikram Hegde /* structure used to store idx into each level of the page tables */ 1713a634bfcSVikram Hegde typedef struct xlate { 1723a634bfcSVikram Hegde int xlt_level; 1733a634bfcSVikram Hegde uint_t xlt_idx; 1743a634bfcSVikram Hegde pgtable_t *xlt_pgtable; 1753a634bfcSVikram Hegde } xlate_t; 1763a634bfcSVikram Hegde 1773a634bfcSVikram Hegde /* 0 is reserved by Vt-d spec. Solaris reserves 1 */ 1783a634bfcSVikram Hegde #define IMMU_UNITY_DID 1 1793a634bfcSVikram Hegde 1803a634bfcSVikram Hegde static mod_hash_t *bdf_domain_hash; 1813a634bfcSVikram Hegde 18250200e77SFrank Van Der Linden int immu_use_alh; 18350200e77SFrank Van Der Linden int immu_use_tm; 18450200e77SFrank Van Der Linden 1853a634bfcSVikram Hegde static domain_t * 1863a634bfcSVikram Hegde bdf_domain_lookup(immu_devi_t *immu_devi) 1873a634bfcSVikram Hegde { 1883a634bfcSVikram Hegde domain_t *domain; 1893a634bfcSVikram Hegde int16_t seg = immu_devi->imd_seg; 1903a634bfcSVikram Hegde int16_t bus = immu_devi->imd_bus; 1913a634bfcSVikram Hegde int16_t devfunc = immu_devi->imd_devfunc; 1923a634bfcSVikram Hegde uintptr_t bdf = (seg << 16 | bus << 8 | devfunc); 1933a634bfcSVikram Hegde 1943a634bfcSVikram Hegde if (seg < 0 || bus < 0 || devfunc < 0) { 1953a634bfcSVikram Hegde return (NULL); 1963a634bfcSVikram Hegde } 1973a634bfcSVikram Hegde 1983a634bfcSVikram Hegde domain = NULL; 1993a634bfcSVikram Hegde if (mod_hash_find(bdf_domain_hash, 2003a634bfcSVikram Hegde (void *)bdf, (void *)&domain) == 0) { 2013a634bfcSVikram Hegde ASSERT(domain); 2023a634bfcSVikram Hegde ASSERT(domain->dom_did > 0); 2033a634bfcSVikram Hegde return (domain); 2043a634bfcSVikram Hegde } else { 2053a634bfcSVikram Hegde return (NULL); 2063a634bfcSVikram Hegde } 2073a634bfcSVikram Hegde } 2083a634bfcSVikram Hegde 2093a634bfcSVikram Hegde static void 2103a634bfcSVikram Hegde bdf_domain_insert(immu_devi_t *immu_devi, domain_t *domain) 2113a634bfcSVikram Hegde { 2123a634bfcSVikram Hegde int16_t seg = immu_devi->imd_seg; 2133a634bfcSVikram Hegde int16_t bus = immu_devi->imd_bus; 2143a634bfcSVikram Hegde int16_t devfunc = immu_devi->imd_devfunc; 2153a634bfcSVikram Hegde uintptr_t bdf = (seg << 16 | bus << 8 | devfunc); 2163a634bfcSVikram Hegde 2173a634bfcSVikram Hegde if (seg < 0 || bus < 0 || devfunc < 0) { 2183a634bfcSVikram Hegde return; 2193a634bfcSVikram Hegde } 2203a634bfcSVikram Hegde 22150200e77SFrank Van Der Linden (void) mod_hash_insert(bdf_domain_hash, (void *)bdf, (void *)domain); 2223a634bfcSVikram Hegde } 2233a634bfcSVikram Hegde 2243a634bfcSVikram Hegde static int 2253a634bfcSVikram Hegde match_lpc(dev_info_t *pdip, void *arg) 2263a634bfcSVikram Hegde { 2273a634bfcSVikram Hegde immu_devi_t *immu_devi; 2283a634bfcSVikram Hegde dvma_arg_t *dvap = (dvma_arg_t *)arg; 2293a634bfcSVikram Hegde 2303a634bfcSVikram Hegde if (list_is_empty(dvap->dva_list)) { 2313a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 2323a634bfcSVikram Hegde } 2333a634bfcSVikram Hegde 2343a634bfcSVikram Hegde immu_devi = list_head(dvap->dva_list); 2353a634bfcSVikram Hegde for (; immu_devi; immu_devi = list_next(dvap->dva_list, 2363a634bfcSVikram Hegde immu_devi)) { 2373a634bfcSVikram Hegde if (immu_devi->imd_dip == pdip) { 2383a634bfcSVikram Hegde dvap->dva_ddip = pdip; 2393a634bfcSVikram Hegde dvap->dva_error = DDI_SUCCESS; 2403a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 2413a634bfcSVikram Hegde } 2423a634bfcSVikram Hegde } 2433a634bfcSVikram Hegde 2443a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 2453a634bfcSVikram Hegde } 2463a634bfcSVikram Hegde 2473a634bfcSVikram Hegde static void 2483a634bfcSVikram Hegde immu_devi_set_spclist(dev_info_t *dip, immu_t *immu) 2493a634bfcSVikram Hegde { 2503a634bfcSVikram Hegde list_t *spclist = NULL; 2513a634bfcSVikram Hegde immu_devi_t *immu_devi; 2523a634bfcSVikram Hegde 2533a634bfcSVikram Hegde immu_devi = IMMU_DEVI(dip); 2543a634bfcSVikram Hegde if (immu_devi->imd_display == B_TRUE) { 2553a634bfcSVikram Hegde spclist = &(immu->immu_dvma_gfx_list); 2563a634bfcSVikram Hegde } else if (immu_devi->imd_lpc == B_TRUE) { 2573a634bfcSVikram Hegde spclist = &(immu->immu_dvma_lpc_list); 2583a634bfcSVikram Hegde } 2593a634bfcSVikram Hegde 2603a634bfcSVikram Hegde if (spclist) { 2613a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 2623a634bfcSVikram Hegde list_insert_head(spclist, immu_devi); 2633a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 2643a634bfcSVikram Hegde } 2653a634bfcSVikram Hegde } 2663a634bfcSVikram Hegde 2673a634bfcSVikram Hegde /* 2683a634bfcSVikram Hegde * Set the immu_devi struct in the immu_devi field of a devinfo node 2693a634bfcSVikram Hegde */ 2703a634bfcSVikram Hegde int 2713a634bfcSVikram Hegde immu_devi_set(dev_info_t *dip, immu_flags_t immu_flags) 2723a634bfcSVikram Hegde { 2733a634bfcSVikram Hegde int bus, dev, func; 2743a634bfcSVikram Hegde immu_devi_t *new_imd; 2753a634bfcSVikram Hegde immu_devi_t *immu_devi; 2763a634bfcSVikram Hegde 2773a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 2783a634bfcSVikram Hegde if (immu_devi != NULL) { 2793a634bfcSVikram Hegde return (DDI_SUCCESS); 2803a634bfcSVikram Hegde } 2813a634bfcSVikram Hegde 2823a634bfcSVikram Hegde bus = dev = func = -1; 2833a634bfcSVikram Hegde 2843a634bfcSVikram Hegde /* 2853a634bfcSVikram Hegde * Assume a new immu_devi struct is needed 2863a634bfcSVikram Hegde */ 2873a634bfcSVikram Hegde if (!DEVI_IS_PCI(dip) || acpica_get_bdf(dip, &bus, &dev, &func) != 0) { 2883a634bfcSVikram Hegde /* 2893a634bfcSVikram Hegde * No BDF. Set bus = -1 to indicate this. 2903a634bfcSVikram Hegde * We still need to create a immu_devi struct 2913a634bfcSVikram Hegde * though 2923a634bfcSVikram Hegde */ 2933a634bfcSVikram Hegde bus = -1; 2943a634bfcSVikram Hegde dev = 0; 2953a634bfcSVikram Hegde func = 0; 2963a634bfcSVikram Hegde } 2973a634bfcSVikram Hegde 2983a634bfcSVikram Hegde new_imd = create_immu_devi(dip, bus, dev, func, immu_flags); 2993a634bfcSVikram Hegde if (new_imd == NULL) { 3003a634bfcSVikram Hegde ddi_err(DER_WARN, dip, "Failed to create immu_devi " 3013a634bfcSVikram Hegde "structure"); 3023a634bfcSVikram Hegde return (DDI_FAILURE); 3033a634bfcSVikram Hegde } 3043a634bfcSVikram Hegde 3053a634bfcSVikram Hegde /* 3063a634bfcSVikram Hegde * Check if some other thread allocated a immu_devi while we 3073a634bfcSVikram Hegde * didn't own the lock. 3083a634bfcSVikram Hegde */ 3093a634bfcSVikram Hegde mutex_enter(&(DEVI(dip)->devi_lock)); 3103a634bfcSVikram Hegde if (IMMU_DEVI(dip) == NULL) { 3113a634bfcSVikram Hegde IMMU_DEVI_SET(dip, new_imd); 3123a634bfcSVikram Hegde } else { 3133a634bfcSVikram Hegde destroy_immu_devi(new_imd); 3143a634bfcSVikram Hegde } 3153a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 3163a634bfcSVikram Hegde 3173a634bfcSVikram Hegde return (DDI_SUCCESS); 3183a634bfcSVikram Hegde } 3193a634bfcSVikram Hegde 3203a634bfcSVikram Hegde static dev_info_t * 3213a634bfcSVikram Hegde get_lpc_devinfo(immu_t *immu, dev_info_t *rdip, immu_flags_t immu_flags) 3223a634bfcSVikram Hegde { 3233a634bfcSVikram Hegde dvma_arg_t dvarg = {0}; 3243a634bfcSVikram Hegde dvarg.dva_list = &(immu->immu_dvma_lpc_list); 3253a634bfcSVikram Hegde dvarg.dva_rdip = rdip; 3263a634bfcSVikram Hegde dvarg.dva_error = DDI_FAILURE; 3273a634bfcSVikram Hegde 3283a634bfcSVikram Hegde if (immu_walk_ancestor(rdip, NULL, match_lpc, 3293a634bfcSVikram Hegde &dvarg, NULL, immu_flags) != DDI_SUCCESS) { 3303a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "Could not walk ancestors to " 3313a634bfcSVikram Hegde "find lpc_devinfo for ISA device"); 3323a634bfcSVikram Hegde return (NULL); 3333a634bfcSVikram Hegde } 3343a634bfcSVikram Hegde 3353a634bfcSVikram Hegde if (dvarg.dva_error != DDI_SUCCESS || dvarg.dva_ddip == NULL) { 3363a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "Could not find lpc_devinfo for " 3373a634bfcSVikram Hegde "ISA device"); 3383a634bfcSVikram Hegde return (NULL); 3393a634bfcSVikram Hegde } 3403a634bfcSVikram Hegde 3413a634bfcSVikram Hegde return (dvarg.dva_ddip); 3423a634bfcSVikram Hegde } 3433a634bfcSVikram Hegde 3443a634bfcSVikram Hegde static dev_info_t * 3453a634bfcSVikram Hegde get_gfx_devinfo(dev_info_t *rdip) 3463a634bfcSVikram Hegde { 3473a634bfcSVikram Hegde immu_t *immu; 3483a634bfcSVikram Hegde immu_devi_t *immu_devi; 3493a634bfcSVikram Hegde list_t *list_gfx; 3503a634bfcSVikram Hegde 3513a634bfcSVikram Hegde /* 35250200e77SFrank Van Der Linden * The GFX device may not be on the same iommu unit as "agpgart" 3533a634bfcSVikram Hegde * so search globally 3543a634bfcSVikram Hegde */ 3553a634bfcSVikram Hegde immu_devi = NULL; 3563a634bfcSVikram Hegde immu = list_head(&immu_list); 3573a634bfcSVikram Hegde for (; immu; immu = list_next(&immu_list, immu)) { 3583a634bfcSVikram Hegde list_gfx = &(immu->immu_dvma_gfx_list); 3593a634bfcSVikram Hegde if (!list_is_empty(list_gfx)) { 3603a634bfcSVikram Hegde immu_devi = list_head(list_gfx); 3613a634bfcSVikram Hegde break; 3623a634bfcSVikram Hegde } 3633a634bfcSVikram Hegde } 3643a634bfcSVikram Hegde 3653a634bfcSVikram Hegde if (immu_devi == NULL) { 36650200e77SFrank Van Der Linden ddi_err(DER_WARN, rdip, "iommu: No GFX device. " 367e03dceedSVikram Hegde "Cannot redirect agpgart"); 3683a634bfcSVikram Hegde return (NULL); 3693a634bfcSVikram Hegde } 3703a634bfcSVikram Hegde 37150200e77SFrank Van Der Linden ddi_err(DER_LOG, rdip, "iommu: GFX redirect to %s", 3723a634bfcSVikram Hegde ddi_node_name(immu_devi->imd_dip)); 3733a634bfcSVikram Hegde 3743a634bfcSVikram Hegde return (immu_devi->imd_dip); 3753a634bfcSVikram Hegde } 3763a634bfcSVikram Hegde 3773a634bfcSVikram Hegde static immu_flags_t 3783a634bfcSVikram Hegde dma_to_immu_flags(struct ddi_dma_req *dmareq) 3793a634bfcSVikram Hegde { 3803a634bfcSVikram Hegde immu_flags_t flags = 0; 3813a634bfcSVikram Hegde 3823a634bfcSVikram Hegde if (dmareq->dmar_fp == DDI_DMA_SLEEP) { 3833a634bfcSVikram Hegde flags |= IMMU_FLAGS_SLEEP; 3843a634bfcSVikram Hegde } else { 3853a634bfcSVikram Hegde flags |= IMMU_FLAGS_NOSLEEP; 3863a634bfcSVikram Hegde } 3873a634bfcSVikram Hegde 388e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS 389e03dceedSVikram Hegde 390e03dceedSVikram Hegde flags |= (IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 391e03dceedSVikram Hegde 392e03dceedSVikram Hegde #else 3933a634bfcSVikram Hegde /* 3943a634bfcSVikram Hegde * Read and write flags need to be reversed. 3953a634bfcSVikram Hegde * DMA_READ means read from device and write 3963a634bfcSVikram Hegde * to memory. So DMA read means DVMA write. 3973a634bfcSVikram Hegde */ 3983a634bfcSVikram Hegde if (dmareq->dmar_flags & DDI_DMA_READ) 3993a634bfcSVikram Hegde flags |= IMMU_FLAGS_WRITE; 4003a634bfcSVikram Hegde 4013a634bfcSVikram Hegde if (dmareq->dmar_flags & DDI_DMA_WRITE) 4023a634bfcSVikram Hegde flags |= IMMU_FLAGS_READ; 4033a634bfcSVikram Hegde 4043a634bfcSVikram Hegde /* 4053a634bfcSVikram Hegde * Some buggy drivers specify neither READ or WRITE 4063a634bfcSVikram Hegde * For such drivers set both read and write permissions 4073a634bfcSVikram Hegde */ 4083a634bfcSVikram Hegde if ((dmareq->dmar_flags & (DDI_DMA_READ | DDI_DMA_WRITE)) == 0) { 4093a634bfcSVikram Hegde flags |= (IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 4103a634bfcSVikram Hegde } 4113a634bfcSVikram Hegde #endif 4123a634bfcSVikram Hegde 4133a634bfcSVikram Hegde return (flags); 4143a634bfcSVikram Hegde } 4153a634bfcSVikram Hegde 41650200e77SFrank Van Der Linden /*ARGSUSED*/ 417e03dceedSVikram Hegde int 418e03dceedSVikram Hegde pgtable_ctor(void *buf, void *arg, int kmflag) 419e03dceedSVikram Hegde { 420e03dceedSVikram Hegde size_t actual_size = 0; 421e03dceedSVikram Hegde pgtable_t *pgtable; 422e03dceedSVikram Hegde int (*dmafp)(caddr_t); 423e03dceedSVikram Hegde caddr_t vaddr; 424e03dceedSVikram Hegde void *next; 42550200e77SFrank Van Der Linden uint_t flags; 42650200e77SFrank Van Der Linden immu_t *immu = arg; 427e03dceedSVikram Hegde 428e03dceedSVikram Hegde pgtable = (pgtable_t *)buf; 429e03dceedSVikram Hegde 430e03dceedSVikram Hegde dmafp = (kmflag & KM_NOSLEEP) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP; 431e03dceedSVikram Hegde 432e03dceedSVikram Hegde next = kmem_zalloc(IMMU_PAGESIZE, kmflag); 433e03dceedSVikram Hegde if (next == NULL) { 434e03dceedSVikram Hegde return (-1); 435e03dceedSVikram Hegde } 436e03dceedSVikram Hegde 437e03dceedSVikram Hegde if (ddi_dma_alloc_handle(root_devinfo, &immu_dma_attr, 438e03dceedSVikram Hegde dmafp, NULL, &pgtable->hwpg_dmahdl) != DDI_SUCCESS) { 439e03dceedSVikram Hegde kmem_free(next, IMMU_PAGESIZE); 440e03dceedSVikram Hegde return (-1); 441e03dceedSVikram Hegde } 442e03dceedSVikram Hegde 44350200e77SFrank Van Der Linden flags = DDI_DMA_CONSISTENT; 44450200e77SFrank Van Der Linden if (!immu->immu_dvma_coherent) 44550200e77SFrank Van Der Linden flags |= IOMEM_DATA_UC_WR_COMBINE; 44650200e77SFrank Van Der Linden 447e03dceedSVikram Hegde if (ddi_dma_mem_alloc(pgtable->hwpg_dmahdl, IMMU_PAGESIZE, 44850200e77SFrank Van Der Linden &immu_acc_attr, flags, 449e03dceedSVikram Hegde dmafp, NULL, &vaddr, &actual_size, 450e03dceedSVikram Hegde &pgtable->hwpg_memhdl) != DDI_SUCCESS) { 451e03dceedSVikram Hegde ddi_dma_free_handle(&pgtable->hwpg_dmahdl); 452e03dceedSVikram Hegde kmem_free(next, IMMU_PAGESIZE); 453e03dceedSVikram Hegde return (-1); 454e03dceedSVikram Hegde } 455e03dceedSVikram Hegde 456e03dceedSVikram Hegde /* 457e03dceedSVikram Hegde * Memory allocation failure. Maybe a temporary condition 458e03dceedSVikram Hegde * so return error rather than panic, so we can try again 459e03dceedSVikram Hegde */ 460e03dceedSVikram Hegde if (actual_size < IMMU_PAGESIZE) { 461e03dceedSVikram Hegde ddi_dma_mem_free(&pgtable->hwpg_memhdl); 462e03dceedSVikram Hegde ddi_dma_free_handle(&pgtable->hwpg_dmahdl); 463e03dceedSVikram Hegde kmem_free(next, IMMU_PAGESIZE); 464e03dceedSVikram Hegde return (-1); 465e03dceedSVikram Hegde } 466e03dceedSVikram Hegde 467e03dceedSVikram Hegde pgtable->hwpg_paddr = pfn_to_pa(hat_getpfnum(kas.a_hat, vaddr)); 468e03dceedSVikram Hegde pgtable->hwpg_vaddr = vaddr; 469e03dceedSVikram Hegde pgtable->swpg_next_array = next; 470e03dceedSVikram Hegde 471e03dceedSVikram Hegde rw_init(&(pgtable->swpg_rwlock), NULL, RW_DEFAULT, NULL); 472e03dceedSVikram Hegde 473e03dceedSVikram Hegde return (0); 474e03dceedSVikram Hegde } 475e03dceedSVikram Hegde 47650200e77SFrank Van Der Linden /*ARGSUSED*/ 477e03dceedSVikram Hegde void 478e03dceedSVikram Hegde pgtable_dtor(void *buf, void *arg) 479e03dceedSVikram Hegde { 480e03dceedSVikram Hegde pgtable_t *pgtable; 481e03dceedSVikram Hegde 482e03dceedSVikram Hegde pgtable = (pgtable_t *)buf; 483e03dceedSVikram Hegde 484e03dceedSVikram Hegde /* destroy will panic if lock is held. */ 485e03dceedSVikram Hegde rw_destroy(&(pgtable->swpg_rwlock)); 486e03dceedSVikram Hegde 487e03dceedSVikram Hegde ddi_dma_mem_free(&pgtable->hwpg_memhdl); 488e03dceedSVikram Hegde ddi_dma_free_handle(&pgtable->hwpg_dmahdl); 489e03dceedSVikram Hegde kmem_free(pgtable->swpg_next_array, IMMU_PAGESIZE); 490e03dceedSVikram Hegde } 491e03dceedSVikram Hegde 4923a634bfcSVikram Hegde /* 4933a634bfcSVikram Hegde * pgtable_alloc() 4943a634bfcSVikram Hegde * alloc a IOMMU pgtable structure. 4953a634bfcSVikram Hegde * This same struct is used for root and context tables as well. 4963a634bfcSVikram Hegde * This routine allocs the f/ollowing: 4973a634bfcSVikram Hegde * - a pgtable_t struct 4983a634bfcSVikram Hegde * - a HW page which holds PTEs/entries which is accesssed by HW 4993a634bfcSVikram Hegde * so we set up DMA for this page 5003a634bfcSVikram Hegde * - a SW page which is only for our bookeeping 5013a634bfcSVikram Hegde * (for example to hold pointers to the next level pgtable). 5023a634bfcSVikram Hegde * So a simple kmem_alloc suffices 5033a634bfcSVikram Hegde */ 5043a634bfcSVikram Hegde static pgtable_t * 505e03dceedSVikram Hegde pgtable_alloc(immu_t *immu, immu_flags_t immu_flags) 5063a634bfcSVikram Hegde { 5073a634bfcSVikram Hegde pgtable_t *pgtable; 5083a634bfcSVikram Hegde int kmflags; 5093a634bfcSVikram Hegde 510e03dceedSVikram Hegde kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP; 5113a634bfcSVikram Hegde 51250200e77SFrank Van Der Linden pgtable = kmem_cache_alloc(immu->immu_pgtable_cache, kmflags); 5133a634bfcSVikram Hegde if (pgtable == NULL) { 5143a634bfcSVikram Hegde return (NULL); 5153a634bfcSVikram Hegde } 5163a634bfcSVikram Hegde return (pgtable); 5173a634bfcSVikram Hegde } 5183a634bfcSVikram Hegde 5193a634bfcSVikram Hegde static void 52050200e77SFrank Van Der Linden pgtable_zero(pgtable_t *pgtable) 521e03dceedSVikram Hegde { 522e03dceedSVikram Hegde bzero(pgtable->hwpg_vaddr, IMMU_PAGESIZE); 523e03dceedSVikram Hegde bzero(pgtable->swpg_next_array, IMMU_PAGESIZE); 524e03dceedSVikram Hegde } 525e03dceedSVikram Hegde 526e03dceedSVikram Hegde static void 527e03dceedSVikram Hegde pgtable_free(immu_t *immu, pgtable_t *pgtable) 5283a634bfcSVikram Hegde { 52950200e77SFrank Van Der Linden kmem_cache_free(immu->immu_pgtable_cache, pgtable); 5303a634bfcSVikram Hegde } 5313a634bfcSVikram Hegde 5323a634bfcSVikram Hegde /* 5333a634bfcSVikram Hegde * Function to identify a display device from the PCI class code 5343a634bfcSVikram Hegde */ 5353a634bfcSVikram Hegde static boolean_t 5363a634bfcSVikram Hegde device_is_display(uint_t classcode) 5373a634bfcSVikram Hegde { 5383a634bfcSVikram Hegde static uint_t disp_classes[] = { 5393a634bfcSVikram Hegde 0x000100, 5403a634bfcSVikram Hegde 0x030000, 5413a634bfcSVikram Hegde 0x030001 5423a634bfcSVikram Hegde }; 5433a634bfcSVikram Hegde int i, nclasses = sizeof (disp_classes) / sizeof (uint_t); 5443a634bfcSVikram Hegde 5453a634bfcSVikram Hegde for (i = 0; i < nclasses; i++) { 5463a634bfcSVikram Hegde if (classcode == disp_classes[i]) 5473a634bfcSVikram Hegde return (B_TRUE); 5483a634bfcSVikram Hegde } 5493a634bfcSVikram Hegde return (B_FALSE); 5503a634bfcSVikram Hegde } 5513a634bfcSVikram Hegde 5523a634bfcSVikram Hegde /* 5533a634bfcSVikram Hegde * Function that determines if device is PCIEX and/or PCIEX bridge 5543a634bfcSVikram Hegde */ 5553a634bfcSVikram Hegde static boolean_t 5563a634bfcSVikram Hegde device_is_pciex( 5573a634bfcSVikram Hegde uchar_t bus, uchar_t dev, uchar_t func, boolean_t *is_pcib) 5583a634bfcSVikram Hegde { 5593a634bfcSVikram Hegde ushort_t cap; 5603a634bfcSVikram Hegde ushort_t capsp; 5613a634bfcSVikram Hegde ushort_t cap_count = PCI_CAP_MAX_PTR; 5623a634bfcSVikram Hegde ushort_t status; 5633a634bfcSVikram Hegde boolean_t is_pciex = B_FALSE; 5643a634bfcSVikram Hegde 5653a634bfcSVikram Hegde *is_pcib = B_FALSE; 5663a634bfcSVikram Hegde 5673a634bfcSVikram Hegde status = pci_getw_func(bus, dev, func, PCI_CONF_STAT); 5683a634bfcSVikram Hegde if (!(status & PCI_STAT_CAP)) 5693a634bfcSVikram Hegde return (B_FALSE); 5703a634bfcSVikram Hegde 5713a634bfcSVikram Hegde capsp = pci_getb_func(bus, dev, func, PCI_CONF_CAP_PTR); 5723a634bfcSVikram Hegde while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) { 5733a634bfcSVikram Hegde capsp &= PCI_CAP_PTR_MASK; 5743a634bfcSVikram Hegde cap = pci_getb_func(bus, dev, func, capsp); 5753a634bfcSVikram Hegde 5763a634bfcSVikram Hegde if (cap == PCI_CAP_ID_PCI_E) { 5773a634bfcSVikram Hegde status = pci_getw_func(bus, dev, func, capsp + 2); 5783a634bfcSVikram Hegde /* 5793a634bfcSVikram Hegde * See section 7.8.2 of PCI-Express Base Spec v1.0a 5803a634bfcSVikram Hegde * for Device/Port Type. 5813a634bfcSVikram Hegde * PCIE_PCIECAP_DEV_TYPE_PCIE2PCI implies that the 5823a634bfcSVikram Hegde * device is a PCIE2PCI bridge 5833a634bfcSVikram Hegde */ 5843a634bfcSVikram Hegde *is_pcib = 5853a634bfcSVikram Hegde ((status & PCIE_PCIECAP_DEV_TYPE_MASK) == 5863a634bfcSVikram Hegde PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ? B_TRUE : B_FALSE; 5873a634bfcSVikram Hegde is_pciex = B_TRUE; 5883a634bfcSVikram Hegde } 5893a634bfcSVikram Hegde 5903a634bfcSVikram Hegde capsp = (*pci_getb_func)(bus, dev, func, 5913a634bfcSVikram Hegde capsp + PCI_CAP_NEXT_PTR); 5923a634bfcSVikram Hegde } 5933a634bfcSVikram Hegde 5943a634bfcSVikram Hegde return (is_pciex); 5953a634bfcSVikram Hegde } 5963a634bfcSVikram Hegde 59750200e77SFrank Van Der Linden static boolean_t 59850200e77SFrank Van Der Linden device_use_premap(uint_t classcode) 59950200e77SFrank Van Der Linden { 60050200e77SFrank Van Der Linden if (IMMU_PCI_CLASS2BASE(classcode) == PCI_CLASS_NET) 60150200e77SFrank Van Der Linden return (B_TRUE); 60250200e77SFrank Van Der Linden return (B_FALSE); 60350200e77SFrank Van Der Linden } 60450200e77SFrank Van Der Linden 6053a634bfcSVikram Hegde 6063a634bfcSVikram Hegde /* 6073a634bfcSVikram Hegde * immu_dvma_get_immu() 6083a634bfcSVikram Hegde * get the immu unit structure for a dev_info node 6093a634bfcSVikram Hegde */ 6103a634bfcSVikram Hegde immu_t * 6113a634bfcSVikram Hegde immu_dvma_get_immu(dev_info_t *dip, immu_flags_t immu_flags) 6123a634bfcSVikram Hegde { 6133a634bfcSVikram Hegde immu_devi_t *immu_devi; 6143a634bfcSVikram Hegde immu_t *immu; 6153a634bfcSVikram Hegde 6163a634bfcSVikram Hegde /* 6173a634bfcSVikram Hegde * check if immu unit was already found earlier. 6183a634bfcSVikram Hegde * If yes, then it will be stashed in immu_devi struct. 6193a634bfcSVikram Hegde */ 6203a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 6213a634bfcSVikram Hegde if (immu_devi == NULL) { 6223a634bfcSVikram Hegde if (immu_devi_set(dip, immu_flags) != DDI_SUCCESS) { 6233a634bfcSVikram Hegde /* 6243a634bfcSVikram Hegde * May fail because of low memory. Return error rather 6253a634bfcSVikram Hegde * than panic as we want driver to rey again later 6263a634bfcSVikram Hegde */ 6273a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "immu_dvma_get_immu: " 6283a634bfcSVikram Hegde "No immu_devi structure"); 6293a634bfcSVikram Hegde /*NOTREACHED*/ 6303a634bfcSVikram Hegde } 6313a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 6323a634bfcSVikram Hegde } 6333a634bfcSVikram Hegde 6343a634bfcSVikram Hegde mutex_enter(&(DEVI(dip)->devi_lock)); 6353a634bfcSVikram Hegde if (immu_devi->imd_immu) { 6363a634bfcSVikram Hegde immu = immu_devi->imd_immu; 6373a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 6383a634bfcSVikram Hegde return (immu); 6393a634bfcSVikram Hegde } 6403a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 6413a634bfcSVikram Hegde 6423a634bfcSVikram Hegde immu = immu_dmar_get_immu(dip); 6433a634bfcSVikram Hegde if (immu == NULL) { 6443a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "immu_dvma_get_immu: " 6453a634bfcSVikram Hegde "Cannot find immu_t for device"); 6463a634bfcSVikram Hegde /*NOTREACHED*/ 6473a634bfcSVikram Hegde } 6483a634bfcSVikram Hegde 6493a634bfcSVikram Hegde /* 6503a634bfcSVikram Hegde * Check if some other thread found immu 6513a634bfcSVikram Hegde * while lock was not held 6523a634bfcSVikram Hegde */ 6533a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 6543a634bfcSVikram Hegde /* immu_devi should be present as we found it earlier */ 6553a634bfcSVikram Hegde if (immu_devi == NULL) { 6563a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, 6573a634bfcSVikram Hegde "immu_dvma_get_immu: No immu_devi structure"); 6583a634bfcSVikram Hegde /*NOTREACHED*/ 6593a634bfcSVikram Hegde } 6603a634bfcSVikram Hegde 6613a634bfcSVikram Hegde mutex_enter(&(DEVI(dip)->devi_lock)); 6623a634bfcSVikram Hegde if (immu_devi->imd_immu == NULL) { 6633a634bfcSVikram Hegde /* nobody else set it, so we should do it */ 6643a634bfcSVikram Hegde immu_devi->imd_immu = immu; 6653a634bfcSVikram Hegde immu_devi_set_spclist(dip, immu); 6663a634bfcSVikram Hegde } else { 6673a634bfcSVikram Hegde /* 6683a634bfcSVikram Hegde * if some other thread got immu before 6693a634bfcSVikram Hegde * us, it should get the same results 6703a634bfcSVikram Hegde */ 6713a634bfcSVikram Hegde if (immu_devi->imd_immu != immu) { 6723a634bfcSVikram Hegde ddi_err(DER_PANIC, dip, "Multiple " 6733a634bfcSVikram Hegde "immu units found for device. Expected (%p), " 6743a634bfcSVikram Hegde "actual (%p)", (void *)immu, 6753a634bfcSVikram Hegde (void *)immu_devi->imd_immu); 6763a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 6773a634bfcSVikram Hegde /*NOTREACHED*/ 6783a634bfcSVikram Hegde } 6793a634bfcSVikram Hegde } 6803a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 6813a634bfcSVikram Hegde 6823a634bfcSVikram Hegde return (immu); 6833a634bfcSVikram Hegde } 6843a634bfcSVikram Hegde 6853a634bfcSVikram Hegde 6863a634bfcSVikram Hegde /* ############################# IMMU_DEVI code ############################ */ 6873a634bfcSVikram Hegde 6883a634bfcSVikram Hegde /* 6893a634bfcSVikram Hegde * Allocate a immu_devi structure and initialize it 6903a634bfcSVikram Hegde */ 6913a634bfcSVikram Hegde static immu_devi_t * 6923a634bfcSVikram Hegde create_immu_devi(dev_info_t *rdip, int bus, int dev, int func, 6933a634bfcSVikram Hegde immu_flags_t immu_flags) 6943a634bfcSVikram Hegde { 6953a634bfcSVikram Hegde uchar_t baseclass, subclass; 6963a634bfcSVikram Hegde uint_t classcode, revclass; 6973a634bfcSVikram Hegde immu_devi_t *immu_devi; 6983a634bfcSVikram Hegde boolean_t pciex = B_FALSE; 6993a634bfcSVikram Hegde int kmflags; 7003a634bfcSVikram Hegde boolean_t is_pcib = B_FALSE; 7013a634bfcSVikram Hegde 7023a634bfcSVikram Hegde /* bus == -1 indicate non-PCI device (no BDF) */ 7033a634bfcSVikram Hegde ASSERT(bus == -1 || bus >= 0); 7043a634bfcSVikram Hegde ASSERT(dev >= 0); 7053a634bfcSVikram Hegde ASSERT(func >= 0); 7063a634bfcSVikram Hegde 7073a634bfcSVikram Hegde kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP; 7083a634bfcSVikram Hegde immu_devi = kmem_zalloc(sizeof (immu_devi_t), kmflags); 7093a634bfcSVikram Hegde if (immu_devi == NULL) { 7103a634bfcSVikram Hegde ddi_err(DER_WARN, rdip, "Failed to allocate memory for " 7113a634bfcSVikram Hegde "Intel IOMMU immu_devi structure"); 7123a634bfcSVikram Hegde return (NULL); 7133a634bfcSVikram Hegde } 7143a634bfcSVikram Hegde immu_devi->imd_dip = rdip; 7153a634bfcSVikram Hegde immu_devi->imd_seg = 0; /* Currently seg can only be 0 */ 7163a634bfcSVikram Hegde immu_devi->imd_bus = bus; 7173a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_BAD; 7183a634bfcSVikram Hegde 7193a634bfcSVikram Hegde if (bus == -1) { 7203a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_NOBDF; 7213a634bfcSVikram Hegde return (immu_devi); 7223a634bfcSVikram Hegde } 7233a634bfcSVikram Hegde 7243a634bfcSVikram Hegde immu_devi->imd_devfunc = IMMU_PCI_DEVFUNC(dev, func); 7253a634bfcSVikram Hegde immu_devi->imd_sec = 0; 7263a634bfcSVikram Hegde immu_devi->imd_sub = 0; 7273a634bfcSVikram Hegde 7283a634bfcSVikram Hegde revclass = pci_getl_func(bus, dev, func, PCI_CONF_REVID); 7293a634bfcSVikram Hegde 7303a634bfcSVikram Hegde classcode = IMMU_PCI_REV2CLASS(revclass); 7313a634bfcSVikram Hegde baseclass = IMMU_PCI_CLASS2BASE(classcode); 7323a634bfcSVikram Hegde subclass = IMMU_PCI_CLASS2SUB(classcode); 7333a634bfcSVikram Hegde 7343a634bfcSVikram Hegde if (baseclass == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) { 7353a634bfcSVikram Hegde 7363a634bfcSVikram Hegde immu_devi->imd_sec = pci_getb_func(bus, dev, func, 7373a634bfcSVikram Hegde PCI_BCNF_SECBUS); 7383a634bfcSVikram Hegde immu_devi->imd_sub = pci_getb_func(bus, dev, func, 7393a634bfcSVikram Hegde PCI_BCNF_SUBBUS); 7403a634bfcSVikram Hegde 7413a634bfcSVikram Hegde pciex = device_is_pciex(bus, dev, func, &is_pcib); 7423a634bfcSVikram Hegde if (pciex == B_TRUE && is_pcib == B_TRUE) { 7433a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_PCIE_PCI; 7443a634bfcSVikram Hegde } else if (pciex == B_TRUE) { 7453a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_PCIE_PCIE; 7463a634bfcSVikram Hegde } else { 7473a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_PCI_PCI; 7483a634bfcSVikram Hegde } 7493a634bfcSVikram Hegde } else { 7503a634bfcSVikram Hegde immu_devi->imd_pcib_type = IMMU_PCIB_ENDPOINT; 7513a634bfcSVikram Hegde } 7523a634bfcSVikram Hegde 7533a634bfcSVikram Hegde /* check for certain special devices */ 7543a634bfcSVikram Hegde immu_devi->imd_display = device_is_display(classcode); 7553a634bfcSVikram Hegde immu_devi->imd_lpc = ((baseclass == PCI_CLASS_BRIDGE) && 7563a634bfcSVikram Hegde (subclass == PCI_BRIDGE_ISA)) ? B_TRUE : B_FALSE; 75750200e77SFrank Van Der Linden immu_devi->imd_use_premap = device_use_premap(classcode); 7583a634bfcSVikram Hegde 7593a634bfcSVikram Hegde immu_devi->imd_domain = NULL; 7603a634bfcSVikram Hegde 7619e986f0eSFrank Van Der Linden immu_devi->imd_dvma_flags = immu_global_dvma_flags; 7629e986f0eSFrank Van Der Linden 7633a634bfcSVikram Hegde return (immu_devi); 7643a634bfcSVikram Hegde } 7653a634bfcSVikram Hegde 7663a634bfcSVikram Hegde static void 7673a634bfcSVikram Hegde destroy_immu_devi(immu_devi_t *immu_devi) 7683a634bfcSVikram Hegde { 7693a634bfcSVikram Hegde kmem_free(immu_devi, sizeof (immu_devi_t)); 7703a634bfcSVikram Hegde } 7713a634bfcSVikram Hegde 7723a634bfcSVikram Hegde static domain_t * 7733a634bfcSVikram Hegde immu_devi_domain(dev_info_t *rdip, dev_info_t **ddipp) 7743a634bfcSVikram Hegde { 7753a634bfcSVikram Hegde immu_devi_t *immu_devi; 7763a634bfcSVikram Hegde domain_t *domain; 7773a634bfcSVikram Hegde dev_info_t *ddip; 7783a634bfcSVikram Hegde 7793a634bfcSVikram Hegde *ddipp = NULL; 7803a634bfcSVikram Hegde 7813a634bfcSVikram Hegde immu_devi = immu_devi_get(rdip); 7823a634bfcSVikram Hegde if (immu_devi == NULL) { 7833a634bfcSVikram Hegde return (NULL); 7843a634bfcSVikram Hegde } 7853a634bfcSVikram Hegde 7863a634bfcSVikram Hegde mutex_enter(&(DEVI(rdip)->devi_lock)); 7873a634bfcSVikram Hegde domain = immu_devi->imd_domain; 7883a634bfcSVikram Hegde ddip = immu_devi->imd_ddip; 7893a634bfcSVikram Hegde mutex_exit(&(DEVI(rdip)->devi_lock)); 7903a634bfcSVikram Hegde 79150200e77SFrank Van Der Linden if (domain) 7923a634bfcSVikram Hegde *ddipp = ddip; 7933a634bfcSVikram Hegde 7943a634bfcSVikram Hegde return (domain); 7953a634bfcSVikram Hegde 7963a634bfcSVikram Hegde } 7973a634bfcSVikram Hegde 7983a634bfcSVikram Hegde /* ############################# END IMMU_DEVI code ######################## */ 7993a634bfcSVikram Hegde /* ############################# DOMAIN code ############################### */ 8003a634bfcSVikram Hegde 8013a634bfcSVikram Hegde /* 8023a634bfcSVikram Hegde * This routine always succeeds 8033a634bfcSVikram Hegde */ 8043a634bfcSVikram Hegde static int 8053a634bfcSVikram Hegde did_alloc(immu_t *immu, dev_info_t *rdip, 8063a634bfcSVikram Hegde dev_info_t *ddip, immu_flags_t immu_flags) 8073a634bfcSVikram Hegde { 8083a634bfcSVikram Hegde int did; 8093a634bfcSVikram Hegde 8103a634bfcSVikram Hegde did = (uintptr_t)vmem_alloc(immu->immu_did_arena, 1, 8113a634bfcSVikram Hegde (immu_flags & IMMU_FLAGS_NOSLEEP) ? VM_NOSLEEP : VM_SLEEP); 8123a634bfcSVikram Hegde 8133a634bfcSVikram Hegde if (did == 0) { 8143a634bfcSVikram Hegde ddi_err(DER_WARN, rdip, "device domain-id alloc error" 8153a634bfcSVikram Hegde " domain-device: %s%d. immu unit is %s. Using " 8163a634bfcSVikram Hegde "unity domain with domain-id (%d)", 8173a634bfcSVikram Hegde ddi_driver_name(ddip), ddi_get_instance(ddip), 8183a634bfcSVikram Hegde immu->immu_name, immu->immu_unity_domain->dom_did); 8193a634bfcSVikram Hegde did = immu->immu_unity_domain->dom_did; 8203a634bfcSVikram Hegde } 8213a634bfcSVikram Hegde 8223a634bfcSVikram Hegde return (did); 8233a634bfcSVikram Hegde } 8243a634bfcSVikram Hegde 8253a634bfcSVikram Hegde static int 8263a634bfcSVikram Hegde get_branch_domain(dev_info_t *pdip, void *arg) 8273a634bfcSVikram Hegde { 8283a634bfcSVikram Hegde immu_devi_t *immu_devi; 8293a634bfcSVikram Hegde domain_t *domain; 8303a634bfcSVikram Hegde dev_info_t *ddip; 8313a634bfcSVikram Hegde immu_t *immu; 8323a634bfcSVikram Hegde dvma_arg_t *dvp = (dvma_arg_t *)arg; 8333a634bfcSVikram Hegde 8343a634bfcSVikram Hegde /* 8353a634bfcSVikram Hegde * The field dvp->dva_rdip is a work-in-progress 8363a634bfcSVikram Hegde * and gets updated as we walk up the ancestor 8373a634bfcSVikram Hegde * tree. The final ddip is set only when we reach 8383a634bfcSVikram Hegde * the top of the tree. So the dvp->dva_ddip field cannot 8393a634bfcSVikram Hegde * be relied on until we reach the top of the field. 8403a634bfcSVikram Hegde */ 8413a634bfcSVikram Hegde 8423a634bfcSVikram Hegde /* immu_devi may not be set. */ 8433a634bfcSVikram Hegde immu_devi = immu_devi_get(pdip); 8443a634bfcSVikram Hegde if (immu_devi == NULL) { 8453a634bfcSVikram Hegde if (immu_devi_set(pdip, dvp->dva_flags) != DDI_SUCCESS) { 8463a634bfcSVikram Hegde dvp->dva_error = DDI_FAILURE; 8473a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 8483a634bfcSVikram Hegde } 8493a634bfcSVikram Hegde } 8503a634bfcSVikram Hegde 8513a634bfcSVikram Hegde immu_devi = immu_devi_get(pdip); 8523a634bfcSVikram Hegde immu = immu_devi->imd_immu; 85350200e77SFrank Van Der Linden if (immu == NULL) 8543a634bfcSVikram Hegde immu = immu_dvma_get_immu(pdip, dvp->dva_flags); 8553a634bfcSVikram Hegde 8563a634bfcSVikram Hegde /* 8573a634bfcSVikram Hegde * If we encounter a PCIE_PCIE bridge *ANCESTOR* we need to 8583a634bfcSVikram Hegde * terminate the walk (since the device under the PCIE bridge 8593a634bfcSVikram Hegde * is a PCIE device and has an independent entry in the 8603a634bfcSVikram Hegde * root/context table) 8613a634bfcSVikram Hegde */ 8623a634bfcSVikram Hegde if (dvp->dva_rdip != pdip && 8633a634bfcSVikram Hegde immu_devi->imd_pcib_type == IMMU_PCIB_PCIE_PCIE) { 8643a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 8653a634bfcSVikram Hegde } 8663a634bfcSVikram Hegde 8673a634bfcSVikram Hegde /* 8683a634bfcSVikram Hegde * In order to be a domain-dim, it must be a PCI device i.e. 8693a634bfcSVikram Hegde * must have valid BDF. This also eliminates the root complex. 8703a634bfcSVikram Hegde */ 8713a634bfcSVikram Hegde if (immu_devi->imd_pcib_type != IMMU_PCIB_BAD && 8723a634bfcSVikram Hegde immu_devi->imd_pcib_type != IMMU_PCIB_NOBDF) { 8733a634bfcSVikram Hegde ASSERT(immu_devi->imd_bus >= 0); 8743a634bfcSVikram Hegde ASSERT(immu_devi->imd_devfunc >= 0); 8753a634bfcSVikram Hegde dvp->dva_ddip = pdip; 8763a634bfcSVikram Hegde } 8773a634bfcSVikram Hegde 8783a634bfcSVikram Hegde if (immu_devi->imd_display == B_TRUE || 8793a634bfcSVikram Hegde (dvp->dva_flags & IMMU_FLAGS_UNITY)) { 8803a634bfcSVikram Hegde dvp->dva_domain = immu->immu_unity_domain; 8813a634bfcSVikram Hegde /* continue walking to find ddip */ 8823a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 8833a634bfcSVikram Hegde } 8843a634bfcSVikram Hegde 8853a634bfcSVikram Hegde mutex_enter(&(DEVI(pdip)->devi_lock)); 8863a634bfcSVikram Hegde domain = immu_devi->imd_domain; 8873a634bfcSVikram Hegde ddip = immu_devi->imd_ddip; 8883a634bfcSVikram Hegde mutex_exit(&(DEVI(pdip)->devi_lock)); 8893a634bfcSVikram Hegde 8903a634bfcSVikram Hegde if (domain && ddip) { 8913a634bfcSVikram Hegde /* if domain is set, it must be the same */ 8923a634bfcSVikram Hegde if (dvp->dva_domain) { 8933a634bfcSVikram Hegde ASSERT(domain == dvp->dva_domain); 8943a634bfcSVikram Hegde } 8953a634bfcSVikram Hegde dvp->dva_domain = domain; 8963a634bfcSVikram Hegde dvp->dva_ddip = ddip; 8973a634bfcSVikram Hegde return (DDI_WALK_TERMINATE); 8983a634bfcSVikram Hegde } 8993a634bfcSVikram Hegde 9003a634bfcSVikram Hegde /* Domain may already be set, continue walking so that ddip gets set */ 9013a634bfcSVikram Hegde if (dvp->dva_domain) { 9023a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 9033a634bfcSVikram Hegde } 9043a634bfcSVikram Hegde 9053a634bfcSVikram Hegde /* domain is not set in either immu_devi or dvp */ 9063a634bfcSVikram Hegde domain = bdf_domain_lookup(immu_devi); 9073a634bfcSVikram Hegde if (domain == NULL) { 9083a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 9093a634bfcSVikram Hegde } 9103a634bfcSVikram Hegde 9113a634bfcSVikram Hegde /* ok, the BDF hash had a domain for this BDF. */ 9123a634bfcSVikram Hegde 9133a634bfcSVikram Hegde /* Grab lock again to check if something else set immu_devi fields */ 9143a634bfcSVikram Hegde mutex_enter(&(DEVI(pdip)->devi_lock)); 9153a634bfcSVikram Hegde if (immu_devi->imd_domain != NULL) { 9163a634bfcSVikram Hegde dvp->dva_domain = domain; 9173a634bfcSVikram Hegde } else { 9183a634bfcSVikram Hegde dvp->dva_domain = domain; 9193a634bfcSVikram Hegde } 9203a634bfcSVikram Hegde mutex_exit(&(DEVI(pdip)->devi_lock)); 9213a634bfcSVikram Hegde 9223a634bfcSVikram Hegde /* 9233a634bfcSVikram Hegde * walk upwards until the topmost PCI bridge is found 9243a634bfcSVikram Hegde */ 9253a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 926e03dceedSVikram Hegde 9273a634bfcSVikram Hegde } 9283a634bfcSVikram Hegde 9293a634bfcSVikram Hegde static void 9303a634bfcSVikram Hegde map_unity_domain(domain_t *domain) 9313a634bfcSVikram Hegde { 9323a634bfcSVikram Hegde struct memlist *mp; 9333a634bfcSVikram Hegde uint64_t start; 9343a634bfcSVikram Hegde uint64_t npages; 93550200e77SFrank Van Der Linden immu_dcookie_t dcookies[1] = {0}; 936e03dceedSVikram Hegde int dcount = 0; 9373a634bfcSVikram Hegde 9383a634bfcSVikram Hegde /* 9393a634bfcSVikram Hegde * UNITY arenas are a mirror of the physical memory 9403a634bfcSVikram Hegde * installed on the system. 9413a634bfcSVikram Hegde */ 9423a634bfcSVikram Hegde 9433a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS 9443a634bfcSVikram Hegde /* 9453a634bfcSVikram Hegde * Dont skip page0. Some broken HW/FW access it. 9463a634bfcSVikram Hegde */ 947e03dceedSVikram Hegde dcookies[0].dck_paddr = 0; 948e03dceedSVikram Hegde dcookies[0].dck_npages = 1; 949e03dceedSVikram Hegde dcount = 1; 95050200e77SFrank Van Der Linden (void) dvma_map(domain, 0, 1, dcookies, dcount, NULL, 9513a634bfcSVikram Hegde IMMU_FLAGS_READ | IMMU_FLAGS_WRITE | IMMU_FLAGS_PAGE1); 9523a634bfcSVikram Hegde #endif 9533a634bfcSVikram Hegde 9543a634bfcSVikram Hegde memlist_read_lock(); 9553a634bfcSVikram Hegde 9563a634bfcSVikram Hegde mp = phys_install; 9573a634bfcSVikram Hegde 9583a634bfcSVikram Hegde if (mp->ml_address == 0) { 9593a634bfcSVikram Hegde /* since we already mapped page1 above */ 9603a634bfcSVikram Hegde start = IMMU_PAGESIZE; 9613a634bfcSVikram Hegde } else { 9623a634bfcSVikram Hegde start = mp->ml_address; 9633a634bfcSVikram Hegde } 9643a634bfcSVikram Hegde npages = mp->ml_size/IMMU_PAGESIZE + 1; 9653a634bfcSVikram Hegde 966e03dceedSVikram Hegde dcookies[0].dck_paddr = start; 967e03dceedSVikram Hegde dcookies[0].dck_npages = npages; 968e03dceedSVikram Hegde dcount = 1; 96950200e77SFrank Van Der Linden (void) dvma_map(domain, start, npages, dcookies, 970e03dceedSVikram Hegde dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 9713a634bfcSVikram Hegde 97250200e77SFrank Van Der Linden ddi_err(DER_LOG, domain->dom_dip, "iommu: mapping PHYS span [0x%" PRIx64 9733a634bfcSVikram Hegde " - 0x%" PRIx64 "]", start, start + mp->ml_size); 9743a634bfcSVikram Hegde 9753a634bfcSVikram Hegde mp = mp->ml_next; 9763a634bfcSVikram Hegde while (mp) { 97750200e77SFrank Van Der Linden ddi_err(DER_LOG, domain->dom_dip, 97850200e77SFrank Van Der Linden "iommu: mapping PHYS span [0x%" PRIx64 " - 0x%" PRIx64 "]", 97950200e77SFrank Van Der Linden mp->ml_address, mp->ml_address + mp->ml_size); 9803a634bfcSVikram Hegde 9813a634bfcSVikram Hegde start = mp->ml_address; 9823a634bfcSVikram Hegde npages = mp->ml_size/IMMU_PAGESIZE + 1; 9833a634bfcSVikram Hegde 984e03dceedSVikram Hegde dcookies[0].dck_paddr = start; 985e03dceedSVikram Hegde dcookies[0].dck_npages = npages; 986e03dceedSVikram Hegde dcount = 1; 98750200e77SFrank Van Der Linden (void) dvma_map(domain, start, npages, 988e03dceedSVikram Hegde dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 9893a634bfcSVikram Hegde mp = mp->ml_next; 9903a634bfcSVikram Hegde } 9913a634bfcSVikram Hegde 9923a634bfcSVikram Hegde mp = bios_rsvd; 9933a634bfcSVikram Hegde while (mp) { 99450200e77SFrank Van Der Linden ddi_err(DER_LOG, domain->dom_dip, 99550200e77SFrank Van Der Linden "iommu: mapping PHYS span [0x%" PRIx64 " - 0x%" PRIx64 "]", 99650200e77SFrank Van Der Linden mp->ml_address, mp->ml_address + mp->ml_size); 9973a634bfcSVikram Hegde 9983a634bfcSVikram Hegde start = mp->ml_address; 9993a634bfcSVikram Hegde npages = mp->ml_size/IMMU_PAGESIZE + 1; 10003a634bfcSVikram Hegde 1001e03dceedSVikram Hegde dcookies[0].dck_paddr = start; 1002e03dceedSVikram Hegde dcookies[0].dck_npages = npages; 1003e03dceedSVikram Hegde dcount = 1; 100450200e77SFrank Van Der Linden (void) dvma_map(domain, start, npages, 1005e03dceedSVikram Hegde dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 10063a634bfcSVikram Hegde 10073a634bfcSVikram Hegde mp = mp->ml_next; 10083a634bfcSVikram Hegde } 10093a634bfcSVikram Hegde 10103a634bfcSVikram Hegde memlist_read_unlock(); 10113a634bfcSVikram Hegde } 10123a634bfcSVikram Hegde 10133a634bfcSVikram Hegde /* 10143a634bfcSVikram Hegde * create_xlate_arena() 10153a634bfcSVikram Hegde * Create the dvma arena for a domain with translation 10163a634bfcSVikram Hegde * mapping 10173a634bfcSVikram Hegde */ 10183a634bfcSVikram Hegde static void 10193a634bfcSVikram Hegde create_xlate_arena(immu_t *immu, domain_t *domain, 10203a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 10213a634bfcSVikram Hegde { 10223a634bfcSVikram Hegde char *arena_name; 10233a634bfcSVikram Hegde struct memlist *mp; 10243a634bfcSVikram Hegde int vmem_flags; 10253a634bfcSVikram Hegde uint64_t start; 10263a634bfcSVikram Hegde uint_t mgaw; 10273a634bfcSVikram Hegde uint64_t size; 10283a634bfcSVikram Hegde uint64_t maxaddr; 10293a634bfcSVikram Hegde void *vmem_ret; 10303a634bfcSVikram Hegde 10313a634bfcSVikram Hegde arena_name = domain->dom_dvma_arena_name; 10323a634bfcSVikram Hegde 10333a634bfcSVikram Hegde /* Note, don't do sizeof (arena_name) - it is just a pointer */ 10343a634bfcSVikram Hegde (void) snprintf(arena_name, 10353a634bfcSVikram Hegde sizeof (domain->dom_dvma_arena_name), 10363a634bfcSVikram Hegde "%s-domain-%d-xlate-DVMA-arena", immu->immu_name, 10373a634bfcSVikram Hegde domain->dom_did); 10383a634bfcSVikram Hegde 10393a634bfcSVikram Hegde vmem_flags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? VM_NOSLEEP : VM_SLEEP; 10403a634bfcSVikram Hegde 10413a634bfcSVikram Hegde /* Restrict mgaddr (max guest addr) to MGAW */ 10423a634bfcSVikram Hegde mgaw = IMMU_CAP_MGAW(immu->immu_regs_cap); 10433a634bfcSVikram Hegde 10443a634bfcSVikram Hegde /* 10453a634bfcSVikram Hegde * To ensure we avoid ioapic and PCI MMIO ranges we just 10463a634bfcSVikram Hegde * use the physical memory address range of the system as the 10473a634bfcSVikram Hegde * range 10483a634bfcSVikram Hegde */ 10493a634bfcSVikram Hegde maxaddr = ((uint64_t)1 << mgaw); 10503a634bfcSVikram Hegde 10513a634bfcSVikram Hegde memlist_read_lock(); 10523a634bfcSVikram Hegde 10533a634bfcSVikram Hegde mp = phys_install; 10543a634bfcSVikram Hegde 10553a634bfcSVikram Hegde if (mp->ml_address == 0) 10563a634bfcSVikram Hegde start = MMU_PAGESIZE; 10573a634bfcSVikram Hegde else 10583a634bfcSVikram Hegde start = mp->ml_address; 10593a634bfcSVikram Hegde 10603a634bfcSVikram Hegde if (start + mp->ml_size > maxaddr) 10613a634bfcSVikram Hegde size = maxaddr - start; 10623a634bfcSVikram Hegde else 10633a634bfcSVikram Hegde size = mp->ml_size; 10643a634bfcSVikram Hegde 10653a634bfcSVikram Hegde ddi_err(DER_VERB, rdip, 106650200e77SFrank Van Der Linden "iommu: %s: Creating dvma vmem arena [0x%" PRIx64 10673a634bfcSVikram Hegde " - 0x%" PRIx64 "]", arena_name, start, start + size); 10683a634bfcSVikram Hegde 10693a634bfcSVikram Hegde /* 10703a634bfcSVikram Hegde * We always allocate in quanta of IMMU_PAGESIZE 10713a634bfcSVikram Hegde */ 10723a634bfcSVikram Hegde domain->dom_dvma_arena = vmem_create(arena_name, 10733a634bfcSVikram Hegde (void *)(uintptr_t)start, /* start addr */ 10743a634bfcSVikram Hegde size, /* size */ 10753a634bfcSVikram Hegde IMMU_PAGESIZE, /* quantum */ 10763a634bfcSVikram Hegde NULL, /* afunc */ 10773a634bfcSVikram Hegde NULL, /* ffunc */ 10783a634bfcSVikram Hegde NULL, /* source */ 10793a634bfcSVikram Hegde 0, /* qcache_max */ 10803a634bfcSVikram Hegde vmem_flags); 10813a634bfcSVikram Hegde 10823a634bfcSVikram Hegde if (domain->dom_dvma_arena == NULL) { 10833a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, 10843a634bfcSVikram Hegde "Failed to allocate DVMA arena(%s) " 10853a634bfcSVikram Hegde "for domain ID (%d)", arena_name, domain->dom_did); 10863a634bfcSVikram Hegde /*NOTREACHED*/ 10873a634bfcSVikram Hegde } 10883a634bfcSVikram Hegde 10893a634bfcSVikram Hegde mp = mp->ml_next; 10903a634bfcSVikram Hegde while (mp) { 10913a634bfcSVikram Hegde 10923a634bfcSVikram Hegde if (mp->ml_address == 0) 10933a634bfcSVikram Hegde start = MMU_PAGESIZE; 10943a634bfcSVikram Hegde else 10953a634bfcSVikram Hegde start = mp->ml_address; 10963a634bfcSVikram Hegde 10973a634bfcSVikram Hegde if (start + mp->ml_size > maxaddr) 10983a634bfcSVikram Hegde size = maxaddr - start; 10993a634bfcSVikram Hegde else 11003a634bfcSVikram Hegde size = mp->ml_size; 11013a634bfcSVikram Hegde 11023a634bfcSVikram Hegde ddi_err(DER_VERB, rdip, 110350200e77SFrank Van Der Linden "iommu: %s: Adding dvma vmem span [0x%" PRIx64 11043a634bfcSVikram Hegde " - 0x%" PRIx64 "]", arena_name, start, 11053a634bfcSVikram Hegde start + size); 11063a634bfcSVikram Hegde 11073a634bfcSVikram Hegde vmem_ret = vmem_add(domain->dom_dvma_arena, 11083a634bfcSVikram Hegde (void *)(uintptr_t)start, size, vmem_flags); 11093a634bfcSVikram Hegde 11103a634bfcSVikram Hegde if (vmem_ret == NULL) { 11113a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, 11123a634bfcSVikram Hegde "Failed to allocate DVMA arena(%s) " 11133a634bfcSVikram Hegde "for domain ID (%d)", 11143a634bfcSVikram Hegde arena_name, domain->dom_did); 11153a634bfcSVikram Hegde /*NOTREACHED*/ 11163a634bfcSVikram Hegde } 11173a634bfcSVikram Hegde mp = mp->ml_next; 11183a634bfcSVikram Hegde } 11193a634bfcSVikram Hegde memlist_read_unlock(); 11203a634bfcSVikram Hegde } 11213a634bfcSVikram Hegde 11223a634bfcSVikram Hegde /* ################################### DOMAIN CODE ######################### */ 11233a634bfcSVikram Hegde 11243a634bfcSVikram Hegde /* 11253a634bfcSVikram Hegde * Set the domain and domain-dip for a dip 11263a634bfcSVikram Hegde */ 11273a634bfcSVikram Hegde static void 11283a634bfcSVikram Hegde set_domain( 11293a634bfcSVikram Hegde dev_info_t *dip, 11303a634bfcSVikram Hegde dev_info_t *ddip, 11313a634bfcSVikram Hegde domain_t *domain) 11323a634bfcSVikram Hegde { 11333a634bfcSVikram Hegde immu_devi_t *immu_devi; 11343a634bfcSVikram Hegde domain_t *fdomain; 11353a634bfcSVikram Hegde dev_info_t *fddip; 11363a634bfcSVikram Hegde 11373a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 11383a634bfcSVikram Hegde 11393a634bfcSVikram Hegde mutex_enter(&(DEVI(dip)->devi_lock)); 11403a634bfcSVikram Hegde fddip = immu_devi->imd_ddip; 11413a634bfcSVikram Hegde fdomain = immu_devi->imd_domain; 11423a634bfcSVikram Hegde 11433a634bfcSVikram Hegde if (fddip) { 11443a634bfcSVikram Hegde ASSERT(fddip == ddip); 11453a634bfcSVikram Hegde } else { 11463a634bfcSVikram Hegde immu_devi->imd_ddip = ddip; 11473a634bfcSVikram Hegde } 11483a634bfcSVikram Hegde 11493a634bfcSVikram Hegde if (fdomain) { 11503a634bfcSVikram Hegde ASSERT(fdomain == domain); 11513a634bfcSVikram Hegde } else { 11523a634bfcSVikram Hegde immu_devi->imd_domain = domain; 11533a634bfcSVikram Hegde } 11543a634bfcSVikram Hegde mutex_exit(&(DEVI(dip)->devi_lock)); 11553a634bfcSVikram Hegde } 11563a634bfcSVikram Hegde 11573a634bfcSVikram Hegde /* 11583a634bfcSVikram Hegde * device_domain() 11593a634bfcSVikram Hegde * Get domain for a device. The domain may be global in which case it 11603a634bfcSVikram Hegde * is shared between all IOMMU units. Due to potential AGAW differences 11613a634bfcSVikram Hegde * between IOMMU units, such global domains *have to be* UNITY mapping 11623a634bfcSVikram Hegde * domains. Alternatively, the domain may be local to a IOMMU unit. 11633a634bfcSVikram Hegde * Local domains may be shared or immu_devi, although the 11643a634bfcSVikram Hegde * scope of sharing 11653a634bfcSVikram Hegde * is restricted to devices controlled by the IOMMU unit to 11663a634bfcSVikram Hegde * which the domain 11673a634bfcSVikram Hegde * belongs. If shared, they (currently) have to be UNITY domains. If 11683a634bfcSVikram Hegde * immu_devi a domain may be either UNITY or translation (XLATE) domain. 11693a634bfcSVikram Hegde */ 11703a634bfcSVikram Hegde static domain_t * 11713a634bfcSVikram Hegde device_domain(dev_info_t *rdip, dev_info_t **ddipp, immu_flags_t immu_flags) 11723a634bfcSVikram Hegde { 11733a634bfcSVikram Hegde dev_info_t *ddip; /* topmost dip in domain i.e. domain owner */ 11743a634bfcSVikram Hegde immu_t *immu; 11753a634bfcSVikram Hegde domain_t *domain; 11763a634bfcSVikram Hegde dvma_arg_t dvarg = {0}; 11773a634bfcSVikram Hegde int level; 11783a634bfcSVikram Hegde 11793a634bfcSVikram Hegde *ddipp = NULL; 11803a634bfcSVikram Hegde 11813a634bfcSVikram Hegde /* 11823a634bfcSVikram Hegde * Check if the domain is already set. This is usually true 11833a634bfcSVikram Hegde * if this is not the first DVMA transaction. 11843a634bfcSVikram Hegde */ 11853a634bfcSVikram Hegde ddip = NULL; 11863a634bfcSVikram Hegde domain = immu_devi_domain(rdip, &ddip); 11873a634bfcSVikram Hegde if (domain) { 11883a634bfcSVikram Hegde *ddipp = ddip; 11893a634bfcSVikram Hegde return (domain); 11903a634bfcSVikram Hegde } 11913a634bfcSVikram Hegde 11923a634bfcSVikram Hegde immu = immu_dvma_get_immu(rdip, immu_flags); 11933a634bfcSVikram Hegde if (immu == NULL) { 11943a634bfcSVikram Hegde /* 11953a634bfcSVikram Hegde * possible that there is no IOMMU unit for this device 11963a634bfcSVikram Hegde * - BIOS bugs are one example. 11973a634bfcSVikram Hegde */ 119850200e77SFrank Van Der Linden ddi_err(DER_WARN, rdip, "No iommu unit found for device"); 11993a634bfcSVikram Hegde return (NULL); 12003a634bfcSVikram Hegde } 12013a634bfcSVikram Hegde 12029e986f0eSFrank Van Der Linden immu_flags |= immu_devi_get(rdip)->imd_dvma_flags; 12039e986f0eSFrank Van Der Linden 1204e03dceedSVikram Hegde dvarg.dva_rdip = rdip; 12053a634bfcSVikram Hegde dvarg.dva_ddip = NULL; 12063a634bfcSVikram Hegde dvarg.dva_domain = NULL; 12073a634bfcSVikram Hegde dvarg.dva_flags = immu_flags; 12083a634bfcSVikram Hegde level = 0; 1209e03dceedSVikram Hegde if (immu_walk_ancestor(rdip, NULL, get_branch_domain, 12103a634bfcSVikram Hegde &dvarg, &level, immu_flags) != DDI_SUCCESS) { 12113a634bfcSVikram Hegde /* 12123a634bfcSVikram Hegde * maybe low memory. return error, 12133a634bfcSVikram Hegde * so driver tries again later 12143a634bfcSVikram Hegde */ 12153a634bfcSVikram Hegde return (NULL); 12163a634bfcSVikram Hegde } 12173a634bfcSVikram Hegde 12183a634bfcSVikram Hegde /* should have walked at least 1 dip (i.e. edip) */ 12193a634bfcSVikram Hegde ASSERT(level > 0); 12203a634bfcSVikram Hegde 12213a634bfcSVikram Hegde ddip = dvarg.dva_ddip; /* must be present */ 12223a634bfcSVikram Hegde domain = dvarg.dva_domain; /* may be NULL */ 12233a634bfcSVikram Hegde 12243a634bfcSVikram Hegde /* 12253a634bfcSVikram Hegde * We may find the domain during our ancestor walk on any one of our 12263a634bfcSVikram Hegde * ancestor dips, If the domain is found then the domain-dip 12273a634bfcSVikram Hegde * (i.e. ddip) will also be found in the same immu_devi struct. 12283a634bfcSVikram Hegde * The domain-dip is the highest ancestor dip which shares the 12293a634bfcSVikram Hegde * same domain with edip. 12303a634bfcSVikram Hegde * The domain may or may not be found, but the domain dip must 12313a634bfcSVikram Hegde * be found. 12323a634bfcSVikram Hegde */ 12333a634bfcSVikram Hegde if (ddip == NULL) { 1234e03dceedSVikram Hegde ddi_err(DER_MODE, rdip, "Cannot find domain dip for device."); 12353a634bfcSVikram Hegde return (NULL); 12363a634bfcSVikram Hegde } 12373a634bfcSVikram Hegde 12383a634bfcSVikram Hegde /* 12393a634bfcSVikram Hegde * Did we find a domain ? 12403a634bfcSVikram Hegde */ 12413a634bfcSVikram Hegde if (domain) { 12423a634bfcSVikram Hegde goto found; 12433a634bfcSVikram Hegde } 12443a634bfcSVikram Hegde 12453a634bfcSVikram Hegde /* nope, so allocate */ 12463a634bfcSVikram Hegde domain = domain_create(immu, ddip, rdip, immu_flags); 12473a634bfcSVikram Hegde if (domain == NULL) { 12483a634bfcSVikram Hegde return (NULL); 12493a634bfcSVikram Hegde } 12503a634bfcSVikram Hegde 12513a634bfcSVikram Hegde /*FALLTHROUGH*/ 12523a634bfcSVikram Hegde found: 12533a634bfcSVikram Hegde /* 12543a634bfcSVikram Hegde * We know *domain *is* the right domain, so panic if 12553a634bfcSVikram Hegde * another domain is set for either the request-dip or 12563a634bfcSVikram Hegde * effective dip. 12573a634bfcSVikram Hegde */ 12583a634bfcSVikram Hegde set_domain(ddip, ddip, domain); 12593a634bfcSVikram Hegde set_domain(rdip, ddip, domain); 12603a634bfcSVikram Hegde 12613a634bfcSVikram Hegde *ddipp = ddip; 12623a634bfcSVikram Hegde return (domain); 12633a634bfcSVikram Hegde } 12643a634bfcSVikram Hegde 12653a634bfcSVikram Hegde static void 12663a634bfcSVikram Hegde create_unity_domain(immu_t *immu) 12673a634bfcSVikram Hegde { 12683a634bfcSVikram Hegde domain_t *domain; 12693a634bfcSVikram Hegde 12703a634bfcSVikram Hegde /* domain created during boot and always use sleep flag */ 12713a634bfcSVikram Hegde domain = kmem_zalloc(sizeof (domain_t), KM_SLEEP); 12723a634bfcSVikram Hegde 12733a634bfcSVikram Hegde rw_init(&(domain->dom_pgtable_rwlock), NULL, RW_DEFAULT, NULL); 12743a634bfcSVikram Hegde 12753a634bfcSVikram Hegde domain->dom_did = IMMU_UNITY_DID; 12763a634bfcSVikram Hegde domain->dom_maptype = IMMU_MAPTYPE_UNITY; 12773a634bfcSVikram Hegde 12783a634bfcSVikram Hegde domain->dom_immu = immu; 12793a634bfcSVikram Hegde immu->immu_unity_domain = domain; 12803a634bfcSVikram Hegde 12813a634bfcSVikram Hegde /* 12823a634bfcSVikram Hegde * Setup the domain's initial page table 12833a634bfcSVikram Hegde * should never fail. 12843a634bfcSVikram Hegde */ 1285e03dceedSVikram Hegde domain->dom_pgtable_root = pgtable_alloc(immu, IMMU_FLAGS_SLEEP); 128650200e77SFrank Van Der Linden pgtable_zero(domain->dom_pgtable_root); 12873a634bfcSVikram Hegde 1288be56ae36SFrank Van Der Linden /* 1289be56ae36SFrank Van Der Linden * Only map all physical memory in to the unity domain 1290be56ae36SFrank Van Der Linden * if passthrough is not supported. If it is supported, 1291be56ae36SFrank Van Der Linden * passthrough is set in the context entry instead. 1292be56ae36SFrank Van Der Linden */ 1293be56ae36SFrank Van Der Linden if (!IMMU_ECAP_GET_PT(immu->immu_regs_excap)) 12943a634bfcSVikram Hegde map_unity_domain(domain); 12953a634bfcSVikram Hegde 1296be56ae36SFrank Van Der Linden 12973a634bfcSVikram Hegde /* 12983a634bfcSVikram Hegde * put it on the system-wide UNITY domain list 12993a634bfcSVikram Hegde */ 13003a634bfcSVikram Hegde mutex_enter(&(immu_domain_lock)); 13013a634bfcSVikram Hegde list_insert_tail(&immu_unity_domain_list, domain); 13023a634bfcSVikram Hegde mutex_exit(&(immu_domain_lock)); 13033a634bfcSVikram Hegde } 13043a634bfcSVikram Hegde 13053a634bfcSVikram Hegde /* 13063a634bfcSVikram Hegde * ddip is the domain-dip - the topmost dip in a domain 13073a634bfcSVikram Hegde * rdip is the requesting-dip - the device which is 13083a634bfcSVikram Hegde * requesting DVMA setup 13093a634bfcSVikram Hegde * if domain is a non-shared domain rdip == ddip 13103a634bfcSVikram Hegde */ 13113a634bfcSVikram Hegde static domain_t * 13123a634bfcSVikram Hegde domain_create(immu_t *immu, dev_info_t *ddip, dev_info_t *rdip, 13133a634bfcSVikram Hegde immu_flags_t immu_flags) 13143a634bfcSVikram Hegde { 13153a634bfcSVikram Hegde int kmflags; 13163a634bfcSVikram Hegde domain_t *domain; 13173a634bfcSVikram Hegde char mod_hash_name[128]; 13183a634bfcSVikram Hegde immu_devi_t *immu_devi; 13193a634bfcSVikram Hegde int did; 132050200e77SFrank Van Der Linden immu_dcookie_t dcookies[1] = {0}; 1321e03dceedSVikram Hegde int dcount = 0; 13223a634bfcSVikram Hegde 13233a634bfcSVikram Hegde immu_devi = immu_devi_get(rdip); 13243a634bfcSVikram Hegde 13253a634bfcSVikram Hegde /* 13263a634bfcSVikram Hegde * First allocate a domainid. 13273a634bfcSVikram Hegde * This routine will never fail, since if we run out 13283a634bfcSVikram Hegde * of domains the unity domain will be allocated. 13293a634bfcSVikram Hegde */ 13303a634bfcSVikram Hegde did = did_alloc(immu, rdip, ddip, immu_flags); 13313a634bfcSVikram Hegde if (did == IMMU_UNITY_DID) { 13323a634bfcSVikram Hegde /* domain overflow */ 13333a634bfcSVikram Hegde ASSERT(immu->immu_unity_domain); 13343a634bfcSVikram Hegde return (immu->immu_unity_domain); 13353a634bfcSVikram Hegde } 13363a634bfcSVikram Hegde 13373a634bfcSVikram Hegde kmflags = (immu_flags & IMMU_FLAGS_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP; 13383a634bfcSVikram Hegde domain = kmem_zalloc(sizeof (domain_t), kmflags); 13393a634bfcSVikram Hegde if (domain == NULL) { 13403a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, "Failed to alloc DVMA domain " 13413a634bfcSVikram Hegde "structure for device. IOMMU unit: %s", immu->immu_name); 13423a634bfcSVikram Hegde /*NOTREACHED*/ 13433a634bfcSVikram Hegde } 13443a634bfcSVikram Hegde 13453a634bfcSVikram Hegde rw_init(&(domain->dom_pgtable_rwlock), NULL, RW_DEFAULT, NULL); 13463a634bfcSVikram Hegde 13473a634bfcSVikram Hegde (void) snprintf(mod_hash_name, sizeof (mod_hash_name), 13483a634bfcSVikram Hegde "immu%s-domain%d-pava-hash", immu->immu_name, did); 13493a634bfcSVikram Hegde 13503a634bfcSVikram Hegde domain->dom_did = did; 13513a634bfcSVikram Hegde domain->dom_immu = immu; 13523a634bfcSVikram Hegde domain->dom_maptype = IMMU_MAPTYPE_XLATE; 135350200e77SFrank Van Der Linden domain->dom_dip = ddip; 13543a634bfcSVikram Hegde 13553a634bfcSVikram Hegde /* 13563a634bfcSVikram Hegde * Create xlate DVMA arena for this domain. 13573a634bfcSVikram Hegde */ 13583a634bfcSVikram Hegde create_xlate_arena(immu, domain, rdip, immu_flags); 13593a634bfcSVikram Hegde 13603a634bfcSVikram Hegde /* 13613a634bfcSVikram Hegde * Setup the domain's initial page table 13623a634bfcSVikram Hegde */ 1363e03dceedSVikram Hegde domain->dom_pgtable_root = pgtable_alloc(immu, immu_flags); 13643a634bfcSVikram Hegde if (domain->dom_pgtable_root == NULL) { 13653a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, "Failed to alloc root " 13663a634bfcSVikram Hegde "pgtable for domain (%d). IOMMU unit: %s", 13673a634bfcSVikram Hegde domain->dom_did, immu->immu_name); 13683a634bfcSVikram Hegde /*NOTREACHED*/ 13693a634bfcSVikram Hegde } 137050200e77SFrank Van Der Linden pgtable_zero(domain->dom_pgtable_root); 13713a634bfcSVikram Hegde 13723a634bfcSVikram Hegde /* 13733a634bfcSVikram Hegde * Since this is a immu unit-specific domain, put it on 13743a634bfcSVikram Hegde * the per-immu domain list. 13753a634bfcSVikram Hegde */ 13763a634bfcSVikram Hegde mutex_enter(&(immu->immu_lock)); 13773a634bfcSVikram Hegde list_insert_head(&immu->immu_domain_list, domain); 13783a634bfcSVikram Hegde mutex_exit(&(immu->immu_lock)); 13793a634bfcSVikram Hegde 13803a634bfcSVikram Hegde /* 13813a634bfcSVikram Hegde * Also put it on the system-wide xlate domain list 13823a634bfcSVikram Hegde */ 13833a634bfcSVikram Hegde mutex_enter(&(immu_domain_lock)); 13843a634bfcSVikram Hegde list_insert_head(&immu_xlate_domain_list, domain); 13853a634bfcSVikram Hegde mutex_exit(&(immu_domain_lock)); 13863a634bfcSVikram Hegde 13873a634bfcSVikram Hegde bdf_domain_insert(immu_devi, domain); 13883a634bfcSVikram Hegde 13893a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS 13903a634bfcSVikram Hegde /* 13913a634bfcSVikram Hegde * Map page0. Some broken HW/FW access it. 13923a634bfcSVikram Hegde */ 1393e03dceedSVikram Hegde dcookies[0].dck_paddr = 0; 1394e03dceedSVikram Hegde dcookies[0].dck_npages = 1; 1395e03dceedSVikram Hegde dcount = 1; 139650200e77SFrank Van Der Linden (void) dvma_map(domain, 0, 1, dcookies, dcount, NULL, 13973a634bfcSVikram Hegde IMMU_FLAGS_READ | IMMU_FLAGS_WRITE | IMMU_FLAGS_PAGE1); 13983a634bfcSVikram Hegde #endif 13993a634bfcSVikram Hegde return (domain); 14003a634bfcSVikram Hegde } 14013a634bfcSVikram Hegde 14023a634bfcSVikram Hegde /* 14033a634bfcSVikram Hegde * Create domainid arena. 14043a634bfcSVikram Hegde * Domainid 0 is reserved by Vt-d spec and cannot be used by 14053a634bfcSVikram Hegde * system software. 14063a634bfcSVikram Hegde * Domainid 1 is reserved by solaris and used for *all* of the following: 14073a634bfcSVikram Hegde * as the "uninitialized" domain - For devices not yet controlled 14083a634bfcSVikram Hegde * by Solaris 14093a634bfcSVikram Hegde * as the "unity" domain - For devices that will always belong 14103a634bfcSVikram Hegde * to the unity domain 14113a634bfcSVikram Hegde * as the "overflow" domain - Used for any new device after we 14123a634bfcSVikram Hegde * run out of domains 14133a634bfcSVikram Hegde * All of the above domains map into a single domain with 14143a634bfcSVikram Hegde * domainid 1 and UNITY DVMA mapping 14153a634bfcSVikram Hegde * Each IMMU unity has its own unity/uninit/overflow domain 14163a634bfcSVikram Hegde */ 14173a634bfcSVikram Hegde static void 14183a634bfcSVikram Hegde did_init(immu_t *immu) 14193a634bfcSVikram Hegde { 14203a634bfcSVikram Hegde (void) snprintf(immu->immu_did_arena_name, 14213a634bfcSVikram Hegde sizeof (immu->immu_did_arena_name), 14223a634bfcSVikram Hegde "%s_domainid_arena", immu->immu_name); 14233a634bfcSVikram Hegde 142450200e77SFrank Van Der Linden ddi_err(DER_VERB, immu->immu_dip, "creating domainid arena %s", 142550200e77SFrank Van Der Linden immu->immu_did_arena_name); 14263a634bfcSVikram Hegde 14273a634bfcSVikram Hegde immu->immu_did_arena = vmem_create( 14283a634bfcSVikram Hegde immu->immu_did_arena_name, 14293a634bfcSVikram Hegde (void *)(uintptr_t)(IMMU_UNITY_DID + 1), /* start addr */ 14303a634bfcSVikram Hegde immu->immu_max_domains - IMMU_UNITY_DID, 14313a634bfcSVikram Hegde 1, /* quantum */ 14323a634bfcSVikram Hegde NULL, /* afunc */ 14333a634bfcSVikram Hegde NULL, /* ffunc */ 14343a634bfcSVikram Hegde NULL, /* source */ 14353a634bfcSVikram Hegde 0, /* qcache_max */ 14363a634bfcSVikram Hegde VM_SLEEP); 14373a634bfcSVikram Hegde 14383a634bfcSVikram Hegde /* Even with SLEEP flag, vmem_create() can fail */ 14393a634bfcSVikram Hegde if (immu->immu_did_arena == NULL) { 14403a634bfcSVikram Hegde ddi_err(DER_PANIC, NULL, "%s: Failed to create Intel " 14413a634bfcSVikram Hegde "IOMMU domainid allocator: %s", immu->immu_name, 14423a634bfcSVikram Hegde immu->immu_did_arena_name); 14433a634bfcSVikram Hegde } 14443a634bfcSVikram Hegde } 14453a634bfcSVikram Hegde 14463a634bfcSVikram Hegde /* ######################### CONTEXT CODE ################################# */ 14473a634bfcSVikram Hegde 14483a634bfcSVikram Hegde static void 14493a634bfcSVikram Hegde context_set(immu_t *immu, domain_t *domain, pgtable_t *root_table, 14503a634bfcSVikram Hegde int bus, int devfunc) 14513a634bfcSVikram Hegde { 14523a634bfcSVikram Hegde pgtable_t *context; 14533a634bfcSVikram Hegde pgtable_t *pgtable_root; 14543a634bfcSVikram Hegde hw_rce_t *hw_rent; 14553a634bfcSVikram Hegde hw_rce_t *hw_cent; 14563a634bfcSVikram Hegde hw_rce_t *ctxp; 1457e03dceedSVikram Hegde int sid; 1458e03dceedSVikram Hegde krw_t rwtype; 1459e03dceedSVikram Hegde boolean_t fill_root; 1460e03dceedSVikram Hegde boolean_t fill_ctx; 14613a634bfcSVikram Hegde 1462e03dceedSVikram Hegde pgtable_root = domain->dom_pgtable_root; 1463e03dceedSVikram Hegde 14643a634bfcSVikram Hegde ctxp = (hw_rce_t *)(root_table->swpg_next_array); 14653a634bfcSVikram Hegde context = *(pgtable_t **)(ctxp + bus); 14663a634bfcSVikram Hegde hw_rent = (hw_rce_t *)(root_table->hwpg_vaddr) + bus; 1467e03dceedSVikram Hegde 1468e03dceedSVikram Hegde fill_root = B_FALSE; 1469e03dceedSVikram Hegde fill_ctx = B_FALSE; 1470e03dceedSVikram Hegde 1471e03dceedSVikram Hegde /* Check the most common case first with reader lock */ 1472e03dceedSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_READER); 1473e03dceedSVikram Hegde rwtype = RW_READER; 1474e03dceedSVikram Hegde again: 14753a634bfcSVikram Hegde if (ROOT_GET_P(hw_rent)) { 1476e03dceedSVikram Hegde hw_cent = (hw_rce_t *)(context->hwpg_vaddr) + devfunc; 1477e03dceedSVikram Hegde if (CONT_GET_AVAIL(hw_cent) == IMMU_CONT_INITED) { 1478e03dceedSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 1479e03dceedSVikram Hegde return; 14803a634bfcSVikram Hegde } else { 1481e03dceedSVikram Hegde fill_ctx = B_TRUE; 1482e03dceedSVikram Hegde } 1483e03dceedSVikram Hegde } else { 1484e03dceedSVikram Hegde fill_root = B_TRUE; 1485e03dceedSVikram Hegde fill_ctx = B_TRUE; 1486e03dceedSVikram Hegde } 1487e03dceedSVikram Hegde 1488e03dceedSVikram Hegde if (rwtype == RW_READER && 1489e03dceedSVikram Hegde rw_tryupgrade(&(immu->immu_ctx_rwlock)) == 0) { 1490e03dceedSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 1491e03dceedSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 1492e03dceedSVikram Hegde rwtype = RW_WRITER; 1493e03dceedSVikram Hegde goto again; 1494e03dceedSVikram Hegde } 1495e03dceedSVikram Hegde rwtype = RW_WRITER; 1496e03dceedSVikram Hegde 1497e03dceedSVikram Hegde if (fill_root == B_TRUE) { 14983a634bfcSVikram Hegde ROOT_SET_CONT(hw_rent, context->hwpg_paddr); 14993a634bfcSVikram Hegde ROOT_SET_P(hw_rent); 15003a634bfcSVikram Hegde immu_regs_cpu_flush(immu, (caddr_t)hw_rent, sizeof (hw_rce_t)); 15013a634bfcSVikram Hegde } 15023a634bfcSVikram Hegde 1503e03dceedSVikram Hegde if (fill_ctx == B_TRUE) { 1504e03dceedSVikram Hegde hw_cent = (hw_rce_t *)(context->hwpg_vaddr) + devfunc; 15053a634bfcSVikram Hegde /* need to disable context entry before reprogramming it */ 15063a634bfcSVikram Hegde bzero(hw_cent, sizeof (hw_rce_t)); 15073a634bfcSVikram Hegde 15083a634bfcSVikram Hegde /* flush caches */ 15093a634bfcSVikram Hegde immu_regs_cpu_flush(immu, (caddr_t)hw_cent, sizeof (hw_rce_t)); 1510e03dceedSVikram Hegde 1511e03dceedSVikram Hegde sid = ((bus << 8) | devfunc); 151250200e77SFrank Van Der Linden immu_flush_context_fsi(immu, 0, sid, domain->dom_did, 151350200e77SFrank Van Der Linden &immu->immu_ctx_inv_wait); 15143a634bfcSVikram Hegde 15153a634bfcSVikram Hegde CONT_SET_AVAIL(hw_cent, IMMU_CONT_INITED); 15163a634bfcSVikram Hegde CONT_SET_DID(hw_cent, domain->dom_did); 15173a634bfcSVikram Hegde CONT_SET_AW(hw_cent, immu->immu_dvma_agaw); 15183a634bfcSVikram Hegde CONT_SET_ASR(hw_cent, pgtable_root->hwpg_paddr); 1519be56ae36SFrank Van Der Linden if (domain->dom_did == IMMU_UNITY_DID && 1520be56ae36SFrank Van Der Linden IMMU_ECAP_GET_PT(immu->immu_regs_excap)) 1521be56ae36SFrank Van Der Linden CONT_SET_TTYPE(hw_cent, TTYPE_PASSTHRU); 1522be56ae36SFrank Van Der Linden else 15233a634bfcSVikram Hegde /*LINTED*/ 15243a634bfcSVikram Hegde CONT_SET_TTYPE(hw_cent, TTYPE_XLATE_ONLY); 15253a634bfcSVikram Hegde CONT_SET_P(hw_cent); 152650200e77SFrank Van Der Linden if (IMMU_ECAP_GET_CH(immu->immu_regs_excap)) { 152750200e77SFrank Van Der Linden CONT_SET_EH(hw_cent); 152850200e77SFrank Van Der Linden if (immu_use_alh) 152950200e77SFrank Van Der Linden CONT_SET_ALH(hw_cent); 153050200e77SFrank Van Der Linden } 15313a634bfcSVikram Hegde immu_regs_cpu_flush(immu, (caddr_t)hw_cent, sizeof (hw_rce_t)); 15323a634bfcSVikram Hegde } 1533e03dceedSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 15343a634bfcSVikram Hegde } 15353a634bfcSVikram Hegde 15363a634bfcSVikram Hegde static pgtable_t * 15373a634bfcSVikram Hegde context_create(immu_t *immu) 15383a634bfcSVikram Hegde { 15393a634bfcSVikram Hegde int bus; 15403a634bfcSVikram Hegde int devfunc; 15413a634bfcSVikram Hegde pgtable_t *root_table; 15423a634bfcSVikram Hegde pgtable_t *context; 15433a634bfcSVikram Hegde pgtable_t *pgtable_root; 15443a634bfcSVikram Hegde hw_rce_t *ctxp; 15453a634bfcSVikram Hegde hw_rce_t *hw_rent; 15463a634bfcSVikram Hegde hw_rce_t *hw_cent; 15473a634bfcSVikram Hegde 15483a634bfcSVikram Hegde /* Allocate a zeroed root table (4K 256b entries) */ 1549e03dceedSVikram Hegde root_table = pgtable_alloc(immu, IMMU_FLAGS_SLEEP); 155050200e77SFrank Van Der Linden pgtable_zero(root_table); 15513a634bfcSVikram Hegde 15523a634bfcSVikram Hegde /* 15533a634bfcSVikram Hegde * Setup context tables for all possible root table entries. 15543a634bfcSVikram Hegde * Start out with unity domains for all entries. 15553a634bfcSVikram Hegde */ 15563a634bfcSVikram Hegde ctxp = (hw_rce_t *)(root_table->swpg_next_array); 15573a634bfcSVikram Hegde hw_rent = (hw_rce_t *)(root_table->hwpg_vaddr); 15583a634bfcSVikram Hegde for (bus = 0; bus < IMMU_ROOT_NUM; bus++, ctxp++, hw_rent++) { 1559e03dceedSVikram Hegde context = pgtable_alloc(immu, IMMU_FLAGS_SLEEP); 156050200e77SFrank Van Der Linden pgtable_zero(context); 15613a634bfcSVikram Hegde ROOT_SET_P(hw_rent); 15623a634bfcSVikram Hegde ROOT_SET_CONT(hw_rent, context->hwpg_paddr); 15633a634bfcSVikram Hegde hw_cent = (hw_rce_t *)(context->hwpg_vaddr); 15643a634bfcSVikram Hegde for (devfunc = 0; devfunc < IMMU_CONT_NUM; 15653a634bfcSVikram Hegde devfunc++, hw_cent++) { 15663a634bfcSVikram Hegde pgtable_root = 15673a634bfcSVikram Hegde immu->immu_unity_domain->dom_pgtable_root; 15683a634bfcSVikram Hegde CONT_SET_DID(hw_cent, 15693a634bfcSVikram Hegde immu->immu_unity_domain->dom_did); 15703a634bfcSVikram Hegde CONT_SET_AW(hw_cent, immu->immu_dvma_agaw); 15713a634bfcSVikram Hegde CONT_SET_ASR(hw_cent, pgtable_root->hwpg_paddr); 1572be56ae36SFrank Van Der Linden if (IMMU_ECAP_GET_PT(immu->immu_regs_excap)) 1573be56ae36SFrank Van Der Linden CONT_SET_TTYPE(hw_cent, TTYPE_PASSTHRU); 1574be56ae36SFrank Van Der Linden else 15753a634bfcSVikram Hegde /*LINTED*/ 15763a634bfcSVikram Hegde CONT_SET_TTYPE(hw_cent, TTYPE_XLATE_ONLY); 15773a634bfcSVikram Hegde CONT_SET_AVAIL(hw_cent, IMMU_CONT_UNINITED); 15783a634bfcSVikram Hegde CONT_SET_P(hw_cent); 15793a634bfcSVikram Hegde } 15803a634bfcSVikram Hegde immu_regs_cpu_flush(immu, context->hwpg_vaddr, IMMU_PAGESIZE); 15813a634bfcSVikram Hegde *((pgtable_t **)ctxp) = context; 15823a634bfcSVikram Hegde } 15833a634bfcSVikram Hegde 15843a634bfcSVikram Hegde return (root_table); 15853a634bfcSVikram Hegde } 15863a634bfcSVikram Hegde 15873a634bfcSVikram Hegde /* 15883a634bfcSVikram Hegde * Called during rootnex attach, so no locks needed 15893a634bfcSVikram Hegde */ 15903a634bfcSVikram Hegde static void 15913a634bfcSVikram Hegde context_init(immu_t *immu) 15923a634bfcSVikram Hegde { 15933a634bfcSVikram Hegde rw_init(&(immu->immu_ctx_rwlock), NULL, RW_DEFAULT, NULL); 15943a634bfcSVikram Hegde 159550200e77SFrank Van Der Linden immu_init_inv_wait(&immu->immu_ctx_inv_wait, "ctxglobal", B_TRUE); 159650200e77SFrank Van Der Linden 15973a634bfcSVikram Hegde immu_regs_wbf_flush(immu); 15983a634bfcSVikram Hegde 15993a634bfcSVikram Hegde immu->immu_ctx_root = context_create(immu); 16003a634bfcSVikram Hegde 16013a634bfcSVikram Hegde immu_regs_set_root_table(immu); 16023a634bfcSVikram Hegde 16033a634bfcSVikram Hegde rw_enter(&(immu->immu_ctx_rwlock), RW_WRITER); 160450200e77SFrank Van Der Linden immu_flush_context_gbl(immu, &immu->immu_ctx_inv_wait); 160550200e77SFrank Van Der Linden immu_flush_iotlb_gbl(immu, &immu->immu_ctx_inv_wait); 16063a634bfcSVikram Hegde rw_exit(&(immu->immu_ctx_rwlock)); 16073a634bfcSVikram Hegde } 16083a634bfcSVikram Hegde 16093a634bfcSVikram Hegde 16103a634bfcSVikram Hegde /* 16113a634bfcSVikram Hegde * Find top pcib 16123a634bfcSVikram Hegde */ 16133a634bfcSVikram Hegde static int 16143a634bfcSVikram Hegde find_top_pcib(dev_info_t *dip, void *arg) 16153a634bfcSVikram Hegde { 16163a634bfcSVikram Hegde immu_devi_t *immu_devi; 16173a634bfcSVikram Hegde dev_info_t **pcibdipp = (dev_info_t **)arg; 16183a634bfcSVikram Hegde 16193a634bfcSVikram Hegde immu_devi = immu_devi_get(dip); 16203a634bfcSVikram Hegde 16213a634bfcSVikram Hegde if (immu_devi->imd_pcib_type == IMMU_PCIB_PCI_PCI) { 16223a634bfcSVikram Hegde *pcibdipp = dip; 16233a634bfcSVikram Hegde } 16243a634bfcSVikram Hegde 16253a634bfcSVikram Hegde return (DDI_WALK_CONTINUE); 16263a634bfcSVikram Hegde } 16273a634bfcSVikram Hegde 16283a634bfcSVikram Hegde static int 16293a634bfcSVikram Hegde immu_context_update(immu_t *immu, domain_t *domain, dev_info_t *ddip, 16303a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 16313a634bfcSVikram Hegde { 16323a634bfcSVikram Hegde immu_devi_t *r_immu_devi; 16333a634bfcSVikram Hegde immu_devi_t *d_immu_devi; 16343a634bfcSVikram Hegde int r_bus; 16353a634bfcSVikram Hegde int d_bus; 16363a634bfcSVikram Hegde int r_devfunc; 16373a634bfcSVikram Hegde int d_devfunc; 16383a634bfcSVikram Hegde immu_pcib_t d_pcib_type; 16393a634bfcSVikram Hegde dev_info_t *pcibdip; 16403a634bfcSVikram Hegde 16413a634bfcSVikram Hegde if (ddip == NULL || rdip == NULL || 16423a634bfcSVikram Hegde ddip == root_devinfo || rdip == root_devinfo) { 16433a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "immu_contexts_update: domain-dip or " 16443a634bfcSVikram Hegde "request-dip are NULL or are root devinfo"); 16453a634bfcSVikram Hegde return (DDI_FAILURE); 16463a634bfcSVikram Hegde } 16473a634bfcSVikram Hegde 16483a634bfcSVikram Hegde /* 16493a634bfcSVikram Hegde * We need to set the context fields 16503a634bfcSVikram Hegde * based on what type of device rdip and ddip are. 16513a634bfcSVikram Hegde * To do that we need the immu_devi field. 16523a634bfcSVikram Hegde * Set the immu_devi field (if not already set) 16533a634bfcSVikram Hegde */ 16543a634bfcSVikram Hegde if (immu_devi_set(ddip, immu_flags) == DDI_FAILURE) { 16553a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, 16563a634bfcSVikram Hegde "immu_context_update: failed to set immu_devi for ddip"); 16573a634bfcSVikram Hegde return (DDI_FAILURE); 16583a634bfcSVikram Hegde } 16593a634bfcSVikram Hegde 16603a634bfcSVikram Hegde if (immu_devi_set(rdip, immu_flags) == DDI_FAILURE) { 16613a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, 16623a634bfcSVikram Hegde "immu_context_update: failed to set immu_devi for rdip"); 16633a634bfcSVikram Hegde return (DDI_FAILURE); 16643a634bfcSVikram Hegde } 16653a634bfcSVikram Hegde 16663a634bfcSVikram Hegde d_immu_devi = immu_devi_get(ddip); 16673a634bfcSVikram Hegde r_immu_devi = immu_devi_get(rdip); 16683a634bfcSVikram Hegde 16693a634bfcSVikram Hegde d_bus = d_immu_devi->imd_bus; 16703a634bfcSVikram Hegde d_devfunc = d_immu_devi->imd_devfunc; 16713a634bfcSVikram Hegde d_pcib_type = d_immu_devi->imd_pcib_type; 16723a634bfcSVikram Hegde r_bus = r_immu_devi->imd_bus; 16733a634bfcSVikram Hegde r_devfunc = r_immu_devi->imd_devfunc; 16743a634bfcSVikram Hegde 16753a634bfcSVikram Hegde if (rdip == ddip) { 16763a634bfcSVikram Hegde /* rdip is a PCIE device. set context for it only */ 16773a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, r_bus, 16783a634bfcSVikram Hegde r_devfunc); 16793a634bfcSVikram Hegde #ifdef BUGGY_DRIVERS 16803a634bfcSVikram Hegde } else if (r_immu_devi == d_immu_devi) { 16813a634bfcSVikram Hegde #ifdef TEST 16823a634bfcSVikram Hegde ddi_err(DER_WARN, rdip, "Driver bug: Devices 0x%lx and " 16833a634bfcSVikram Hegde "0x%lx are identical", rdip, ddip); 16843a634bfcSVikram Hegde #endif 16853a634bfcSVikram Hegde /* rdip is a PCIE device. set context for it only */ 16863a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, r_bus, 16873a634bfcSVikram Hegde r_devfunc); 16883a634bfcSVikram Hegde #endif 16893a634bfcSVikram Hegde } else if (d_pcib_type == IMMU_PCIB_PCIE_PCI) { 16903a634bfcSVikram Hegde /* 16913a634bfcSVikram Hegde * ddip is a PCIE_PCI bridge. Set context for ddip's 16923a634bfcSVikram Hegde * secondary bus. If rdip is on ddip's secondary 16933a634bfcSVikram Hegde * bus, set context for rdip. Else, set context 16943a634bfcSVikram Hegde * for rdip's PCI bridge on ddip's secondary bus. 16953a634bfcSVikram Hegde */ 16963a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, 16973a634bfcSVikram Hegde d_immu_devi->imd_sec, 0); 16983a634bfcSVikram Hegde if (d_immu_devi->imd_sec == r_bus) { 16993a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, 17003a634bfcSVikram Hegde r_bus, r_devfunc); 17013a634bfcSVikram Hegde } else { 17023a634bfcSVikram Hegde pcibdip = NULL; 17033a634bfcSVikram Hegde if (immu_walk_ancestor(rdip, ddip, find_top_pcib, 17043a634bfcSVikram Hegde &pcibdip, NULL, immu_flags) == DDI_SUCCESS && 17053a634bfcSVikram Hegde pcibdip != NULL) { 17063a634bfcSVikram Hegde r_immu_devi = immu_devi_get(pcibdip); 17073a634bfcSVikram Hegde r_bus = r_immu_devi->imd_bus; 17083a634bfcSVikram Hegde r_devfunc = r_immu_devi->imd_devfunc; 17093a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, 17103a634bfcSVikram Hegde r_bus, r_devfunc); 17113a634bfcSVikram Hegde } else { 17123a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, "Failed to find PCI " 17133a634bfcSVikram Hegde " bridge for PCI device"); 17143a634bfcSVikram Hegde /*NOTREACHED*/ 17153a634bfcSVikram Hegde } 17163a634bfcSVikram Hegde } 17173a634bfcSVikram Hegde } else if (d_pcib_type == IMMU_PCIB_PCI_PCI) { 17183a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, d_bus, 17193a634bfcSVikram Hegde d_devfunc); 17203a634bfcSVikram Hegde } else if (d_pcib_type == IMMU_PCIB_ENDPOINT) { 17213a634bfcSVikram Hegde /* 17223a634bfcSVikram Hegde * ddip is a PCIE device which has a non-PCI device under it 17233a634bfcSVikram Hegde * i.e. it is a PCI-nonPCI bridge. Example: pciicde-ata 17243a634bfcSVikram Hegde */ 17253a634bfcSVikram Hegde context_set(immu, domain, immu->immu_ctx_root, d_bus, 17263a634bfcSVikram Hegde d_devfunc); 17273a634bfcSVikram Hegde } else { 17283a634bfcSVikram Hegde ddi_err(DER_PANIC, rdip, "unknown device type. Cannot " 172950200e77SFrank Van Der Linden "set iommu context."); 17303a634bfcSVikram Hegde /*NOTREACHED*/ 17313a634bfcSVikram Hegde } 17323a634bfcSVikram Hegde 17333a634bfcSVikram Hegde /* XXX do we need a membar_producer() here */ 17343a634bfcSVikram Hegde return (DDI_SUCCESS); 17353a634bfcSVikram Hegde } 17363a634bfcSVikram Hegde 17373a634bfcSVikram Hegde /* ##################### END CONTEXT CODE ################################## */ 17383a634bfcSVikram Hegde /* ##################### MAPPING CODE ################################## */ 17393a634bfcSVikram Hegde 17403a634bfcSVikram Hegde 174150200e77SFrank Van Der Linden #ifdef DEBUG 17423a634bfcSVikram Hegde static boolean_t 17433a634bfcSVikram Hegde PDTE_check(immu_t *immu, hw_pdte_t pdte, pgtable_t *next, paddr_t paddr, 17443a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 17453a634bfcSVikram Hegde { 17463a634bfcSVikram Hegde /* The PDTE must be set i.e. present bit is set */ 17473a634bfcSVikram Hegde if (!PDTE_P(pdte)) { 17483a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "No present flag"); 17493a634bfcSVikram Hegde return (B_FALSE); 17503a634bfcSVikram Hegde } 17513a634bfcSVikram Hegde 17523a634bfcSVikram Hegde /* 17533a634bfcSVikram Hegde * Just assert to check most significant system software field 17543a634bfcSVikram Hegde * (PDTE_SW4) as it is same as present bit and we 17553a634bfcSVikram Hegde * checked that above 17563a634bfcSVikram Hegde */ 17573a634bfcSVikram Hegde ASSERT(PDTE_SW4(pdte)); 17583a634bfcSVikram Hegde 17593a634bfcSVikram Hegde /* 17603a634bfcSVikram Hegde * TM field should be clear if not reserved. 17613a634bfcSVikram Hegde * non-leaf is always reserved 17623a634bfcSVikram Hegde */ 1763e03dceedSVikram Hegde if (next == NULL && immu->immu_TM_reserved == B_FALSE) { 17643a634bfcSVikram Hegde if (PDTE_TM(pdte)) { 17653a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "TM flag set"); 17663a634bfcSVikram Hegde return (B_FALSE); 17673a634bfcSVikram Hegde } 17683a634bfcSVikram Hegde } 17693a634bfcSVikram Hegde 17703a634bfcSVikram Hegde /* 17713a634bfcSVikram Hegde * The SW3 field is not used and must be clear 17723a634bfcSVikram Hegde */ 17733a634bfcSVikram Hegde if (PDTE_SW3(pdte)) { 17743a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "SW3 set"); 17753a634bfcSVikram Hegde return (B_FALSE); 17763a634bfcSVikram Hegde } 17773a634bfcSVikram Hegde 17783a634bfcSVikram Hegde /* 17793a634bfcSVikram Hegde * PFN (for PTE) or next level pgtable-paddr (for PDE) must be set 17803a634bfcSVikram Hegde */ 17813a634bfcSVikram Hegde if (next == NULL) { 17823a634bfcSVikram Hegde ASSERT(paddr % IMMU_PAGESIZE == 0); 17833a634bfcSVikram Hegde if (PDTE_PADDR(pdte) != paddr) { 17843a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, 17853a634bfcSVikram Hegde "PTE paddr mismatch: %lx != %lx", 17863a634bfcSVikram Hegde PDTE_PADDR(pdte), paddr); 17873a634bfcSVikram Hegde return (B_FALSE); 17883a634bfcSVikram Hegde } 17893a634bfcSVikram Hegde } else { 17903a634bfcSVikram Hegde if (PDTE_PADDR(pdte) != next->hwpg_paddr) { 17913a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, 17923a634bfcSVikram Hegde "PDE paddr mismatch: %lx != %lx", 17933a634bfcSVikram Hegde PDTE_PADDR(pdte), next->hwpg_paddr); 17943a634bfcSVikram Hegde return (B_FALSE); 17953a634bfcSVikram Hegde } 17963a634bfcSVikram Hegde } 17973a634bfcSVikram Hegde 17983a634bfcSVikram Hegde /* 17993a634bfcSVikram Hegde * SNP field should be clear if not reserved. 18003a634bfcSVikram Hegde * non-leaf is always reserved 18013a634bfcSVikram Hegde */ 1802e03dceedSVikram Hegde if (next == NULL && immu->immu_SNP_reserved == B_FALSE) { 18033a634bfcSVikram Hegde if (PDTE_SNP(pdte)) { 18043a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "SNP set"); 18053a634bfcSVikram Hegde return (B_FALSE); 18063a634bfcSVikram Hegde } 18073a634bfcSVikram Hegde } 18083a634bfcSVikram Hegde 18093a634bfcSVikram Hegde /* second field available for system software should be clear */ 18103a634bfcSVikram Hegde if (PDTE_SW2(pdte)) { 18113a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "SW2 set"); 18123a634bfcSVikram Hegde return (B_FALSE); 18133a634bfcSVikram Hegde } 18143a634bfcSVikram Hegde 18153a634bfcSVikram Hegde /* Super pages field should be clear */ 18163a634bfcSVikram Hegde if (PDTE_SP(pdte)) { 18173a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "SP set"); 18183a634bfcSVikram Hegde return (B_FALSE); 18193a634bfcSVikram Hegde } 18203a634bfcSVikram Hegde 18213a634bfcSVikram Hegde /* 18223a634bfcSVikram Hegde * least significant field available for 18233a634bfcSVikram Hegde * system software should be clear 18243a634bfcSVikram Hegde */ 18253a634bfcSVikram Hegde if (PDTE_SW1(pdte)) { 18263a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "SW1 set"); 18273a634bfcSVikram Hegde return (B_FALSE); 18283a634bfcSVikram Hegde } 18293a634bfcSVikram Hegde 18303a634bfcSVikram Hegde if ((immu_flags & IMMU_FLAGS_READ) && !PDTE_READ(pdte)) { 18313a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "READ not set"); 18323a634bfcSVikram Hegde return (B_FALSE); 18333a634bfcSVikram Hegde } 18343a634bfcSVikram Hegde 18353a634bfcSVikram Hegde if ((immu_flags & IMMU_FLAGS_WRITE) && !PDTE_WRITE(pdte)) { 18363a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "WRITE not set"); 18373a634bfcSVikram Hegde return (B_FALSE); 18383a634bfcSVikram Hegde } 18393a634bfcSVikram Hegde 18403a634bfcSVikram Hegde return (B_TRUE); 18413a634bfcSVikram Hegde } 184250200e77SFrank Van Der Linden #endif 184350200e77SFrank Van Der Linden 18443a634bfcSVikram Hegde /*ARGSUSED*/ 18453a634bfcSVikram Hegde static void 1846e03dceedSVikram Hegde PTE_clear_all(immu_t *immu, domain_t *domain, xlate_t *xlate, 1847e03dceedSVikram Hegde uint64_t *dvma_ptr, uint64_t *npages_ptr, dev_info_t *rdip) 18483a634bfcSVikram Hegde { 1849e03dceedSVikram Hegde uint64_t npages; 1850e03dceedSVikram Hegde uint64_t dvma; 18513a634bfcSVikram Hegde pgtable_t *pgtable; 1852e03dceedSVikram Hegde hw_pdte_t *hwp; 1853e03dceedSVikram Hegde hw_pdte_t *shwp; 18543a634bfcSVikram Hegde int idx; 18553a634bfcSVikram Hegde 18563a634bfcSVikram Hegde pgtable = xlate->xlt_pgtable; 1857e03dceedSVikram Hegde idx = xlate->xlt_idx; 18583a634bfcSVikram Hegde 1859e03dceedSVikram Hegde dvma = *dvma_ptr; 1860e03dceedSVikram Hegde npages = *npages_ptr; 1861e03dceedSVikram Hegde 18623a634bfcSVikram Hegde /* 1863e03dceedSVikram Hegde * since a caller gets a unique dvma for a physical address, 1864e03dceedSVikram Hegde * no other concurrent thread will be writing to the same 1865e03dceedSVikram Hegde * PTE even if it has the same paddr. So no locks needed. 18663a634bfcSVikram Hegde */ 1867e03dceedSVikram Hegde shwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx; 18683a634bfcSVikram Hegde 1869e03dceedSVikram Hegde hwp = shwp; 1870e03dceedSVikram Hegde for (; npages > 0 && idx <= IMMU_PGTABLE_MAXIDX; idx++, hwp++) { 187150200e77SFrank Van Der Linden PDTE_CLEAR_P(*hwp); 1872e03dceedSVikram Hegde dvma += IMMU_PAGESIZE; 1873e03dceedSVikram Hegde npages--; 1874e03dceedSVikram Hegde } 18753a634bfcSVikram Hegde 1876e03dceedSVikram Hegde *dvma_ptr = dvma; 1877e03dceedSVikram Hegde *npages_ptr = npages; 1878e03dceedSVikram Hegde 1879e03dceedSVikram Hegde xlate->xlt_idx = idx; 18803a634bfcSVikram Hegde } 18813a634bfcSVikram Hegde 18823a634bfcSVikram Hegde static void 188350200e77SFrank Van Der Linden xlate_setup(uint64_t dvma, xlate_t *xlate, int nlevels) 18843a634bfcSVikram Hegde { 18853a634bfcSVikram Hegde int level; 18863a634bfcSVikram Hegde uint64_t offbits; 18873a634bfcSVikram Hegde 18883a634bfcSVikram Hegde /* 18893a634bfcSVikram Hegde * Skip the first 12 bits which is the offset into 18903a634bfcSVikram Hegde * 4K PFN (phys page frame based on IMMU_PAGESIZE) 18913a634bfcSVikram Hegde */ 18923a634bfcSVikram Hegde offbits = dvma >> IMMU_PAGESHIFT; 18933a634bfcSVikram Hegde 18943a634bfcSVikram Hegde /* skip to level 1 i.e. leaf PTE */ 18953a634bfcSVikram Hegde for (level = 1, xlate++; level <= nlevels; level++, xlate++) { 18963a634bfcSVikram Hegde xlate->xlt_level = level; 18973a634bfcSVikram Hegde xlate->xlt_idx = (offbits & IMMU_PGTABLE_LEVEL_MASK); 18983a634bfcSVikram Hegde ASSERT(xlate->xlt_idx <= IMMU_PGTABLE_MAXIDX); 18993a634bfcSVikram Hegde xlate->xlt_pgtable = NULL; 19003a634bfcSVikram Hegde offbits >>= IMMU_PGTABLE_LEVEL_STRIDE; 19013a634bfcSVikram Hegde } 19023a634bfcSVikram Hegde } 19033a634bfcSVikram Hegde 19043a634bfcSVikram Hegde /* 19053a634bfcSVikram Hegde * Read the pgtables 19063a634bfcSVikram Hegde */ 190750200e77SFrank Van Der Linden static boolean_t 190850200e77SFrank Van Der Linden PDE_lookup(domain_t *domain, xlate_t *xlate, int nlevels) 19093a634bfcSVikram Hegde { 19103a634bfcSVikram Hegde pgtable_t *pgtable; 19113a634bfcSVikram Hegde pgtable_t *next; 19123a634bfcSVikram Hegde uint_t idx; 19133a634bfcSVikram Hegde 19143a634bfcSVikram Hegde /* start with highest level pgtable i.e. root */ 19153a634bfcSVikram Hegde xlate += nlevels; 19163a634bfcSVikram Hegde 19173a634bfcSVikram Hegde if (xlate->xlt_pgtable == NULL) { 19183a634bfcSVikram Hegde xlate->xlt_pgtable = domain->dom_pgtable_root; 19193a634bfcSVikram Hegde } 19203a634bfcSVikram Hegde 19213a634bfcSVikram Hegde for (; xlate->xlt_level > 1; xlate--) { 19223a634bfcSVikram Hegde idx = xlate->xlt_idx; 19233a634bfcSVikram Hegde pgtable = xlate->xlt_pgtable; 19243a634bfcSVikram Hegde 19253a634bfcSVikram Hegde if ((xlate - 1)->xlt_pgtable) { 19263a634bfcSVikram Hegde continue; 19273a634bfcSVikram Hegde } 19283a634bfcSVikram Hegde 19293a634bfcSVikram Hegde /* Lock the pgtable in read mode */ 19303a634bfcSVikram Hegde rw_enter(&(pgtable->swpg_rwlock), RW_READER); 19313a634bfcSVikram Hegde 19323a634bfcSVikram Hegde /* 19333a634bfcSVikram Hegde * since we are unmapping, the pgtable should 19343a634bfcSVikram Hegde * already point to a leafier pgtable. 19353a634bfcSVikram Hegde */ 19363a634bfcSVikram Hegde next = *(pgtable->swpg_next_array + idx); 19373a634bfcSVikram Hegde (xlate - 1)->xlt_pgtable = next; 19383a634bfcSVikram Hegde rw_exit(&(pgtable->swpg_rwlock)); 193950200e77SFrank Van Der Linden if (next == NULL) 194050200e77SFrank Van Der Linden return (B_FALSE); 19413a634bfcSVikram Hegde } 194250200e77SFrank Van Der Linden 194350200e77SFrank Van Der Linden return (B_TRUE); 194450200e77SFrank Van Der Linden } 194550200e77SFrank Van Der Linden 194650200e77SFrank Van Der Linden static void 194750200e77SFrank Van Der Linden immu_fault_walk(void *arg, void *base, size_t len) 194850200e77SFrank Van Der Linden { 194950200e77SFrank Van Der Linden uint64_t dvma, start; 195050200e77SFrank Van Der Linden 195150200e77SFrank Van Der Linden dvma = *(uint64_t *)arg; 195250200e77SFrank Van Der Linden start = (uint64_t)(uintptr_t)base; 195350200e77SFrank Van Der Linden 195450200e77SFrank Van Der Linden if (dvma >= start && dvma < (start + len)) { 195550200e77SFrank Van Der Linden ddi_err(DER_WARN, NULL, 195650200e77SFrank Van Der Linden "faulting DVMA address is in vmem arena " 195750200e77SFrank Van Der Linden "(%" PRIx64 "-%" PRIx64 ")", 195850200e77SFrank Van Der Linden start, start + len); 195950200e77SFrank Van Der Linden *(uint64_t *)arg = ~0ULL; 196050200e77SFrank Van Der Linden } 196150200e77SFrank Van Der Linden } 196250200e77SFrank Van Der Linden 196350200e77SFrank Van Der Linden void 196450200e77SFrank Van Der Linden immu_print_fault_info(uint_t sid, uint64_t dvma) 196550200e77SFrank Van Der Linden { 196650200e77SFrank Van Der Linden int nlevels; 196750200e77SFrank Van Der Linden xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0}; 196850200e77SFrank Van Der Linden xlate_t *xlatep; 196950200e77SFrank Van Der Linden hw_pdte_t pte; 197050200e77SFrank Van Der Linden domain_t *domain; 197150200e77SFrank Van Der Linden immu_t *immu; 197250200e77SFrank Van Der Linden uint64_t dvma_arg; 197350200e77SFrank Van Der Linden 197450200e77SFrank Van Der Linden if (mod_hash_find(bdf_domain_hash, 197550200e77SFrank Van Der Linden (void *)(uintptr_t)sid, (void *)&domain) != 0) { 197650200e77SFrank Van Der Linden ddi_err(DER_WARN, NULL, 197750200e77SFrank Van Der Linden "no domain for faulting SID %08x", sid); 197850200e77SFrank Van Der Linden return; 197950200e77SFrank Van Der Linden } 198050200e77SFrank Van Der Linden 198150200e77SFrank Van Der Linden immu = domain->dom_immu; 198250200e77SFrank Van Der Linden 198350200e77SFrank Van Der Linden dvma_arg = dvma; 198450200e77SFrank Van Der Linden vmem_walk(domain->dom_dvma_arena, VMEM_ALLOC, immu_fault_walk, 198550200e77SFrank Van Der Linden (void *)&dvma_arg); 198650200e77SFrank Van Der Linden if (dvma_arg != ~0ULL) 198750200e77SFrank Van Der Linden ddi_err(DER_WARN, domain->dom_dip, 198850200e77SFrank Van Der Linden "faulting DVMA address is not in vmem arena"); 198950200e77SFrank Van Der Linden 199050200e77SFrank Van Der Linden nlevels = immu->immu_dvma_nlevels; 199150200e77SFrank Van Der Linden xlate_setup(dvma, xlate, nlevels); 199250200e77SFrank Van Der Linden 199350200e77SFrank Van Der Linden if (!PDE_lookup(domain, xlate, nlevels)) { 199450200e77SFrank Van Der Linden ddi_err(DER_WARN, domain->dom_dip, 199550200e77SFrank Van Der Linden "pte not found in domid %d for faulting addr %" PRIx64, 199650200e77SFrank Van Der Linden domain->dom_did, dvma); 199750200e77SFrank Van Der Linden return; 199850200e77SFrank Van Der Linden } 199950200e77SFrank Van Der Linden 200050200e77SFrank Van Der Linden xlatep = &xlate[1]; 200150200e77SFrank Van Der Linden pte = *((hw_pdte_t *) 200250200e77SFrank Van Der Linden (xlatep->xlt_pgtable->hwpg_vaddr) + xlatep->xlt_idx); 200350200e77SFrank Van Der Linden 200450200e77SFrank Van Der Linden ddi_err(DER_WARN, domain->dom_dip, 200550200e77SFrank Van Der Linden "domid %d pte: %" PRIx64 "(paddr %" PRIx64 ")", domain->dom_did, 200650200e77SFrank Van Der Linden (unsigned long long)pte, (unsigned long long)PDTE_PADDR(pte)); 20073a634bfcSVikram Hegde } 20083a634bfcSVikram Hegde 2009e03dceedSVikram Hegde /*ARGSUSED*/ 20103a634bfcSVikram Hegde static void 20113a634bfcSVikram Hegde PTE_set_one(immu_t *immu, hw_pdte_t *hwp, paddr_t paddr, 20123a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 20133a634bfcSVikram Hegde { 20143a634bfcSVikram Hegde hw_pdte_t pte; 20153a634bfcSVikram Hegde 2016e03dceedSVikram Hegde #ifndef DEBUG 201750200e77SFrank Van Der Linden pte = immu->immu_ptemask; 2018e03dceedSVikram Hegde PDTE_SET_PADDR(pte, paddr); 2019e03dceedSVikram Hegde #else 202050200e77SFrank Van Der Linden pte = *hwp; 2021e03dceedSVikram Hegde 20223a634bfcSVikram Hegde if (PDTE_P(pte)) { 20233a634bfcSVikram Hegde if (PDTE_PADDR(pte) != paddr) { 20243a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "PTE paddr %lx != paddr %lx", 20253a634bfcSVikram Hegde PDTE_PADDR(pte), paddr); 20263a634bfcSVikram Hegde } 2027e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS 2028e03dceedSVikram Hegde return; 2029e03dceedSVikram Hegde #else 20303a634bfcSVikram Hegde goto out; 2031e03dceedSVikram Hegde #endif 20323a634bfcSVikram Hegde } 20333a634bfcSVikram Hegde 20343a634bfcSVikram Hegde /* clear TM field if not reserved */ 2035e03dceedSVikram Hegde if (immu->immu_TM_reserved == B_FALSE) { 20363a634bfcSVikram Hegde PDTE_CLEAR_TM(pte); 20373a634bfcSVikram Hegde } 20383a634bfcSVikram Hegde 20393a634bfcSVikram Hegde /* Clear 3rd field for system software - not used */ 20403a634bfcSVikram Hegde PDTE_CLEAR_SW3(pte); 20413a634bfcSVikram Hegde 20423a634bfcSVikram Hegde /* Set paddr */ 20433a634bfcSVikram Hegde ASSERT(paddr % IMMU_PAGESIZE == 0); 20443a634bfcSVikram Hegde PDTE_CLEAR_PADDR(pte); 20453a634bfcSVikram Hegde PDTE_SET_PADDR(pte, paddr); 20463a634bfcSVikram Hegde 20473a634bfcSVikram Hegde /* clear SNP field if not reserved. */ 2048e03dceedSVikram Hegde if (immu->immu_SNP_reserved == B_FALSE) { 20493a634bfcSVikram Hegde PDTE_CLEAR_SNP(pte); 20503a634bfcSVikram Hegde } 20513a634bfcSVikram Hegde 20523a634bfcSVikram Hegde /* Clear SW2 field available for software */ 20533a634bfcSVikram Hegde PDTE_CLEAR_SW2(pte); 20543a634bfcSVikram Hegde 2055e03dceedSVikram Hegde 20563a634bfcSVikram Hegde /* SP is don't care for PTEs. Clear it for cleanliness */ 20573a634bfcSVikram Hegde PDTE_CLEAR_SP(pte); 20583a634bfcSVikram Hegde 20593a634bfcSVikram Hegde /* Clear SW1 field available for software */ 20603a634bfcSVikram Hegde PDTE_CLEAR_SW1(pte); 20613a634bfcSVikram Hegde 20623a634bfcSVikram Hegde /* 20633a634bfcSVikram Hegde * Now that we are done writing the PTE 20643a634bfcSVikram Hegde * set the "present" flag. Note this present 20653a634bfcSVikram Hegde * flag is a bit in the PDE/PTE that the 20663a634bfcSVikram Hegde * spec says is available for system software. 20673a634bfcSVikram Hegde * This is an implementation detail of Solaris 20683a634bfcSVikram Hegde * bare-metal Intel IOMMU. 20693a634bfcSVikram Hegde * The present field in a PDE/PTE is not defined 20703a634bfcSVikram Hegde * by the Vt-d spec 20713a634bfcSVikram Hegde */ 20723a634bfcSVikram Hegde 20733a634bfcSVikram Hegde PDTE_SET_P(pte); 20743a634bfcSVikram Hegde 207550200e77SFrank Van Der Linden pte |= immu->immu_ptemask; 207650200e77SFrank Van Der Linden 20773a634bfcSVikram Hegde out: 207850200e77SFrank Van Der Linden #endif /* DEBUG */ 2079e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS 2080e03dceedSVikram Hegde PDTE_SET_READ(pte); 2081e03dceedSVikram Hegde PDTE_SET_WRITE(pte); 2082e03dceedSVikram Hegde #else 20833a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_READ) 20843a634bfcSVikram Hegde PDTE_SET_READ(pte); 20853a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_WRITE) 20863a634bfcSVikram Hegde PDTE_SET_WRITE(pte); 208750200e77SFrank Van Der Linden #endif /* BUGGY_DRIVERS */ 20883a634bfcSVikram Hegde 20893a634bfcSVikram Hegde *hwp = pte; 20903a634bfcSVikram Hegde } 20913a634bfcSVikram Hegde 20923a634bfcSVikram Hegde /*ARGSUSED*/ 20933a634bfcSVikram Hegde static void 20943a634bfcSVikram Hegde PTE_set_all(immu_t *immu, domain_t *domain, xlate_t *xlate, 209550200e77SFrank Van Der Linden uint64_t *dvma_ptr, uint64_t *nvpages_ptr, immu_dcookie_t *dcookies, 2096e03dceedSVikram Hegde int dcount, dev_info_t *rdip, immu_flags_t immu_flags) 20973a634bfcSVikram Hegde { 20983a634bfcSVikram Hegde paddr_t paddr; 2099e03dceedSVikram Hegde uint64_t nvpages; 2100e03dceedSVikram Hegde uint64_t nppages; 21013a634bfcSVikram Hegde uint64_t dvma; 21023a634bfcSVikram Hegde pgtable_t *pgtable; 21033a634bfcSVikram Hegde hw_pdte_t *hwp; 21043a634bfcSVikram Hegde hw_pdte_t *shwp; 210550200e77SFrank Van Der Linden int idx, nset; 2106e03dceedSVikram Hegde int j; 21073a634bfcSVikram Hegde 21083a634bfcSVikram Hegde pgtable = xlate->xlt_pgtable; 21093a634bfcSVikram Hegde idx = xlate->xlt_idx; 21103a634bfcSVikram Hegde 21113a634bfcSVikram Hegde dvma = *dvma_ptr; 2112e03dceedSVikram Hegde nvpages = *nvpages_ptr; 21133a634bfcSVikram Hegde 21143a634bfcSVikram Hegde /* 2115e03dceedSVikram Hegde * since a caller gets a unique dvma for a physical address, 2116e03dceedSVikram Hegde * no other concurrent thread will be writing to the same 2117e03dceedSVikram Hegde * PTE even if it has the same paddr. So no locks needed. 21183a634bfcSVikram Hegde */ 21193a634bfcSVikram Hegde shwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx; 21203a634bfcSVikram Hegde 21213a634bfcSVikram Hegde hwp = shwp; 2122e03dceedSVikram Hegde for (j = dcount - 1; j >= 0; j--) { 2123e03dceedSVikram Hegde if (nvpages <= dcookies[j].dck_npages) 2124e03dceedSVikram Hegde break; 2125e03dceedSVikram Hegde nvpages -= dcookies[j].dck_npages; 2126e03dceedSVikram Hegde } 2127e03dceedSVikram Hegde 2128e03dceedSVikram Hegde nppages = nvpages; 2129e03dceedSVikram Hegde paddr = dcookies[j].dck_paddr + 2130e03dceedSVikram Hegde (dcookies[j].dck_npages - nppages) * IMMU_PAGESIZE; 2131e03dceedSVikram Hegde 2132e03dceedSVikram Hegde nvpages = *nvpages_ptr; 213350200e77SFrank Van Der Linden nset = 0; 2134e03dceedSVikram Hegde for (; nvpages > 0 && idx <= IMMU_PGTABLE_MAXIDX; idx++, hwp++) { 21353a634bfcSVikram Hegde PTE_set_one(immu, hwp, paddr, rdip, immu_flags); 213650200e77SFrank Van Der Linden nset++; 21373a634bfcSVikram Hegde 21383a634bfcSVikram Hegde ASSERT(PDTE_check(immu, *hwp, NULL, paddr, rdip, immu_flags) 21393a634bfcSVikram Hegde == B_TRUE); 2140e03dceedSVikram Hegde nppages--; 2141e03dceedSVikram Hegde nvpages--; 21423a634bfcSVikram Hegde paddr += IMMU_PAGESIZE; 21433a634bfcSVikram Hegde dvma += IMMU_PAGESIZE; 2144e03dceedSVikram Hegde 2145e03dceedSVikram Hegde if (nppages == 0) { 2146e03dceedSVikram Hegde j++; 2147e03dceedSVikram Hegde } 2148e03dceedSVikram Hegde 214950200e77SFrank Van Der Linden if (j == dcount) 2150e03dceedSVikram Hegde break; 2151e03dceedSVikram Hegde 2152e03dceedSVikram Hegde if (nppages == 0) { 2153e03dceedSVikram Hegde nppages = dcookies[j].dck_npages; 2154e03dceedSVikram Hegde paddr = dcookies[j].dck_paddr; 2155e03dceedSVikram Hegde } 21563a634bfcSVikram Hegde } 21573a634bfcSVikram Hegde 2158e03dceedSVikram Hegde if (nvpages) { 21593a634bfcSVikram Hegde *dvma_ptr = dvma; 2160e03dceedSVikram Hegde *nvpages_ptr = nvpages; 2161e03dceedSVikram Hegde } else { 2162e03dceedSVikram Hegde *dvma_ptr = 0; 2163e03dceedSVikram Hegde *nvpages_ptr = 0; 2164e03dceedSVikram Hegde } 21653a634bfcSVikram Hegde 2166e03dceedSVikram Hegde xlate->xlt_idx = idx; 21673a634bfcSVikram Hegde } 21683a634bfcSVikram Hegde 21693a634bfcSVikram Hegde /*ARGSUSED*/ 21703a634bfcSVikram Hegde static void 21713a634bfcSVikram Hegde PDE_set_one(immu_t *immu, hw_pdte_t *hwp, pgtable_t *next, 21723a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 21733a634bfcSVikram Hegde { 21743a634bfcSVikram Hegde hw_pdte_t pde; 21753a634bfcSVikram Hegde 21763a634bfcSVikram Hegde pde = *hwp; 21773a634bfcSVikram Hegde 21783a634bfcSVikram Hegde /* if PDE is already set, make sure it is correct */ 21793a634bfcSVikram Hegde if (PDTE_P(pde)) { 21803a634bfcSVikram Hegde ASSERT(PDTE_PADDR(pde) == next->hwpg_paddr); 2181e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS 2182e03dceedSVikram Hegde return; 2183e03dceedSVikram Hegde #else 21843a634bfcSVikram Hegde goto out; 2185e03dceedSVikram Hegde #endif 21863a634bfcSVikram Hegde } 21873a634bfcSVikram Hegde 21883a634bfcSVikram Hegde /* Dont touch SW4, it is the present bit */ 21893a634bfcSVikram Hegde 21903a634bfcSVikram Hegde /* don't touch TM field it is reserved for PDEs */ 21913a634bfcSVikram Hegde 21923a634bfcSVikram Hegde /* 3rd field available for system software is not used */ 21933a634bfcSVikram Hegde PDTE_CLEAR_SW3(pde); 21943a634bfcSVikram Hegde 21953a634bfcSVikram Hegde /* Set next level pgtable-paddr for PDE */ 21963a634bfcSVikram Hegde PDTE_CLEAR_PADDR(pde); 21973a634bfcSVikram Hegde PDTE_SET_PADDR(pde, next->hwpg_paddr); 21983a634bfcSVikram Hegde 21993a634bfcSVikram Hegde /* don't touch SNP field it is reserved for PDEs */ 22003a634bfcSVikram Hegde 22013a634bfcSVikram Hegde /* Clear second field available for system software */ 22023a634bfcSVikram Hegde PDTE_CLEAR_SW2(pde); 22033a634bfcSVikram Hegde 22043a634bfcSVikram Hegde /* No super pages for PDEs */ 22053a634bfcSVikram Hegde PDTE_CLEAR_SP(pde); 22063a634bfcSVikram Hegde 22073a634bfcSVikram Hegde /* Clear SW1 for software */ 22083a634bfcSVikram Hegde PDTE_CLEAR_SW1(pde); 22093a634bfcSVikram Hegde 22103a634bfcSVikram Hegde /* 22113a634bfcSVikram Hegde * Now that we are done writing the PDE 22123a634bfcSVikram Hegde * set the "present" flag. Note this present 22133a634bfcSVikram Hegde * flag is a bit in the PDE/PTE that the 22143a634bfcSVikram Hegde * spec says is available for system software. 22153a634bfcSVikram Hegde * This is an implementation detail of Solaris 22163a634bfcSVikram Hegde * base-metal Intel IOMMU. 22173a634bfcSVikram Hegde * The present field in a PDE/PTE is not defined 22183a634bfcSVikram Hegde * by the Vt-d spec 22193a634bfcSVikram Hegde */ 22203a634bfcSVikram Hegde 2221e03dceedSVikram Hegde out: 2222e03dceedSVikram Hegde #ifdef BUGGY_DRIVERS 2223e03dceedSVikram Hegde PDTE_SET_READ(pde); 2224e03dceedSVikram Hegde PDTE_SET_WRITE(pde); 2225e03dceedSVikram Hegde #else 22263a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_READ) 22273a634bfcSVikram Hegde PDTE_SET_READ(pde); 22283a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_WRITE) 22293a634bfcSVikram Hegde PDTE_SET_WRITE(pde); 22303a634bfcSVikram Hegde #endif 22313a634bfcSVikram Hegde 22323a634bfcSVikram Hegde PDTE_SET_P(pde); 22333a634bfcSVikram Hegde 22343a634bfcSVikram Hegde *hwp = pde; 22353a634bfcSVikram Hegde } 22363a634bfcSVikram Hegde 22373a634bfcSVikram Hegde /* 22383a634bfcSVikram Hegde * Used to set PDEs 22393a634bfcSVikram Hegde */ 2240e03dceedSVikram Hegde static boolean_t 22413a634bfcSVikram Hegde PDE_set_all(immu_t *immu, domain_t *domain, xlate_t *xlate, int nlevels, 22423a634bfcSVikram Hegde dev_info_t *rdip, immu_flags_t immu_flags) 22433a634bfcSVikram Hegde { 22443a634bfcSVikram Hegde pgtable_t *pgtable; 22453a634bfcSVikram Hegde pgtable_t *new; 22463a634bfcSVikram Hegde pgtable_t *next; 22473a634bfcSVikram Hegde hw_pdte_t *hwp; 22483a634bfcSVikram Hegde int level; 22493a634bfcSVikram Hegde uint_t idx; 2250e03dceedSVikram Hegde krw_t rwtype; 2251e03dceedSVikram Hegde boolean_t set = B_FALSE; 22523a634bfcSVikram Hegde 22533a634bfcSVikram Hegde /* start with highest level pgtable i.e. root */ 22543a634bfcSVikram Hegde xlate += nlevels; 22553a634bfcSVikram Hegde 22563a634bfcSVikram Hegde new = NULL; 22573a634bfcSVikram Hegde xlate->xlt_pgtable = domain->dom_pgtable_root; 22583a634bfcSVikram Hegde for (level = nlevels; level > 1; level--, xlate--) { 22593a634bfcSVikram Hegde idx = xlate->xlt_idx; 22603a634bfcSVikram Hegde pgtable = xlate->xlt_pgtable; 22613a634bfcSVikram Hegde 2262e03dceedSVikram Hegde /* Lock the pgtable in READ mode first */ 2263e03dceedSVikram Hegde rw_enter(&(pgtable->swpg_rwlock), RW_READER); 2264e03dceedSVikram Hegde rwtype = RW_READER; 2265e03dceedSVikram Hegde again: 22663a634bfcSVikram Hegde hwp = (hw_pdte_t *)(pgtable->hwpg_vaddr) + idx; 22673a634bfcSVikram Hegde next = (pgtable->swpg_next_array)[idx]; 22683a634bfcSVikram Hegde 22693a634bfcSVikram Hegde /* 22703a634bfcSVikram Hegde * check if leafier level already has a pgtable 22713a634bfcSVikram Hegde * if yes, verify 22723a634bfcSVikram Hegde */ 22733a634bfcSVikram Hegde if (next == NULL) { 227450200e77SFrank Van Der Linden if (new == NULL) { 227550200e77SFrank Van Der Linden 227650200e77SFrank Van Der Linden IMMU_DPROBE2(immu__pdp__alloc, dev_info_t *, 227750200e77SFrank Van Der Linden rdip, int, level); 227850200e77SFrank Van Der Linden 227950200e77SFrank Van Der Linden new = pgtable_alloc(immu, immu_flags); 228050200e77SFrank Van Der Linden if (new == NULL) { 228150200e77SFrank Van Der Linden ddi_err(DER_PANIC, rdip, 228250200e77SFrank Van Der Linden "pgtable alloc err"); 228350200e77SFrank Van Der Linden } 228450200e77SFrank Van Der Linden pgtable_zero(new); 228550200e77SFrank Van Der Linden } 228650200e77SFrank Van Der Linden 2287e03dceedSVikram Hegde /* Change to a write lock */ 2288e03dceedSVikram Hegde if (rwtype == RW_READER && 2289e03dceedSVikram Hegde rw_tryupgrade(&(pgtable->swpg_rwlock)) == 0) { 2290e03dceedSVikram Hegde rw_exit(&(pgtable->swpg_rwlock)); 2291e03dceedSVikram Hegde rw_enter(&(pgtable->swpg_rwlock), RW_WRITER); 2292e03dceedSVikram Hegde rwtype = RW_WRITER; 2293e03dceedSVikram Hegde goto again; 2294e03dceedSVikram Hegde } 2295e03dceedSVikram Hegde rwtype = RW_WRITER; 22963a634bfcSVikram Hegde next = new; 22973a634bfcSVikram Hegde (pgtable->swpg_next_array)[idx] = next; 229850200e77SFrank Van Der Linden new = NULL; 22993a634bfcSVikram Hegde PDE_set_one(immu, hwp, next, rdip, immu_flags); 2300e03dceedSVikram Hegde set = B_TRUE; 2301e03dceedSVikram Hegde rw_downgrade(&(pgtable->swpg_rwlock)); 2302e03dceedSVikram Hegde rwtype = RW_READER; 230350200e77SFrank Van Der Linden } 230450200e77SFrank Van Der Linden #ifndef BUGGY_DRIVERS 230550200e77SFrank Van Der Linden else { 23063a634bfcSVikram Hegde hw_pdte_t pde = *hwp; 23073a634bfcSVikram Hegde 2308e03dceedSVikram Hegde /* 2309e03dceedSVikram Hegde * If buggy driver we already set permission 2310e03dceedSVikram Hegde * READ+WRITE so nothing to do for that case 2311e03dceedSVikram Hegde * XXX Check that read writer perms change before 2312e03dceedSVikram Hegde * actually setting perms. Also need to hold lock 2313e03dceedSVikram Hegde */ 23143a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_READ) 23153a634bfcSVikram Hegde PDTE_SET_READ(pde); 23163a634bfcSVikram Hegde if (immu_flags & IMMU_FLAGS_WRITE) 23173a634bfcSVikram Hegde PDTE_SET_WRITE(pde); 23183a634bfcSVikram Hegde 23193a634bfcSVikram Hegde *hwp = pde; 23203a634bfcSVikram Hegde } 232150200e77SFrank Van Der Linden #endif 23223a634bfcSVikram Hegde 23233a634bfcSVikram Hegde ASSERT(PDTE_check(immu, *hwp, next, 0, rdip, immu_flags) 23243a634bfcSVikram Hegde == B_TRUE); 23253a634bfcSVikram Hegde 23263a634bfcSVikram Hegde (xlate - 1)->xlt_pgtable = next; 23273a634bfcSVikram Hegde rw_exit(&(pgtable->swpg_rwlock)); 23283a634bfcSVikram Hegde } 23293a634bfcSVikram Hegde 23303a634bfcSVikram Hegde if (new) { 2331e03dceedSVikram Hegde pgtable_free(immu, new); 23323a634bfcSVikram Hegde } 2333e03dceedSVikram Hegde 2334e03dceedSVikram Hegde return (set); 23353a634bfcSVikram Hegde } 23363a634bfcSVikram Hegde 23373a634bfcSVikram Hegde /* 23383a634bfcSVikram Hegde * dvma_map() 23393a634bfcSVikram Hegde * map a contiguous range of DVMA pages 23403a634bfcSVikram Hegde * 23413a634bfcSVikram Hegde * immu: IOMMU unit for which we are generating DVMA cookies 23423a634bfcSVikram Hegde * domain: domain 23433a634bfcSVikram Hegde * sdvma: Starting dvma 23443a634bfcSVikram Hegde * spaddr: Starting paddr 23453a634bfcSVikram Hegde * npages: Number of pages 23463a634bfcSVikram Hegde * rdip: requesting device 23473a634bfcSVikram Hegde * immu_flags: flags 23483a634bfcSVikram Hegde */ 2349e03dceedSVikram Hegde static boolean_t 235050200e77SFrank Van Der Linden dvma_map(domain_t *domain, uint64_t sdvma, uint64_t snvpages, 235150200e77SFrank Van Der Linden immu_dcookie_t *dcookies, int dcount, dev_info_t *rdip, 235250200e77SFrank Van Der Linden immu_flags_t immu_flags) 23533a634bfcSVikram Hegde { 23543a634bfcSVikram Hegde uint64_t dvma; 23553a634bfcSVikram Hegde uint64_t n; 235650200e77SFrank Van Der Linden immu_t *immu = domain->dom_immu; 23573a634bfcSVikram Hegde int nlevels = immu->immu_dvma_nlevels; 23583a634bfcSVikram Hegde xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0}; 2359e03dceedSVikram Hegde boolean_t pde_set = B_FALSE; 23603a634bfcSVikram Hegde 2361e03dceedSVikram Hegde n = snvpages; 23623a634bfcSVikram Hegde dvma = sdvma; 23633a634bfcSVikram Hegde 23643a634bfcSVikram Hegde while (n > 0) { 236550200e77SFrank Van Der Linden xlate_setup(dvma, xlate, nlevels); 23663a634bfcSVikram Hegde 23673a634bfcSVikram Hegde /* Lookup or allocate PGDIRs and PGTABLEs if necessary */ 2368e03dceedSVikram Hegde if (PDE_set_all(immu, domain, xlate, nlevels, rdip, immu_flags) 2369e03dceedSVikram Hegde == B_TRUE) { 2370e03dceedSVikram Hegde pde_set = B_TRUE; 2371e03dceedSVikram Hegde } 23723a634bfcSVikram Hegde 23733a634bfcSVikram Hegde /* set all matching ptes that fit into this leaf pgtable */ 2374e03dceedSVikram Hegde PTE_set_all(immu, domain, &xlate[1], &dvma, &n, dcookies, 2375e03dceedSVikram Hegde dcount, rdip, immu_flags); 23763a634bfcSVikram Hegde } 2377e03dceedSVikram Hegde 2378e03dceedSVikram Hegde return (pde_set); 23793a634bfcSVikram Hegde } 23803a634bfcSVikram Hegde 23813a634bfcSVikram Hegde /* 23823a634bfcSVikram Hegde * dvma_unmap() 23833a634bfcSVikram Hegde * unmap a range of DVMAs 23843a634bfcSVikram Hegde * 23853a634bfcSVikram Hegde * immu: IOMMU unit state 23863a634bfcSVikram Hegde * domain: domain for requesting device 23873a634bfcSVikram Hegde * ddip: domain-dip 23883a634bfcSVikram Hegde * dvma: starting DVMA 23893a634bfcSVikram Hegde * npages: Number of IMMU pages to be unmapped 23903a634bfcSVikram Hegde * rdip: requesting device 23913a634bfcSVikram Hegde */ 23923a634bfcSVikram Hegde static void 239350200e77SFrank Van Der Linden dvma_unmap(domain_t *domain, uint64_t sdvma, uint64_t snpages, 23943a634bfcSVikram Hegde dev_info_t *rdip) 23953a634bfcSVikram Hegde { 239650200e77SFrank Van Der Linden immu_t *immu = domain->dom_immu; 23973a634bfcSVikram Hegde int nlevels = immu->immu_dvma_nlevels; 23983a634bfcSVikram Hegde xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0}; 2399e03dceedSVikram Hegde uint64_t n; 2400e03dceedSVikram Hegde uint64_t dvma; 24013a634bfcSVikram Hegde 2402e03dceedSVikram Hegde dvma = sdvma; 2403e03dceedSVikram Hegde n = snpages; 2404e03dceedSVikram Hegde 2405e03dceedSVikram Hegde while (n > 0) { 24063a634bfcSVikram Hegde /* setup the xlate array */ 240750200e77SFrank Van Der Linden xlate_setup(dvma, xlate, nlevels); 24083a634bfcSVikram Hegde 24093a634bfcSVikram Hegde /* just lookup existing pgtables. Should never fail */ 241050200e77SFrank Van Der Linden if (!PDE_lookup(domain, xlate, nlevels)) 241150200e77SFrank Van Der Linden ddi_err(DER_PANIC, rdip, 241250200e77SFrank Van Der Linden "PTE not found for addr %" PRIx64, 241350200e77SFrank Van Der Linden (unsigned long long)dvma); 24143a634bfcSVikram Hegde 2415e03dceedSVikram Hegde /* clear all matching ptes that fit into this leaf pgtable */ 2416e03dceedSVikram Hegde PTE_clear_all(immu, domain, &xlate[1], &dvma, &n, rdip); 24173a634bfcSVikram Hegde } 2418e03dceedSVikram Hegde 2419e03dceedSVikram Hegde /* No need to flush IOTLB after unmap */ 24203a634bfcSVikram Hegde } 24213a634bfcSVikram Hegde 24223a634bfcSVikram Hegde static uint64_t 242350200e77SFrank Van Der Linden dvma_alloc(domain_t *domain, ddi_dma_attr_t *dma_attr, uint_t npages, int kmf) 24243a634bfcSVikram Hegde { 24253a634bfcSVikram Hegde uint64_t dvma; 2426e03dceedSVikram Hegde size_t xsize, align; 24273a634bfcSVikram Hegde uint64_t minaddr, maxaddr; 24283a634bfcSVikram Hegde 24293a634bfcSVikram Hegde /* parameters */ 24303a634bfcSVikram Hegde xsize = npages * IMMU_PAGESIZE; 24313a634bfcSVikram Hegde align = MAX((size_t)(dma_attr->dma_attr_align), IMMU_PAGESIZE); 24323a634bfcSVikram Hegde minaddr = dma_attr->dma_attr_addr_lo; 24333a634bfcSVikram Hegde maxaddr = dma_attr->dma_attr_addr_hi + 1; 24343a634bfcSVikram Hegde 24353a634bfcSVikram Hegde /* handle the rollover cases */ 24363a634bfcSVikram Hegde if (maxaddr < dma_attr->dma_attr_addr_hi) { 24373a634bfcSVikram Hegde maxaddr = dma_attr->dma_attr_addr_hi; 24383a634bfcSVikram Hegde } 24393a634bfcSVikram Hegde 24403a634bfcSVikram Hegde /* 24413a634bfcSVikram Hegde * allocate from vmem arena. 24423a634bfcSVikram Hegde */ 24433a634bfcSVikram Hegde dvma = (uint64_t)(uintptr_t)vmem_xalloc(domain->dom_dvma_arena, 2444e03dceedSVikram Hegde xsize, align, 0, 0, (void *)(uintptr_t)minaddr, 244550200e77SFrank Van Der Linden (void *)(uintptr_t)maxaddr, kmf); 24463a634bfcSVikram Hegde 24473a634bfcSVikram Hegde return (dvma); 24483a634bfcSVikram Hegde } 24493a634bfcSVikram Hegde 24503a634bfcSVikram Hegde static void 245150200e77SFrank Van Der Linden dvma_prealloc(dev_info_t *rdip, immu_hdl_priv_t *ihp, ddi_dma_attr_t *dma_attr) 245250200e77SFrank Van Der Linden { 245350200e77SFrank Van Der Linden int nlevels; 245450200e77SFrank Van Der Linden xlate_t xlate[IMMU_PGTABLE_MAX_LEVELS + 1] = {0}, *xlp; 245550200e77SFrank Van Der Linden uint64_t dvma, n; 245650200e77SFrank Van Der Linden size_t xsize, align; 245750200e77SFrank Van Der Linden uint64_t minaddr, maxaddr, dmamax; 245850200e77SFrank Van Der Linden int on, npte, pindex; 245950200e77SFrank Van Der Linden hw_pdte_t *shwp; 246050200e77SFrank Van Der Linden immu_t *immu; 246150200e77SFrank Van Der Linden domain_t *domain; 246250200e77SFrank Van Der Linden 246350200e77SFrank Van Der Linden /* parameters */ 246450200e77SFrank Van Der Linden domain = IMMU_DEVI(rdip)->imd_domain; 246550200e77SFrank Van Der Linden immu = domain->dom_immu; 246650200e77SFrank Van Der Linden nlevels = immu->immu_dvma_nlevels; 246750200e77SFrank Van Der Linden xsize = IMMU_NPREPTES * IMMU_PAGESIZE; 246850200e77SFrank Van Der Linden align = MAX((size_t)(dma_attr->dma_attr_align), IMMU_PAGESIZE); 246950200e77SFrank Van Der Linden minaddr = dma_attr->dma_attr_addr_lo; 247050200e77SFrank Van Der Linden if (dma_attr->dma_attr_flags & _DDI_DMA_BOUNCE_ON_SEG) 247150200e77SFrank Van Der Linden dmamax = dma_attr->dma_attr_seg; 247250200e77SFrank Van Der Linden else 247350200e77SFrank Van Der Linden dmamax = dma_attr->dma_attr_addr_hi; 247450200e77SFrank Van Der Linden maxaddr = dmamax + 1; 247550200e77SFrank Van Der Linden 247650200e77SFrank Van Der Linden if (maxaddr < dmamax) 247750200e77SFrank Van Der Linden maxaddr = dmamax; 247850200e77SFrank Van Der Linden 247950200e77SFrank Van Der Linden dvma = (uint64_t)(uintptr_t)vmem_xalloc(domain->dom_dvma_arena, 248050200e77SFrank Van Der Linden xsize, align, 0, dma_attr->dma_attr_seg + 1, 248150200e77SFrank Van Der Linden (void *)(uintptr_t)minaddr, (void *)(uintptr_t)maxaddr, VM_NOSLEEP); 248250200e77SFrank Van Der Linden 248350200e77SFrank Van Der Linden ihp->ihp_predvma = dvma; 248450200e77SFrank Van Der Linden ihp->ihp_npremapped = 0; 248550200e77SFrank Van Der Linden if (dvma == 0) 248650200e77SFrank Van Der Linden return; 248750200e77SFrank Van Der Linden 248850200e77SFrank Van Der Linden n = IMMU_NPREPTES; 248950200e77SFrank Van Der Linden pindex = 0; 249050200e77SFrank Van Der Linden 249150200e77SFrank Van Der Linden /* 249250200e77SFrank Van Der Linden * Set up a mapping at address 0, just so that all PDPs get allocated 249350200e77SFrank Van Der Linden * now. Although this initial mapping should never be used, 249450200e77SFrank Van Der Linden * explicitly set it to read-only, just to be safe. 249550200e77SFrank Van Der Linden */ 249650200e77SFrank Van Der Linden while (n > 0) { 249750200e77SFrank Van Der Linden xlate_setup(dvma, xlate, nlevels); 249850200e77SFrank Van Der Linden 249950200e77SFrank Van Der Linden (void) PDE_set_all(immu, domain, xlate, nlevels, rdip, 250050200e77SFrank Van Der Linden IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 250150200e77SFrank Van Der Linden 250250200e77SFrank Van Der Linden xlp = &xlate[1]; 250350200e77SFrank Van Der Linden shwp = (hw_pdte_t *)(xlp->xlt_pgtable->hwpg_vaddr) 250450200e77SFrank Van Der Linden + xlp->xlt_idx; 250550200e77SFrank Van Der Linden on = n; 250650200e77SFrank Van Der Linden 250750200e77SFrank Van Der Linden PTE_set_all(immu, domain, xlp, &dvma, &n, &immu_precookie, 250850200e77SFrank Van Der Linden 1, rdip, IMMU_FLAGS_READ); 250950200e77SFrank Van Der Linden 251050200e77SFrank Van Der Linden npte = on - n; 251150200e77SFrank Van Der Linden 251250200e77SFrank Van Der Linden while (npte > 0) { 251350200e77SFrank Van Der Linden ihp->ihp_preptes[pindex++] = shwp; 251450200e77SFrank Van Der Linden #ifdef BUGGY_DRIVERS 251550200e77SFrank Van Der Linden PDTE_CLEAR_WRITE(*shwp); 251650200e77SFrank Van Der Linden #endif 251750200e77SFrank Van Der Linden shwp++; 251850200e77SFrank Van Der Linden npte--; 251950200e77SFrank Van Der Linden } 252050200e77SFrank Van Der Linden } 252150200e77SFrank Van Der Linden } 252250200e77SFrank Van Der Linden 252350200e77SFrank Van Der Linden static void 252450200e77SFrank Van Der Linden dvma_prefree(dev_info_t *rdip, immu_hdl_priv_t *ihp) 252550200e77SFrank Van Der Linden { 252650200e77SFrank Van Der Linden domain_t *domain; 252750200e77SFrank Van Der Linden 252850200e77SFrank Van Der Linden domain = IMMU_DEVI(rdip)->imd_domain; 252950200e77SFrank Van Der Linden 253050200e77SFrank Van Der Linden if (ihp->ihp_predvma != 0) { 253150200e77SFrank Van Der Linden dvma_unmap(domain, ihp->ihp_predvma, IMMU_NPREPTES, rdip); 253250200e77SFrank Van Der Linden vmem_free(domain->dom_dvma_arena, 253350200e77SFrank Van Der Linden (void *)(uintptr_t)ihp->ihp_predvma, 253450200e77SFrank Van Der Linden IMMU_NPREPTES * IMMU_PAGESIZE); 253550200e77SFrank Van Der Linden } 253650200e77SFrank Van Der Linden } 253750200e77SFrank Van Der Linden 253850200e77SFrank Van Der Linden static void 25393a634bfcSVikram Hegde dvma_free(domain_t *domain, uint64_t dvma, uint64_t npages) 25403a634bfcSVikram Hegde { 25413a634bfcSVikram Hegde uint64_t size = npages * IMMU_PAGESIZE; 25423a634bfcSVikram Hegde 254350200e77SFrank Van Der Linden if (domain->dom_maptype != IMMU_MAPTYPE_XLATE) 25443a634bfcSVikram Hegde return; 25453a634bfcSVikram Hegde 25463a634bfcSVikram Hegde vmem_free(domain->dom_dvma_arena, (void *)(uintptr_t)dvma, size); 25473a634bfcSVikram Hegde } 25483a634bfcSVikram Hegde 25493a634bfcSVikram Hegde static int 255050200e77SFrank Van Der Linden immu_map_dvmaseg(dev_info_t *rdip, ddi_dma_handle_t handle, 255150200e77SFrank Van Der Linden immu_hdl_priv_t *ihp, struct ddi_dma_req *dmareq, 255250200e77SFrank Van Der Linden ddi_dma_obj_t *dma_out) 25533a634bfcSVikram Hegde { 255450200e77SFrank Van Der Linden domain_t *domain; 255550200e77SFrank Van Der Linden immu_t *immu; 255650200e77SFrank Van Der Linden immu_flags_t immu_flags; 25573a634bfcSVikram Hegde ddi_dma_atyp_t buftype; 25583a634bfcSVikram Hegde ddi_dma_obj_t *dmar_object; 255950200e77SFrank Van Der Linden ddi_dma_attr_t *attrp; 256050200e77SFrank Van Der Linden uint64_t offset, paddr, dvma, sdvma, rwmask; 256150200e77SFrank Van Der Linden size_t npages, npgalloc; 256250200e77SFrank Van Der Linden uint_t psize, size, pcnt, dmax; 256350200e77SFrank Van Der Linden page_t **pparray; 256450200e77SFrank Van Der Linden caddr_t vaddr; 256550200e77SFrank Van Der Linden page_t *page; 256650200e77SFrank Van Der Linden struct as *vas; 256750200e77SFrank Van Der Linden immu_dcookie_t *dcookies; 256850200e77SFrank Van Der Linden int pde_set; 25693a634bfcSVikram Hegde 257050200e77SFrank Van Der Linden domain = IMMU_DEVI(rdip)->imd_domain; 257150200e77SFrank Van Der Linden immu = domain->dom_immu; 257250200e77SFrank Van Der Linden immu_flags = dma_to_immu_flags(dmareq); 257350200e77SFrank Van Der Linden 257450200e77SFrank Van Der Linden attrp = &((ddi_dma_impl_t *)handle)->dmai_attr; 257550200e77SFrank Van Der Linden 257650200e77SFrank Van Der Linden dmar_object = &dmareq->dmar_object; 25773a634bfcSVikram Hegde pparray = dmar_object->dmao_obj.virt_obj.v_priv; 25783a634bfcSVikram Hegde vaddr = dmar_object->dmao_obj.virt_obj.v_addr; 25793a634bfcSVikram Hegde buftype = dmar_object->dmao_type; 25803a634bfcSVikram Hegde size = dmar_object->dmao_size; 25813a634bfcSVikram Hegde 258250200e77SFrank Van Der Linden IMMU_DPROBE3(immu__map__dvma, dev_info_t *, rdip, ddi_dma_atyp_t, 258350200e77SFrank Van Der Linden buftype, uint_t, size); 25843a634bfcSVikram Hegde 258550200e77SFrank Van Der Linden dcookies = &ihp->ihp_dcookies[0]; 258650200e77SFrank Van Der Linden 258750200e77SFrank Van Der Linden pcnt = dmax = 0; 25883a634bfcSVikram Hegde 25893a634bfcSVikram Hegde /* retrieve paddr, psize, offset from dmareq */ 25903a634bfcSVikram Hegde if (buftype == DMA_OTYP_PAGES) { 25913a634bfcSVikram Hegde page = dmar_object->dmao_obj.pp_obj.pp_pp; 25923a634bfcSVikram Hegde offset = dmar_object->dmao_obj.pp_obj.pp_offset & 25933a634bfcSVikram Hegde MMU_PAGEOFFSET; 25943a634bfcSVikram Hegde paddr = pfn_to_pa(page->p_pagenum) + offset; 25953a634bfcSVikram Hegde psize = MIN((MMU_PAGESIZE - offset), size); 25963a634bfcSVikram Hegde page = page->p_next; 259750200e77SFrank Van Der Linden vas = dmar_object->dmao_obj.virt_obj.v_as; 25983a634bfcSVikram Hegde } else { 259950200e77SFrank Van Der Linden if (vas == NULL) { 260050200e77SFrank Van Der Linden vas = &kas; 26013a634bfcSVikram Hegde } 26023a634bfcSVikram Hegde offset = (uintptr_t)vaddr & MMU_PAGEOFFSET; 26033a634bfcSVikram Hegde if (pparray != NULL) { 26043a634bfcSVikram Hegde paddr = pfn_to_pa(pparray[pcnt]->p_pagenum) + offset; 26053a634bfcSVikram Hegde psize = MIN((MMU_PAGESIZE - offset), size); 26063a634bfcSVikram Hegde pcnt++; 26073a634bfcSVikram Hegde } else { 260850200e77SFrank Van Der Linden paddr = pfn_to_pa(hat_getpfnum(vas->a_hat, 26093a634bfcSVikram Hegde vaddr)) + offset; 26103a634bfcSVikram Hegde psize = MIN(size, (MMU_PAGESIZE - offset)); 26113a634bfcSVikram Hegde vaddr += psize; 26123a634bfcSVikram Hegde } 26133a634bfcSVikram Hegde } 26143a634bfcSVikram Hegde 261550200e77SFrank Van Der Linden npgalloc = IMMU_BTOPR(size + offset); 26163a634bfcSVikram Hegde 261750200e77SFrank Van Der Linden if (npgalloc <= IMMU_NPREPTES && ihp->ihp_predvma != 0) { 261850200e77SFrank Van Der Linden #ifdef BUGGY_DRIVERS 261950200e77SFrank Van Der Linden rwmask = PDTE_MASK_R | PDTE_MASK_W | immu->immu_ptemask; 262050200e77SFrank Van Der Linden #else 262150200e77SFrank Van Der Linden rwmask = immu->immu_ptemask; 262250200e77SFrank Van Der Linden if (immu_flags & IMMU_FLAGS_READ) 262350200e77SFrank Van Der Linden rwmask |= PDTE_MASK_R; 262450200e77SFrank Van Der Linden if (immu_flags & IMMU_FLAGS_WRITE) 262550200e77SFrank Van Der Linden rwmask |= PDTE_MASK_W; 262650200e77SFrank Van Der Linden #endif 262750200e77SFrank Van Der Linden #ifdef DEBUG 262850200e77SFrank Van Der Linden rwmask |= PDTE_MASK_P; 262950200e77SFrank Van Der Linden #endif 263050200e77SFrank Van Der Linden sdvma = ihp->ihp_predvma; 263150200e77SFrank Van Der Linden ihp->ihp_npremapped = npgalloc; 263250200e77SFrank Van Der Linden *ihp->ihp_preptes[0] = 263350200e77SFrank Van Der Linden PDTE_PADDR(paddr & ~MMU_PAGEOFFSET) | rwmask; 263450200e77SFrank Van Der Linden } else { 263550200e77SFrank Van Der Linden ihp->ihp_npremapped = 0; 263650200e77SFrank Van Der Linden sdvma = dvma_alloc(domain, attrp, npgalloc, 263750200e77SFrank Van Der Linden dmareq->dmar_fp == DDI_DMA_SLEEP ? VM_SLEEP : VM_NOSLEEP); 263850200e77SFrank Van Der Linden if (sdvma == 0) 263950200e77SFrank Van Der Linden return (DDI_DMA_NORESOURCES); 26403a634bfcSVikram Hegde 264150200e77SFrank Van Der Linden dcookies[0].dck_paddr = (paddr & ~MMU_PAGEOFFSET); 264250200e77SFrank Van Der Linden dcookies[0].dck_npages = 1; 264350200e77SFrank Van Der Linden } 264450200e77SFrank Van Der Linden 264550200e77SFrank Van Der Linden IMMU_DPROBE3(immu__dvma__alloc, dev_info_t *, rdip, uint64_t, npgalloc, 264650200e77SFrank Van Der Linden uint64_t, sdvma); 264750200e77SFrank Van Der Linden 264850200e77SFrank Van Der Linden dvma = sdvma; 264950200e77SFrank Van Der Linden pde_set = 0; 265050200e77SFrank Van Der Linden npages = 1; 26513a634bfcSVikram Hegde size -= psize; 26523a634bfcSVikram Hegde while (size > 0) { 26533a634bfcSVikram Hegde /* get the size for this page (i.e. partial or full page) */ 26543a634bfcSVikram Hegde psize = MIN(size, MMU_PAGESIZE); 26553a634bfcSVikram Hegde if (buftype == DMA_OTYP_PAGES) { 26563a634bfcSVikram Hegde /* get the paddr from the page_t */ 26573a634bfcSVikram Hegde paddr = pfn_to_pa(page->p_pagenum); 26583a634bfcSVikram Hegde page = page->p_next; 26593a634bfcSVikram Hegde } else if (pparray != NULL) { 26603a634bfcSVikram Hegde /* index into the array of page_t's to get the paddr */ 26613a634bfcSVikram Hegde paddr = pfn_to_pa(pparray[pcnt]->p_pagenum); 26623a634bfcSVikram Hegde pcnt++; 26633a634bfcSVikram Hegde } else { 26643a634bfcSVikram Hegde /* call into the VM to get the paddr */ 266550200e77SFrank Van Der Linden paddr = pfn_to_pa(hat_getpfnum(vas->a_hat, vaddr)); 26663a634bfcSVikram Hegde vaddr += psize; 26673a634bfcSVikram Hegde } 266850200e77SFrank Van Der Linden 266950200e77SFrank Van Der Linden npages++; 267050200e77SFrank Van Der Linden 267150200e77SFrank Van Der Linden if (ihp->ihp_npremapped > 0) { 267250200e77SFrank Van Der Linden *ihp->ihp_preptes[npages - 1] = 267350200e77SFrank Van Der Linden PDTE_PADDR(paddr) | rwmask; 267450200e77SFrank Van Der Linden } else if (IMMU_CONTIG_PADDR(dcookies[dmax], paddr)) { 267550200e77SFrank Van Der Linden dcookies[dmax].dck_npages++; 267650200e77SFrank Van Der Linden } else { 267750200e77SFrank Van Der Linden /* No, we need a new dcookie */ 267850200e77SFrank Van Der Linden if (dmax == (IMMU_NDCK - 1)) { 26793a634bfcSVikram Hegde /* 268050200e77SFrank Van Der Linden * Ran out of dcookies. Map them now. 26813a634bfcSVikram Hegde */ 268250200e77SFrank Van Der Linden if (dvma_map(domain, dvma, 268350200e77SFrank Van Der Linden npages, dcookies, dmax + 1, rdip, 268450200e77SFrank Van Der Linden immu_flags)) 268550200e77SFrank Van Der Linden pde_set++; 268650200e77SFrank Van Der Linden 268750200e77SFrank Van Der Linden IMMU_DPROBE4(immu__dvmamap__early, 268850200e77SFrank Van Der Linden dev_info_t *, rdip, uint64_t, dvma, 268950200e77SFrank Van Der Linden uint_t, npages, uint_t, dmax+1); 269050200e77SFrank Van Der Linden 269150200e77SFrank Van Der Linden dvma += (npages << IMMU_PAGESHIFT); 269250200e77SFrank Van Der Linden npages = 0; 269350200e77SFrank Van Der Linden dmax = 0; 269450200e77SFrank Van Der Linden } else 269550200e77SFrank Van Der Linden dmax++; 269650200e77SFrank Van Der Linden dcookies[dmax].dck_paddr = paddr; 269750200e77SFrank Van Der Linden dcookies[dmax].dck_npages = 1; 269850200e77SFrank Van Der Linden } 26993a634bfcSVikram Hegde size -= psize; 27003a634bfcSVikram Hegde } 27013a634bfcSVikram Hegde 270250200e77SFrank Van Der Linden /* 270350200e77SFrank Van Der Linden * Finish up, mapping all, or all of the remaining, 270450200e77SFrank Van Der Linden * physical memory ranges. 270550200e77SFrank Van Der Linden */ 270650200e77SFrank Van Der Linden if (ihp->ihp_npremapped == 0 && npages > 0) { 270750200e77SFrank Van Der Linden IMMU_DPROBE4(immu__dvmamap__late, dev_info_t *, rdip, \ 270850200e77SFrank Van Der Linden uint64_t, dvma, uint_t, npages, uint_t, dmax+1); 27093a634bfcSVikram Hegde 271050200e77SFrank Van Der Linden if (dvma_map(domain, dvma, npages, dcookies, 271150200e77SFrank Van Der Linden dmax + 1, rdip, immu_flags)) 271250200e77SFrank Van Der Linden pde_set++; 271350200e77SFrank Van Der Linden } 27143a634bfcSVikram Hegde 271550200e77SFrank Van Der Linden /* Invalidate the IOTLB */ 271650200e77SFrank Van Der Linden immu_flush_iotlb_psi(immu, domain->dom_did, sdvma, npgalloc, 271750200e77SFrank Van Der Linden pde_set > 0 ? TLB_IVA_WHOLE : TLB_IVA_LEAF, 271850200e77SFrank Van Der Linden &ihp->ihp_inv_wait); 271950200e77SFrank Van Der Linden 272050200e77SFrank Van Der Linden ihp->ihp_ndvseg = 1; 272150200e77SFrank Van Der Linden ihp->ihp_dvseg[0].dvs_start = sdvma; 272250200e77SFrank Van Der Linden ihp->ihp_dvseg[0].dvs_len = dmar_object->dmao_size; 272350200e77SFrank Van Der Linden 272450200e77SFrank Van Der Linden dma_out->dmao_size = dmar_object->dmao_size; 272550200e77SFrank Van Der Linden dma_out->dmao_obj.dvma_obj.dv_off = offset & IMMU_PAGEOFFSET; 272650200e77SFrank Van Der Linden dma_out->dmao_obj.dvma_obj.dv_nseg = 1; 272750200e77SFrank Van Der Linden dma_out->dmao_obj.dvma_obj.dv_seg = &ihp->ihp_dvseg[0]; 272850200e77SFrank Van Der Linden dma_out->dmao_type = DMA_OTYP_DVADDR; 272950200e77SFrank Van Der Linden 273050200e77SFrank Van Der Linden return (DDI_DMA_MAPPED); 273150200e77SFrank Van Der Linden } 273250200e77SFrank Van Der Linden 273350200e77SFrank Van Der Linden static int 273450200e77SFrank Van Der Linden immu_unmap_dvmaseg(dev_info_t *rdip, ddi_dma_obj_t *dmao) 273550200e77SFrank Van Der Linden { 273650200e77SFrank Van Der Linden uint64_t dvma, npages; 273750200e77SFrank Van Der Linden domain_t *domain; 273850200e77SFrank Van Der Linden struct dvmaseg *dvs; 273950200e77SFrank Van Der Linden 274050200e77SFrank Van Der Linden domain = IMMU_DEVI(rdip)->imd_domain; 274150200e77SFrank Van Der Linden dvs = dmao->dmao_obj.dvma_obj.dv_seg; 274250200e77SFrank Van Der Linden 274350200e77SFrank Van Der Linden dvma = dvs[0].dvs_start; 274450200e77SFrank Van Der Linden npages = IMMU_BTOPR(dvs[0].dvs_len + dmao->dmao_obj.dvma_obj.dv_off); 274550200e77SFrank Van Der Linden 274650200e77SFrank Van Der Linden #ifdef DEBUG 274750200e77SFrank Van Der Linden /* Unmap only in DEBUG mode */ 274850200e77SFrank Van Der Linden dvma_unmap(domain, dvma, npages, rdip); 274950200e77SFrank Van Der Linden #endif 275050200e77SFrank Van Der Linden dvma_free(domain, dvma, npages); 275150200e77SFrank Van Der Linden 275250200e77SFrank Van Der Linden IMMU_DPROBE3(immu__dvma__free, dev_info_t *, rdip, uint_t, npages, 275350200e77SFrank Van Der Linden uint64_t, dvma); 275450200e77SFrank Van Der Linden 275550200e77SFrank Van Der Linden #ifdef DEBUG 275650200e77SFrank Van Der Linden /* 275750200e77SFrank Van Der Linden * In the DEBUG case, the unmap was actually done, 275850200e77SFrank Van Der Linden * but an IOTLB flush was not done. So, an explicit 275950200e77SFrank Van Der Linden * write back flush is needed. 276050200e77SFrank Van Der Linden */ 276150200e77SFrank Van Der Linden immu_regs_wbf_flush(domain->dom_immu); 276250200e77SFrank Van Der Linden #endif 27633a634bfcSVikram Hegde 27643a634bfcSVikram Hegde return (DDI_SUCCESS); 27653a634bfcSVikram Hegde } 27663a634bfcSVikram Hegde 27673a634bfcSVikram Hegde /* ############################# Functions exported ######################## */ 27683a634bfcSVikram Hegde 27693a634bfcSVikram Hegde /* 27703a634bfcSVikram Hegde * setup the DVMA subsystem 27713a634bfcSVikram Hegde * this code runs only for the first IOMMU unit 27723a634bfcSVikram Hegde */ 27733a634bfcSVikram Hegde void 27743a634bfcSVikram Hegde immu_dvma_setup(list_t *listp) 27753a634bfcSVikram Hegde { 27763a634bfcSVikram Hegde immu_t *immu; 27773a634bfcSVikram Hegde uint_t kval; 27783a634bfcSVikram Hegde size_t nchains; 27793a634bfcSVikram Hegde 27803a634bfcSVikram Hegde /* locks */ 27813a634bfcSVikram Hegde mutex_init(&immu_domain_lock, NULL, MUTEX_DEFAULT, NULL); 27823a634bfcSVikram Hegde 27833a634bfcSVikram Hegde /* Create lists */ 27843a634bfcSVikram Hegde list_create(&immu_unity_domain_list, sizeof (domain_t), 27853a634bfcSVikram Hegde offsetof(domain_t, dom_maptype_node)); 27863a634bfcSVikram Hegde list_create(&immu_xlate_domain_list, sizeof (domain_t), 27873a634bfcSVikram Hegde offsetof(domain_t, dom_maptype_node)); 27883a634bfcSVikram Hegde 27893a634bfcSVikram Hegde /* Setup BDF domain hash */ 27903a634bfcSVikram Hegde nchains = 0xff; 27913a634bfcSVikram Hegde kval = mod_hash_iddata_gen(nchains); 27923a634bfcSVikram Hegde 27933a634bfcSVikram Hegde bdf_domain_hash = mod_hash_create_extended("BDF-DOMAIN_HASH", 27943a634bfcSVikram Hegde nchains, mod_hash_null_keydtor, mod_hash_null_valdtor, 27953a634bfcSVikram Hegde mod_hash_byid, (void *)(uintptr_t)kval, mod_hash_idkey_cmp, 27963a634bfcSVikram Hegde KM_NOSLEEP); 27973a634bfcSVikram Hegde 27983a634bfcSVikram Hegde immu = list_head(listp); 27993a634bfcSVikram Hegde for (; immu; immu = list_next(listp, immu)) { 28003a634bfcSVikram Hegde create_unity_domain(immu); 28013a634bfcSVikram Hegde did_init(immu); 28023a634bfcSVikram Hegde context_init(immu); 28033a634bfcSVikram Hegde immu->immu_dvma_setup = B_TRUE; 28043a634bfcSVikram Hegde } 28053a634bfcSVikram Hegde } 28063a634bfcSVikram Hegde 28073a634bfcSVikram Hegde /* 28083a634bfcSVikram Hegde * Startup up one DVMA unit 28093a634bfcSVikram Hegde */ 28103a634bfcSVikram Hegde void 28113a634bfcSVikram Hegde immu_dvma_startup(immu_t *immu) 28123a634bfcSVikram Hegde { 28133a634bfcSVikram Hegde if (immu_gfxdvma_enable == B_FALSE && 28143a634bfcSVikram Hegde immu->immu_dvma_gfx_only == B_TRUE) { 28153a634bfcSVikram Hegde return; 28163a634bfcSVikram Hegde } 28173a634bfcSVikram Hegde 28183a634bfcSVikram Hegde /* 28193a634bfcSVikram Hegde * DVMA will start once IOMMU is "running" 28203a634bfcSVikram Hegde */ 28213a634bfcSVikram Hegde immu->immu_dvma_running = B_TRUE; 28223a634bfcSVikram Hegde } 28233a634bfcSVikram Hegde 28243a634bfcSVikram Hegde /* 28253a634bfcSVikram Hegde * immu_dvma_physmem_update() 28263a634bfcSVikram Hegde * called when the installed memory on a 28273a634bfcSVikram Hegde * system increases, to expand domain DVMA 28283a634bfcSVikram Hegde * for domains with UNITY mapping 28293a634bfcSVikram Hegde */ 28303a634bfcSVikram Hegde void 28313a634bfcSVikram Hegde immu_dvma_physmem_update(uint64_t addr, uint64_t size) 28323a634bfcSVikram Hegde { 28333a634bfcSVikram Hegde uint64_t start; 28343a634bfcSVikram Hegde uint64_t npages; 2835e03dceedSVikram Hegde int dcount; 283650200e77SFrank Van Der Linden immu_dcookie_t dcookies[1] = {0}; 28373a634bfcSVikram Hegde domain_t *domain; 28383a634bfcSVikram Hegde 28393a634bfcSVikram Hegde /* 28403a634bfcSVikram Hegde * Just walk the system-wide list of domains with 28413a634bfcSVikram Hegde * UNITY mapping. Both the list of *all* domains 28423a634bfcSVikram Hegde * and *UNITY* domains is protected by the same 28433a634bfcSVikram Hegde * single lock 28443a634bfcSVikram Hegde */ 28453a634bfcSVikram Hegde mutex_enter(&immu_domain_lock); 28463a634bfcSVikram Hegde domain = list_head(&immu_unity_domain_list); 28473a634bfcSVikram Hegde for (; domain; domain = list_next(&immu_unity_domain_list, domain)) { 2848be56ae36SFrank Van Der Linden /* 2849be56ae36SFrank Van Der Linden * Nothing to do if the IOMMU supports passthrough. 2850be56ae36SFrank Van Der Linden */ 2851be56ae36SFrank Van Der Linden if (IMMU_ECAP_GET_PT(domain->dom_immu->immu_regs_excap)) 2852be56ae36SFrank Van Der Linden continue; 28533a634bfcSVikram Hegde 28543a634bfcSVikram Hegde /* There is no vmem_arena for unity domains. Just map it */ 285550200e77SFrank Van Der Linden ddi_err(DER_LOG, domain->dom_dip, 285650200e77SFrank Van Der Linden "iommu: unity-domain: Adding map " 28573a634bfcSVikram Hegde "[0x%" PRIx64 " - 0x%" PRIx64 "]", addr, addr + size); 28583a634bfcSVikram Hegde 28593a634bfcSVikram Hegde start = IMMU_ROUNDOWN(addr); 28603a634bfcSVikram Hegde npages = (IMMU_ROUNDUP(size) / IMMU_PAGESIZE) + 1; 28613a634bfcSVikram Hegde 2862e03dceedSVikram Hegde dcookies[0].dck_paddr = start; 2863e03dceedSVikram Hegde dcookies[0].dck_npages = npages; 2864e03dceedSVikram Hegde dcount = 1; 286550200e77SFrank Van Der Linden (void) dvma_map(domain, start, npages, 2866e03dceedSVikram Hegde dcookies, dcount, NULL, IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 28673a634bfcSVikram Hegde 28683a634bfcSVikram Hegde } 28693a634bfcSVikram Hegde mutex_exit(&immu_domain_lock); 28703a634bfcSVikram Hegde } 28713a634bfcSVikram Hegde 28723a634bfcSVikram Hegde int 287350200e77SFrank Van Der Linden immu_dvma_device_setup(dev_info_t *rdip, immu_flags_t immu_flags) 28743a634bfcSVikram Hegde { 287550200e77SFrank Van Der Linden dev_info_t *ddip, *odip; 28763a634bfcSVikram Hegde immu_t *immu; 287750200e77SFrank Van Der Linden domain_t *domain; 28783a634bfcSVikram Hegde 287950200e77SFrank Van Der Linden odip = rdip; 28803a634bfcSVikram Hegde 2881e03dceedSVikram Hegde immu = immu_dvma_get_immu(rdip, immu_flags); 2882e03dceedSVikram Hegde if (immu == NULL) { 2883e03dceedSVikram Hegde /* 2884e03dceedSVikram Hegde * possible that there is no IOMMU unit for this device 2885e03dceedSVikram Hegde * - BIOS bugs are one example. 2886e03dceedSVikram Hegde */ 288750200e77SFrank Van Der Linden ddi_err(DER_WARN, rdip, "No iommu unit found for device"); 2888e03dceedSVikram Hegde return (DDI_DMA_NORESOURCES); 2889e03dceedSVikram Hegde } 2890e03dceedSVikram Hegde 2891e03dceedSVikram Hegde /* 2892e03dceedSVikram Hegde * redirect isa devices attached under lpc to lpc dip 2893e03dceedSVikram Hegde */ 2894e03dceedSVikram Hegde if (strcmp(ddi_node_name(ddi_get_parent(rdip)), "isa") == 0) { 2895e03dceedSVikram Hegde rdip = get_lpc_devinfo(immu, rdip, immu_flags); 2896e03dceedSVikram Hegde if (rdip == NULL) { 289750200e77SFrank Van Der Linden ddi_err(DER_PANIC, rdip, "iommu redirect failed"); 2898e03dceedSVikram Hegde /*NOTREACHED*/ 2899e03dceedSVikram Hegde } 2900e03dceedSVikram Hegde } 2901e03dceedSVikram Hegde 2902e03dceedSVikram Hegde /* Reset immu, as redirection can change IMMU */ 2903e03dceedSVikram Hegde immu = NULL; 2904e03dceedSVikram Hegde 2905e03dceedSVikram Hegde /* 2906e03dceedSVikram Hegde * for gart, redirect to the real graphic devinfo 2907e03dceedSVikram Hegde */ 2908e03dceedSVikram Hegde if (strcmp(ddi_node_name(rdip), "agpgart") == 0) { 2909e03dceedSVikram Hegde rdip = get_gfx_devinfo(rdip); 2910e03dceedSVikram Hegde if (rdip == NULL) { 291150200e77SFrank Van Der Linden ddi_err(DER_PANIC, rdip, "iommu redirect failed"); 2912e03dceedSVikram Hegde /*NOTREACHED*/ 2913e03dceedSVikram Hegde } 2914e03dceedSVikram Hegde } 2915e03dceedSVikram Hegde 29163a634bfcSVikram Hegde /* 29173a634bfcSVikram Hegde * Setup DVMA domain for the device. This does 29183a634bfcSVikram Hegde * work only the first time we do DVMA for a 29193a634bfcSVikram Hegde * device. 29203a634bfcSVikram Hegde */ 29213a634bfcSVikram Hegde ddip = NULL; 29223a634bfcSVikram Hegde domain = device_domain(rdip, &ddip, immu_flags); 29233a634bfcSVikram Hegde if (domain == NULL) { 29243a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "Intel IOMMU setup failed for device"); 29253a634bfcSVikram Hegde return (DDI_DMA_NORESOURCES); 29263a634bfcSVikram Hegde } 29273a634bfcSVikram Hegde 292850200e77SFrank Van Der Linden immu = domain->dom_immu; 292950200e77SFrank Van Der Linden 29303a634bfcSVikram Hegde /* 29313a634bfcSVikram Hegde * If a domain is found, we must also have a domain dip 29323a634bfcSVikram Hegde * which is the topmost ancestor dip of rdip that shares 29333a634bfcSVikram Hegde * the same domain with rdip. 29343a634bfcSVikram Hegde */ 29353a634bfcSVikram Hegde if (domain->dom_did == 0 || ddip == NULL) { 29363a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "domain did 0(%d) or ddip NULL(%p)", 29373a634bfcSVikram Hegde domain->dom_did, ddip); 29383a634bfcSVikram Hegde return (DDI_DMA_NORESOURCES); 29393a634bfcSVikram Hegde } 29403a634bfcSVikram Hegde 294150200e77SFrank Van Der Linden if (odip != rdip) 294250200e77SFrank Van Der Linden set_domain(odip, ddip, domain); 29433a634bfcSVikram Hegde 29443a634bfcSVikram Hegde /* 29453a634bfcSVikram Hegde * Update the root and context entries 29463a634bfcSVikram Hegde */ 29473a634bfcSVikram Hegde if (immu_context_update(immu, domain, ddip, rdip, immu_flags) 29483a634bfcSVikram Hegde != DDI_SUCCESS) { 29493a634bfcSVikram Hegde ddi_err(DER_MODE, rdip, "DVMA map: context update failed"); 29503a634bfcSVikram Hegde return (DDI_DMA_NORESOURCES); 29513a634bfcSVikram Hegde } 29523a634bfcSVikram Hegde 295350200e77SFrank Van Der Linden return (DDI_SUCCESS); 29543a634bfcSVikram Hegde } 29553a634bfcSVikram Hegde 29563a634bfcSVikram Hegde int 295750200e77SFrank Van Der Linden immu_map_memrange(dev_info_t *rdip, memrng_t *mrng) 29583a634bfcSVikram Hegde { 295950200e77SFrank Van Der Linden immu_dcookie_t dcookies[1] = {0}; 296050200e77SFrank Van Der Linden boolean_t pde_set; 29613a634bfcSVikram Hegde immu_t *immu; 296250200e77SFrank Van Der Linden domain_t *domain; 296350200e77SFrank Van Der Linden immu_inv_wait_t iw; 29643a634bfcSVikram Hegde 296550200e77SFrank Van Der Linden dcookies[0].dck_paddr = mrng->mrng_start; 296650200e77SFrank Van Der Linden dcookies[0].dck_npages = mrng->mrng_npages; 29673a634bfcSVikram Hegde 296850200e77SFrank Van Der Linden domain = IMMU_DEVI(rdip)->imd_domain; 29693a634bfcSVikram Hegde immu = domain->dom_immu; 29703a634bfcSVikram Hegde 297150200e77SFrank Van Der Linden pde_set = dvma_map(domain, mrng->mrng_start, 297250200e77SFrank Van Der Linden mrng->mrng_npages, dcookies, 1, rdip, 297350200e77SFrank Van Der Linden IMMU_FLAGS_READ | IMMU_FLAGS_WRITE); 29743a634bfcSVikram Hegde 297550200e77SFrank Van Der Linden immu_init_inv_wait(&iw, "memrange", B_TRUE); 29763a634bfcSVikram Hegde 297750200e77SFrank Van Der Linden immu_flush_iotlb_psi(immu, domain->dom_did, mrng->mrng_start, 297850200e77SFrank Van Der Linden mrng->mrng_npages, pde_set == B_TRUE ? 297950200e77SFrank Van Der Linden TLB_IVA_WHOLE : TLB_IVA_LEAF, &iw); 29803a634bfcSVikram Hegde 29813a634bfcSVikram Hegde return (DDI_SUCCESS); 29823a634bfcSVikram Hegde } 29833a634bfcSVikram Hegde 29843a634bfcSVikram Hegde immu_devi_t * 29853a634bfcSVikram Hegde immu_devi_get(dev_info_t *rdip) 29863a634bfcSVikram Hegde { 29873a634bfcSVikram Hegde immu_devi_t *immu_devi; 2988e03dceedSVikram Hegde volatile uintptr_t *vptr = (uintptr_t *)&(DEVI(rdip)->devi_iommu); 29893a634bfcSVikram Hegde 2990e03dceedSVikram Hegde /* Just want atomic reads. No need for lock */ 2991e03dceedSVikram Hegde immu_devi = (immu_devi_t *)(uintptr_t)atomic_or_64_nv((uint64_t *)vptr, 2992e03dceedSVikram Hegde 0); 29933a634bfcSVikram Hegde return (immu_devi); 29943a634bfcSVikram Hegde } 299550200e77SFrank Van Der Linden 299650200e77SFrank Van Der Linden /*ARGSUSED*/ 299750200e77SFrank Van Der Linden int 299850200e77SFrank Van Der Linden immu_hdl_priv_ctor(void *buf, void *arg, int kmf) 299950200e77SFrank Van Der Linden { 300050200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 300150200e77SFrank Van Der Linden 300250200e77SFrank Van Der Linden ihp = buf; 300350200e77SFrank Van Der Linden immu_init_inv_wait(&ihp->ihp_inv_wait, "dmahandle", B_FALSE); 300450200e77SFrank Van Der Linden 300550200e77SFrank Van Der Linden return (0); 300650200e77SFrank Van Der Linden } 300750200e77SFrank Van Der Linden 300850200e77SFrank Van Der Linden /* 300950200e77SFrank Van Der Linden * iommulib interface functions 301050200e77SFrank Van Der Linden */ 301150200e77SFrank Van Der Linden static int 301250200e77SFrank Van Der Linden immu_probe(iommulib_handle_t handle, dev_info_t *dip) 301350200e77SFrank Van Der Linden { 301450200e77SFrank Van Der Linden immu_devi_t *immu_devi; 301550200e77SFrank Van Der Linden int ret; 301650200e77SFrank Van Der Linden 301750200e77SFrank Van Der Linden if (!immu_enable) 301850200e77SFrank Van Der Linden return (DDI_FAILURE); 301950200e77SFrank Van Der Linden 302050200e77SFrank Van Der Linden /* 302150200e77SFrank Van Der Linden * Make sure the device has all the IOMMU structures 302250200e77SFrank Van Der Linden * initialized. If this device goes through an IOMMU 302350200e77SFrank Van Der Linden * unit (e.g. this probe function returns success), 302450200e77SFrank Van Der Linden * this will be called at most N times, with N being 302550200e77SFrank Van Der Linden * the number of IOMMUs in the system. 302650200e77SFrank Van Der Linden * 302750200e77SFrank Van Der Linden * After that, when iommulib_nex_open succeeds, 302850200e77SFrank Van Der Linden * we can always assume that this device has all 302950200e77SFrank Van Der Linden * the structures initialized. IOMMU_USED(dip) will 303050200e77SFrank Van Der Linden * be true. There is no need to find the controlling 303150200e77SFrank Van Der Linden * IOMMU/domain again. 303250200e77SFrank Van Der Linden */ 303350200e77SFrank Van Der Linden ret = immu_dvma_device_setup(dip, IMMU_FLAGS_NOSLEEP); 303450200e77SFrank Van Der Linden if (ret != DDI_SUCCESS) 303550200e77SFrank Van Der Linden return (ret); 303650200e77SFrank Van Der Linden 303750200e77SFrank Van Der Linden immu_devi = IMMU_DEVI(dip); 303850200e77SFrank Van Der Linden 303950200e77SFrank Van Der Linden /* 304050200e77SFrank Van Der Linden * For unity domains, there is no need to call in to 304150200e77SFrank Van Der Linden * the IOMMU code. 304250200e77SFrank Van Der Linden */ 304350200e77SFrank Van Der Linden if (immu_devi->imd_domain->dom_did == IMMU_UNITY_DID) 304450200e77SFrank Van Der Linden return (DDI_FAILURE); 304550200e77SFrank Van Der Linden 304650200e77SFrank Van Der Linden if (immu_devi->imd_immu->immu_dip == iommulib_iommu_getdip(handle)) 304750200e77SFrank Van Der Linden return (DDI_SUCCESS); 304850200e77SFrank Van Der Linden 304950200e77SFrank Van Der Linden return (DDI_FAILURE); 305050200e77SFrank Van Der Linden } 305150200e77SFrank Van Der Linden 305250200e77SFrank Van Der Linden /*ARGSUSED*/ 305350200e77SFrank Van Der Linden static int 305450200e77SFrank Van Der Linden immu_allochdl(iommulib_handle_t handle, 305550200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, 305650200e77SFrank Van Der Linden int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep) 305750200e77SFrank Van Der Linden { 305850200e77SFrank Van Der Linden int ret; 305950200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 306050200e77SFrank Van Der Linden immu_t *immu; 306150200e77SFrank Van Der Linden 306250200e77SFrank Van Der Linden ret = iommulib_iommu_dma_allochdl(dip, rdip, attr, waitfp, 306350200e77SFrank Van Der Linden arg, dma_handlep); 306450200e77SFrank Van Der Linden if (ret == DDI_SUCCESS) { 306550200e77SFrank Van Der Linden immu = IMMU_DEVI(rdip)->imd_immu; 306650200e77SFrank Van Der Linden 306750200e77SFrank Van Der Linden ihp = kmem_cache_alloc(immu->immu_hdl_cache, 306850200e77SFrank Van Der Linden waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP); 306950200e77SFrank Van Der Linden if (ihp == NULL) { 307050200e77SFrank Van Der Linden (void) iommulib_iommu_dma_freehdl(dip, rdip, 307150200e77SFrank Van Der Linden *dma_handlep); 307250200e77SFrank Van Der Linden return (DDI_DMA_NORESOURCES); 307350200e77SFrank Van Der Linden } 307450200e77SFrank Van Der Linden 307550200e77SFrank Van Der Linden if (IMMU_DEVI(rdip)->imd_use_premap) 307650200e77SFrank Van Der Linden dvma_prealloc(rdip, ihp, attr); 307750200e77SFrank Van Der Linden else { 307850200e77SFrank Van Der Linden ihp->ihp_npremapped = 0; 307950200e77SFrank Van Der Linden ihp->ihp_predvma = 0; 308050200e77SFrank Van Der Linden } 308150200e77SFrank Van Der Linden ret = iommulib_iommu_dmahdl_setprivate(dip, rdip, *dma_handlep, 308250200e77SFrank Van Der Linden ihp); 308350200e77SFrank Van Der Linden } 308450200e77SFrank Van Der Linden return (ret); 308550200e77SFrank Van Der Linden } 308650200e77SFrank Van Der Linden 308750200e77SFrank Van Der Linden /*ARGSUSED*/ 308850200e77SFrank Van Der Linden static int 308950200e77SFrank Van Der Linden immu_freehdl(iommulib_handle_t handle, 309050200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle) 309150200e77SFrank Van Der Linden { 309250200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 309350200e77SFrank Van Der Linden 309450200e77SFrank Van Der Linden ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle); 309550200e77SFrank Van Der Linden if (ihp != NULL) { 309650200e77SFrank Van Der Linden if (IMMU_DEVI(rdip)->imd_use_premap) 309750200e77SFrank Van Der Linden dvma_prefree(rdip, ihp); 309850200e77SFrank Van Der Linden kmem_cache_free(IMMU_DEVI(rdip)->imd_immu->immu_hdl_cache, ihp); 309950200e77SFrank Van Der Linden } 310050200e77SFrank Van Der Linden 310150200e77SFrank Van Der Linden return (iommulib_iommu_dma_freehdl(dip, rdip, dma_handle)); 310250200e77SFrank Van Der Linden } 310350200e77SFrank Van Der Linden 310450200e77SFrank Van Der Linden 310550200e77SFrank Van Der Linden /*ARGSUSED*/ 310650200e77SFrank Van Der Linden static int 310750200e77SFrank Van Der Linden immu_bindhdl(iommulib_handle_t handle, dev_info_t *dip, 310850200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, 310950200e77SFrank Van Der Linden struct ddi_dma_req *dma_req, ddi_dma_cookie_t *cookiep, 311050200e77SFrank Van Der Linden uint_t *ccountp) 311150200e77SFrank Van Der Linden { 311250200e77SFrank Van Der Linden int ret; 311350200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 311450200e77SFrank Van Der Linden 311550200e77SFrank Van Der Linden ret = iommulib_iommu_dma_bindhdl(dip, rdip, dma_handle, 311650200e77SFrank Van Der Linden dma_req, cookiep, ccountp); 311750200e77SFrank Van Der Linden 311850200e77SFrank Van Der Linden if (ret == DDI_DMA_MAPPED) { 311950200e77SFrank Van Der Linden ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle); 312050200e77SFrank Van Der Linden immu_flush_wait(IMMU_DEVI(rdip)->imd_immu, &ihp->ihp_inv_wait); 312150200e77SFrank Van Der Linden } 312250200e77SFrank Van Der Linden 312350200e77SFrank Van Der Linden return (ret); 312450200e77SFrank Van Der Linden } 312550200e77SFrank Van Der Linden 312650200e77SFrank Van Der Linden /*ARGSUSED*/ 312750200e77SFrank Van Der Linden static int 312850200e77SFrank Van Der Linden immu_unbindhdl(iommulib_handle_t handle, 312950200e77SFrank Van Der Linden dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle) 313050200e77SFrank Van Der Linden { 313150200e77SFrank Van Der Linden return (iommulib_iommu_dma_unbindhdl(dip, rdip, dma_handle)); 313250200e77SFrank Van Der Linden } 313350200e77SFrank Van Der Linden 313450200e77SFrank Van Der Linden /*ARGSUSED*/ 313550200e77SFrank Van Der Linden static int 313650200e77SFrank Van Der Linden immu_sync(iommulib_handle_t handle, dev_info_t *dip, 313750200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off, 313850200e77SFrank Van Der Linden size_t len, uint_t cachefl) 313950200e77SFrank Van Der Linden { 314050200e77SFrank Van Der Linden return (iommulib_iommu_dma_sync(dip, rdip, dma_handle, off, len, 314150200e77SFrank Van Der Linden cachefl)); 314250200e77SFrank Van Der Linden } 314350200e77SFrank Van Der Linden 314450200e77SFrank Van Der Linden /*ARGSUSED*/ 314550200e77SFrank Van Der Linden static int 314650200e77SFrank Van Der Linden immu_win(iommulib_handle_t handle, dev_info_t *dip, 314750200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win, 314850200e77SFrank Van Der Linden off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, 314950200e77SFrank Van Der Linden uint_t *ccountp) 315050200e77SFrank Van Der Linden { 315150200e77SFrank Van Der Linden return (iommulib_iommu_dma_win(dip, rdip, dma_handle, win, offp, 315250200e77SFrank Van Der Linden lenp, cookiep, ccountp)); 315350200e77SFrank Van Der Linden } 315450200e77SFrank Van Der Linden 315550200e77SFrank Van Der Linden /*ARGSUSED*/ 315650200e77SFrank Van Der Linden static int 315750200e77SFrank Van Der Linden immu_mapobject(iommulib_handle_t handle, dev_info_t *dip, 315850200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, 315950200e77SFrank Van Der Linden struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao) 316050200e77SFrank Van Der Linden { 316150200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 316250200e77SFrank Van Der Linden 316350200e77SFrank Van Der Linden ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle); 316450200e77SFrank Van Der Linden 316550200e77SFrank Van Der Linden return (immu_map_dvmaseg(rdip, dma_handle, ihp, dmareq, dmao)); 316650200e77SFrank Van Der Linden } 316750200e77SFrank Van Der Linden 316850200e77SFrank Van Der Linden /*ARGSUSED*/ 316950200e77SFrank Van Der Linden static int 317050200e77SFrank Van Der Linden immu_unmapobject(iommulib_handle_t handle, dev_info_t *dip, 317150200e77SFrank Van Der Linden dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao) 317250200e77SFrank Van Der Linden { 317350200e77SFrank Van Der Linden immu_hdl_priv_t *ihp; 317450200e77SFrank Van Der Linden 317550200e77SFrank Van Der Linden ihp = iommulib_iommu_dmahdl_getprivate(dip, rdip, dma_handle); 317650200e77SFrank Van Der Linden if (ihp->ihp_npremapped > 0) 317750200e77SFrank Van Der Linden return (DDI_SUCCESS); 317850200e77SFrank Van Der Linden return (immu_unmap_dvmaseg(rdip, dmao)); 317950200e77SFrank Van Der Linden } 3180