17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 549f91442Ssuha * Common Development and Distribution License (the "License"). 649f91442Ssuha * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2249f91442Ssuha * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 25*cd21e7c5SGarrett D'Amore /* 26*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 27*cd21e7c5SGarrett D'Amore */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * PCI iommu initialization and configuration 317c478bd9Sstevel@tonic-gate */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 357c478bd9Sstevel@tonic-gate #include <sys/async.h> 367c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 377c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 387c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 397c478bd9Sstevel@tonic-gate #include <sys/vmem.h> 407c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> /* lddphys() */ 417c478bd9Sstevel@tonic-gate #include <sys/iommutsb.h> 427c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h> 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate static void iommu_tlb_flushall(iommu_t *iommu_p); 477c478bd9Sstevel@tonic-gate static void iommu_preserve_tsb(iommu_t *iommu_p); 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate void 507c478bd9Sstevel@tonic-gate iommu_create(pci_t *pci_p) 517c478bd9Sstevel@tonic-gate { 527c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 537c478bd9Sstevel@tonic-gate iommu_t *iommu_p; 547c478bd9Sstevel@tonic-gate uintptr_t a; 557c478bd9Sstevel@tonic-gate size_t cache_size; 567c478bd9Sstevel@tonic-gate uint32_t tsb_entries; 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate char map_name[32]; 597c478bd9Sstevel@tonic-gate extern uint64_t va_to_pa(void *); 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate pci_dvma_range_prop_t pci_dvma_range; 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate /* 647c478bd9Sstevel@tonic-gate * Allocate iommu state structure and link it to the 657c478bd9Sstevel@tonic-gate * pci state structure. 667c478bd9Sstevel@tonic-gate */ 677c478bd9Sstevel@tonic-gate iommu_p = (iommu_t *)kmem_zalloc(sizeof (iommu_t), KM_SLEEP); 687c478bd9Sstevel@tonic-gate pci_p->pci_iommu_p = iommu_p; 697c478bd9Sstevel@tonic-gate iommu_p->iommu_pci_p = pci_p; 707c478bd9Sstevel@tonic-gate iommu_p->iommu_inst = ddi_get_instance(dip); 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate /* 737c478bd9Sstevel@tonic-gate * chip specific dvma_end, tsb_size & context support 747c478bd9Sstevel@tonic-gate */ 757c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_end = pci_iommu_dvma_end; 767c478bd9Sstevel@tonic-gate a = pci_iommu_setup(iommu_p); 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate /* 797c478bd9Sstevel@tonic-gate * Determine the virtual address of iommu registers. 807c478bd9Sstevel@tonic-gate */ 817c478bd9Sstevel@tonic-gate iommu_p->iommu_ctrl_reg = 827c478bd9Sstevel@tonic-gate (uint64_t *)(a + COMMON_IOMMU_CTRL_REG_OFFSET); 837c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_base_addr_reg = 847c478bd9Sstevel@tonic-gate (uint64_t *)(a + COMMON_IOMMU_TSB_BASE_ADDR_REG_OFFSET); 857c478bd9Sstevel@tonic-gate iommu_p->iommu_flush_page_reg = 867c478bd9Sstevel@tonic-gate (uint64_t *)(a + COMMON_IOMMU_FLUSH_PAGE_REG_OFFSET); 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate /* 897c478bd9Sstevel@tonic-gate * Configure the rest of the iommu parameters according to: 907c478bd9Sstevel@tonic-gate * tsb_size and dvma_end 917c478bd9Sstevel@tonic-gate */ 927c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_vaddr = /* retrieve TSB VA reserved by system */ 937c478bd9Sstevel@tonic-gate iommu_tsb_cookie_to_va(pci_p->pci_tsb_cookie); 947c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_entries = tsb_entries = 957c478bd9Sstevel@tonic-gate IOMMU_TSBSIZE_TO_TSBENTRIES(iommu_p->iommu_tsb_size); 967c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_paddr = va_to_pa((caddr_t)iommu_p->iommu_tsb_vaddr); 977c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_cache_locks = 987c478bd9Sstevel@tonic-gate kmem_zalloc(pci_dvma_page_cache_entries, KM_SLEEP); 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_base = iommu_p->iommu_dvma_end + 1 1017c478bd9Sstevel@tonic-gate - (tsb_entries * IOMMU_PAGE_SIZE); 1027c478bd9Sstevel@tonic-gate iommu_p->dvma_base_pg = IOMMU_BTOP(iommu_p->iommu_dvma_base); 1037c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_reserve = tsb_entries >> 1; 1047c478bd9Sstevel@tonic-gate iommu_p->dvma_end_pg = IOMMU_BTOP(iommu_p->iommu_dvma_end); 1057c478bd9Sstevel@tonic-gate iommu_p->iommu_dma_bypass_base = COMMON_IOMMU_BYPASS_BASE; 10649f91442Ssuha iommu_p->iommu_dma_bypass_end = pci_iommu_bypass_end_configure(); 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * export "virtual-dma" software property to support 1107c478bd9Sstevel@tonic-gate * child devices needing to know DVMA range 1117c478bd9Sstevel@tonic-gate */ 1127c478bd9Sstevel@tonic-gate pci_dvma_range.dvma_base = (uint32_t)iommu_p->iommu_dvma_base; 1137c478bd9Sstevel@tonic-gate pci_dvma_range.dvma_len = (uint32_t) 1147c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_end - iommu_p->iommu_dvma_base + 1; 1157c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, 1167c478bd9Sstevel@tonic-gate "virtual-dma", (caddr_t)&pci_dvma_range, 1177c478bd9Sstevel@tonic-gate sizeof (pci_dvma_range)); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "iommu_create: ctrl=%p, tsb=%p\n", 1207c478bd9Sstevel@tonic-gate iommu_p->iommu_ctrl_reg, iommu_p->iommu_tsb_base_addr_reg); 1217c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "iommu_create: page_flush=%p, ctx_flush=%p\n", 1227c478bd9Sstevel@tonic-gate iommu_p->iommu_flush_page_reg, iommu_p->iommu_flush_ctx_reg); 1237c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "iommu_create: tsb vaddr=%p tsb_paddr=%p\n", 1247c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_vaddr, iommu_p->iommu_tsb_paddr); 1257c478bd9Sstevel@tonic-gate DEBUG1(DBG_ATTACH, dip, "iommu_create: allocated size=%x\n", 1267c478bd9Sstevel@tonic-gate iommu_tsb_cookie_to_size(pci_p->pci_tsb_cookie)); 1277c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "iommu_create: fast tsb tte addr: %x + %x\n", 1287c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_vaddr, 1297c478bd9Sstevel@tonic-gate pci_dvma_page_cache_entries * pci_dvma_page_cache_clustsz); 1307c478bd9Sstevel@tonic-gate DEBUG3(DBG_ATTACH, dip, 1317c478bd9Sstevel@tonic-gate "iommu_create: tsb size=%x, tsb entries=%x, dvma base=%x\n", 1327c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_size, iommu_p->iommu_tsb_entries, 1337c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_base); 1347c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, 1357c478bd9Sstevel@tonic-gate "iommu_create: dvma_cache_locks=%x cache_entries=%x\n", 1367c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_cache_locks, pci_dvma_page_cache_entries); 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* 1397c478bd9Sstevel@tonic-gate * zero out the area to be used for iommu tsb 1407c478bd9Sstevel@tonic-gate */ 1417c478bd9Sstevel@tonic-gate bzero(iommu_p->iommu_tsb_vaddr, tsb_entries << 3); 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate /* 1447c478bd9Sstevel@tonic-gate * Create a virtual memory map for dvma address space. 1457c478bd9Sstevel@tonic-gate * Reserve 'size' bytes of low dvma space for fast track cache. 1467c478bd9Sstevel@tonic-gate */ 1477c478bd9Sstevel@tonic-gate (void) snprintf(map_name, sizeof (map_name), "%s%d_dvma", 1487c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate cache_size = IOMMU_PTOB(pci_dvma_page_cache_entries * 1517c478bd9Sstevel@tonic-gate pci_dvma_page_cache_clustsz); 1527c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_fast_end = iommu_p->iommu_dvma_base + 1537c478bd9Sstevel@tonic-gate cache_size - 1; 1547c478bd9Sstevel@tonic-gate iommu_p->iommu_dvma_map = vmem_create(map_name, 1557c478bd9Sstevel@tonic-gate (void *)(iommu_p->iommu_dvma_fast_end + 1), 1567c478bd9Sstevel@tonic-gate IOMMU_PTOB(tsb_entries) - cache_size, IOMMU_PAGE_SIZE, 1577c478bd9Sstevel@tonic-gate NULL, NULL, NULL, IOMMU_PAGE_SIZE, VM_SLEEP); 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate mutex_init(&iommu_p->dvma_debug_lock, NULL, MUTEX_DRIVER, NULL); 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate /* 1627c478bd9Sstevel@tonic-gate * On detach, the TSB Base Address Register gets set to zero, 1637c478bd9Sstevel@tonic-gate * so if its zero here, there is no need to preserve TTEs. 1647c478bd9Sstevel@tonic-gate */ 1657c478bd9Sstevel@tonic-gate if (pci_preserve_iommu_tsb && *iommu_p->iommu_tsb_base_addr_reg) 1667c478bd9Sstevel@tonic-gate iommu_preserve_tsb(iommu_p); 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate iommu_configure(iommu_p); 1697c478bd9Sstevel@tonic-gate } 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate void 1727c478bd9Sstevel@tonic-gate iommu_destroy(pci_t *pci_p) 1737c478bd9Sstevel@tonic-gate { 1747c478bd9Sstevel@tonic-gate #ifdef DEBUG 1757c478bd9Sstevel@tonic-gate dev_info_t *dip = pci_p->pci_dip; 1767c478bd9Sstevel@tonic-gate #endif 1777c478bd9Sstevel@tonic-gate iommu_t *iommu_p = pci_p->pci_iommu_p; 1787c478bd9Sstevel@tonic-gate volatile uint64_t ctl_val = *iommu_p->iommu_ctrl_reg; 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate DEBUG0(DBG_DETACH, dip, "iommu_destroy:\n"); 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * Disable the IOMMU by setting the TSB Base Address to zero 1847c478bd9Sstevel@tonic-gate * and the TSB Table size to the smallest possible. 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate ctl_val = ctl_val & ~(7 << COMMON_IOMMU_CTRL_TSB_SZ_SHIFT); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate *iommu_p->iommu_ctrl_reg = ctl_val; 1897c478bd9Sstevel@tonic-gate *iommu_p->iommu_tsb_base_addr_reg = 0; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* 1927c478bd9Sstevel@tonic-gate * Return the boot time allocated tsb. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate iommu_tsb_free(pci_p->pci_tsb_cookie); 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate /* 1977c478bd9Sstevel@tonic-gate * Teardown any implementation-specific structures set up in 1987c478bd9Sstevel@tonic-gate * pci_iommu_setup. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate pci_iommu_teardown(iommu_p); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate if (DVMA_DBG_ON(iommu_p)) 2037c478bd9Sstevel@tonic-gate pci_dvma_debug_fini(iommu_p); 2047c478bd9Sstevel@tonic-gate mutex_destroy(&iommu_p->dvma_debug_lock); 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate /* 2077c478bd9Sstevel@tonic-gate * Free the dvma resource map. 2087c478bd9Sstevel@tonic-gate */ 2097c478bd9Sstevel@tonic-gate vmem_destroy(iommu_p->iommu_dvma_map); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate kmem_free(iommu_p->iommu_dvma_cache_locks, 2127c478bd9Sstevel@tonic-gate pci_dvma_page_cache_entries); 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate /* 2157c478bd9Sstevel@tonic-gate * Free the iommu state structure. 2167c478bd9Sstevel@tonic-gate */ 2177c478bd9Sstevel@tonic-gate kmem_free(iommu_p, sizeof (iommu_t)); 2187c478bd9Sstevel@tonic-gate pci_p->pci_iommu_p = NULL; 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate /* 2227c478bd9Sstevel@tonic-gate * re-program iommu on the fly while preserving on-going dma 2237c478bd9Sstevel@tonic-gate * transactions on the PCI bus. 2247c478bd9Sstevel@tonic-gate */ 2257c478bd9Sstevel@tonic-gate void 2267c478bd9Sstevel@tonic-gate iommu_configure(iommu_t *iommu_p) 2277c478bd9Sstevel@tonic-gate { 2287c478bd9Sstevel@tonic-gate pci_t *pci_p = iommu_p->iommu_pci_p; 2297c478bd9Sstevel@tonic-gate uint64_t cfgpa = pci_get_cfg_pabase(pci_p); 2307c478bd9Sstevel@tonic-gate dev_info_t *dip = iommu_p->iommu_pci_p->pci_dip; 2317c478bd9Sstevel@tonic-gate dev_info_t *cdip = NULL; 2327c478bd9Sstevel@tonic-gate volatile uint64_t ctl_val = (uint64_t) 2337c478bd9Sstevel@tonic-gate ((iommu_p->iommu_tsb_size << COMMON_IOMMU_CTRL_TSB_SZ_SHIFT) | 2347c478bd9Sstevel@tonic-gate (0 /* 8k page */ << COMMON_IOMMU_CTRL_TBW_SZ_SHIFT) | 235*cd21e7c5SGarrett D'Amore COMMON_IOMMU_CTRL_ENABLE | COMMON_IOMMU_CTRL_DIAG_ENABLE | 2367c478bd9Sstevel@tonic-gate (pci_lock_tlb ? COMMON_IOMMU_CTRL_LCK_ENABLE : 0)); 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "iommu_configure: iommu_ctl=%08x.%08x\n", 2397c478bd9Sstevel@tonic-gate HI32(ctl_val), LO32(ctl_val)); 2407c478bd9Sstevel@tonic-gate if (!pci_preserve_iommu_tsb || !(*iommu_p->iommu_tsb_base_addr_reg)) { 2417c478bd9Sstevel@tonic-gate *iommu_p->iommu_ctrl_reg = COMMON_IOMMU_CTRL_DIAG_ENABLE; 2427c478bd9Sstevel@tonic-gate iommu_tlb_flushall(iommu_p); 2437c478bd9Sstevel@tonic-gate goto config; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate cdip = ddi_get_child(dip); 2467c478bd9Sstevel@tonic-gate for (; cdip; cdip = ddi_get_next_sibling(cdip)) { 2477c478bd9Sstevel@tonic-gate uint32_t *reg_p; 2487c478bd9Sstevel@tonic-gate int reg_len; 249a3282898Scth if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 2507c478bd9Sstevel@tonic-gate "reg", (caddr_t)®_p, ®_len) != DDI_PROP_SUCCESS) 2517c478bd9Sstevel@tonic-gate continue; 2527c478bd9Sstevel@tonic-gate cfgpa += (*reg_p) & (PCI_CONF_ADDR_MASK ^ PCI_REG_REG_M); 2537c478bd9Sstevel@tonic-gate kmem_free(reg_p, reg_len); 2547c478bd9Sstevel@tonic-gate break; 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate config: 2587c478bd9Sstevel@tonic-gate pci_iommu_config(iommu_p, ctl_val, cdip ? cfgpa : 0); 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate void 2627c478bd9Sstevel@tonic-gate iommu_map_pages(iommu_t *iommu_p, ddi_dma_impl_t *mp, 2637c478bd9Sstevel@tonic-gate dvma_addr_t dvma_pg, size_t npages, size_t pfn_index) 2647c478bd9Sstevel@tonic-gate { 2657c478bd9Sstevel@tonic-gate int i; 2667c478bd9Sstevel@tonic-gate dvma_addr_t pg_index = dvma_pg - iommu_p->dvma_base_pg; 2677c478bd9Sstevel@tonic-gate uint64_t *tte_addr = iommu_p->iommu_tsb_vaddr + pg_index; 2687c478bd9Sstevel@tonic-gate size_t pfn_last = pfn_index + npages; 2697c478bd9Sstevel@tonic-gate uint64_t tte = PCI_GET_MP_TTE(mp->dmai_tte); 2707c478bd9Sstevel@tonic-gate #ifdef DEBUG 2717c478bd9Sstevel@tonic-gate dev_info_t *dip = iommu_p->iommu_pci_p->pci_dip; 2727c478bd9Sstevel@tonic-gate #endif 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate ASSERT(pfn_last <= mp->dmai_ndvmapages); 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate DEBUG5(DBG_MAP_WIN, dip, 2777c478bd9Sstevel@tonic-gate "iommu_map_pages:%x+%x=%x npages=0x%x pfn_index=0x%x\n", 2787c478bd9Sstevel@tonic-gate (uint_t)iommu_p->dvma_base_pg, (uint_t)pg_index, dvma_pg, 2797c478bd9Sstevel@tonic-gate (uint_t)npages, (uint_t)pfn_index); 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate for (i = pfn_index; i < pfn_last; i++, pg_index++, tte_addr++) { 2827c478bd9Sstevel@tonic-gate iopfn_t pfn = PCI_GET_MP_PFN(mp, i); 2837c478bd9Sstevel@tonic-gate volatile uint64_t cur_tte = IOMMU_PTOB(pfn) | tte; 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate DEBUG3(DBG_MAP_WIN, dip, "iommu_map_pages: mp=%p pg[%x]=%x\n", 2867c478bd9Sstevel@tonic-gate mp, i, (uint_t)pfn); 2877c478bd9Sstevel@tonic-gate DEBUG3(DBG_MAP_WIN, dip, 2887c478bd9Sstevel@tonic-gate "iommu_map_pages: pg_index=%x tte=%08x.%08x\n", 2897c478bd9Sstevel@tonic-gate pg_index, HI32(cur_tte), LO32(cur_tte)); 2907c478bd9Sstevel@tonic-gate ASSERT(TTE_IS_INVALID(*tte_addr)); 2917c478bd9Sstevel@tonic-gate *tte_addr = cur_tte; 2927c478bd9Sstevel@tonic-gate #ifdef DEBUG 2937c478bd9Sstevel@tonic-gate if (pfn == 0 && pci_warn_pp0) 2947c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d <%p> doing DMA to pp0\n", 2957c478bd9Sstevel@tonic-gate ddi_driver_name(mp->dmai_rdip), 2967c478bd9Sstevel@tonic-gate ddi_get_instance(mp->dmai_rdip), mp); 2977c478bd9Sstevel@tonic-gate #endif 2987c478bd9Sstevel@tonic-gate } 2997c478bd9Sstevel@tonic-gate ASSERT(tte_addr == iommu_p->iommu_tsb_vaddr + pg_index); 3007c478bd9Sstevel@tonic-gate #ifdef DEBUG 3017c478bd9Sstevel@tonic-gate if (HAS_REDZONE(mp)) { 3027c478bd9Sstevel@tonic-gate DEBUG1(DBG_MAP_WIN, dip, "iommu_map_pages: redzone pg=%x\n", 3037c478bd9Sstevel@tonic-gate pg_index); 3047c478bd9Sstevel@tonic-gate ASSERT(TTE_IS_INVALID(iommu_p->iommu_tsb_vaddr[pg_index])); 3057c478bd9Sstevel@tonic-gate } 3067c478bd9Sstevel@tonic-gate #endif 3077c478bd9Sstevel@tonic-gate if (DVMA_DBG_ON(iommu_p)) 3087c478bd9Sstevel@tonic-gate pci_dvma_alloc_debug(iommu_p, (char *)mp->dmai_mapping, 3097c478bd9Sstevel@tonic-gate mp->dmai_size, mp); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate /* 3137c478bd9Sstevel@tonic-gate * iommu_map_window - map a dvma window into the iommu 3147c478bd9Sstevel@tonic-gate * 315*cd21e7c5SGarrett D'Amore * used by: pci_dma_win(), pci_dma_ctlops() - DDI_DMA_MOVWIN 3167c478bd9Sstevel@tonic-gate * 3177c478bd9Sstevel@tonic-gate * return value: none 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3207c478bd9Sstevel@tonic-gate void 3217c478bd9Sstevel@tonic-gate iommu_map_window(iommu_t *iommu_p, ddi_dma_impl_t *mp, window_t win_no) 3227c478bd9Sstevel@tonic-gate { 3237c478bd9Sstevel@tonic-gate uint32_t obj_pg0_off = mp->dmai_roffset; 3247c478bd9Sstevel@tonic-gate uint32_t win_pg0_off = win_no ? 0 : obj_pg0_off; 3257c478bd9Sstevel@tonic-gate size_t win_size = mp->dmai_winsize; 3267c478bd9Sstevel@tonic-gate size_t pfn_index = win_size * win_no; /* temp value */ 3277c478bd9Sstevel@tonic-gate size_t obj_off = win_no ? pfn_index - obj_pg0_off : 0; /* xferred sz */ 3287c478bd9Sstevel@tonic-gate dvma_addr_t dvma_pg = IOMMU_BTOP(mp->dmai_mapping); 3297c478bd9Sstevel@tonic-gate size_t res_size = mp->dmai_object.dmao_size - obj_off + win_pg0_off; 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate ASSERT(!(win_size & IOMMU_PAGE_OFFSET)); 3327c478bd9Sstevel@tonic-gate if (win_no >= mp->dmai_nwin) 3337c478bd9Sstevel@tonic-gate return; 3347c478bd9Sstevel@tonic-gate if (res_size < win_size) /* last window */ 3357c478bd9Sstevel@tonic-gate win_size = res_size; /* mp->dmai_winsize unchanged */ 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate mp->dmai_mapping = IOMMU_PTOB(dvma_pg) | win_pg0_off; 3387c478bd9Sstevel@tonic-gate mp->dmai_size = win_size - win_pg0_off; /* cur win xferrable size */ 3397c478bd9Sstevel@tonic-gate mp->dmai_offset = obj_off; /* win offset into object */ 3407c478bd9Sstevel@tonic-gate pfn_index = IOMMU_BTOP(pfn_index); /* index into pfnlist */ 3417c478bd9Sstevel@tonic-gate iommu_map_pages(iommu_p, mp, dvma_pg, IOMMU_BTOPR(win_size), pfn_index); 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate void 3457c478bd9Sstevel@tonic-gate iommu_unmap_pages(iommu_t *iommu_p, dvma_addr_t dvma_pg, uint_t npages) 3467c478bd9Sstevel@tonic-gate { 3477c478bd9Sstevel@tonic-gate dvma_addr_t pg_index = IOMMU_PAGE_INDEX(iommu_p, dvma_pg); 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate for (; npages; npages--, dvma_pg++, pg_index++) { 3507c478bd9Sstevel@tonic-gate DEBUG1(DBG_UNMAP_WIN|DBG_CONT, 0, " %x", dvma_pg); 3517c478bd9Sstevel@tonic-gate IOMMU_UNLOAD_TTE(iommu_p, pg_index); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate if (!tm_mtlb_gc) 3547c478bd9Sstevel@tonic-gate IOMMU_PAGE_FLUSH(iommu_p, dvma_pg); 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate } 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate void 3597c478bd9Sstevel@tonic-gate iommu_remap_pages(iommu_t *iommu_p, ddi_dma_impl_t *mp, dvma_addr_t dvma_pg, 3607c478bd9Sstevel@tonic-gate size_t npages, size_t pfn_index) 3617c478bd9Sstevel@tonic-gate { 3627c478bd9Sstevel@tonic-gate iommu_unmap_pages(iommu_p, dvma_pg, npages); 3637c478bd9Sstevel@tonic-gate iommu_map_pages(iommu_p, mp, dvma_pg, npages, pfn_index); 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate /* 3677c478bd9Sstevel@tonic-gate * iommu_unmap_window 3687c478bd9Sstevel@tonic-gate * 3697c478bd9Sstevel@tonic-gate * This routine is called to break down the iommu mappings to a dvma window. 3707c478bd9Sstevel@tonic-gate * Non partial mappings are viewed as single window mapping. 3717c478bd9Sstevel@tonic-gate * 3727c478bd9Sstevel@tonic-gate * used by: pci_dma_unbindhdl(), pci_dma_window(), 3737c478bd9Sstevel@tonic-gate * and pci_dma_ctlops() - DDI_DMA_FREE, DDI_DMA_MOVWIN, DDI_DMA_NEXTWIN 3747c478bd9Sstevel@tonic-gate * 3757c478bd9Sstevel@tonic-gate * return value: none 3767c478bd9Sstevel@tonic-gate */ 3777c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3787c478bd9Sstevel@tonic-gate void 3797c478bd9Sstevel@tonic-gate iommu_unmap_window(iommu_t *iommu_p, ddi_dma_impl_t *mp) 3807c478bd9Sstevel@tonic-gate { 3817c478bd9Sstevel@tonic-gate dvma_addr_t dvma_pg = IOMMU_BTOP(mp->dmai_mapping); 3827c478bd9Sstevel@tonic-gate dvma_addr_t pg_index = IOMMU_PAGE_INDEX(iommu_p, dvma_pg); 3837c478bd9Sstevel@tonic-gate uint_t npages = IOMMU_BTOP(mp->dmai_winsize); 3847c478bd9Sstevel@tonic-gate #ifdef DEBUG 3857c478bd9Sstevel@tonic-gate dev_info_t *dip = iommu_p->iommu_pci_p->pci_dip; 3867c478bd9Sstevel@tonic-gate #endif 3877c478bd9Sstevel@tonic-gate /* 3887c478bd9Sstevel@tonic-gate * Invalidate each page of the mapping in the tsb and flush 3897c478bd9Sstevel@tonic-gate * it from the tlb. 3907c478bd9Sstevel@tonic-gate */ 3917c478bd9Sstevel@tonic-gate DEBUG2(DBG_UNMAP_WIN, dip, "mp=%p %x pfns:", mp, npages); 3927c478bd9Sstevel@tonic-gate if (mp->dmai_flags & DMAI_FLAGS_CONTEXT) { 3937c478bd9Sstevel@tonic-gate dvma_context_t ctx = MP2CTX(mp); 3947c478bd9Sstevel@tonic-gate for (; npages; npages--, pg_index++) { 3957c478bd9Sstevel@tonic-gate DEBUG1(DBG_UNMAP_WIN|DBG_CONT, dip, " %x", pg_index); 3967c478bd9Sstevel@tonic-gate IOMMU_UNLOAD_TTE(iommu_p, pg_index); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate DEBUG1(DBG_UNMAP_WIN|DBG_CONT, dip, " (context %x)", ctx); 3997c478bd9Sstevel@tonic-gate *iommu_p->iommu_flush_ctx_reg = ctx; 4007c478bd9Sstevel@tonic-gate } else 4017c478bd9Sstevel@tonic-gate iommu_unmap_pages(iommu_p, dvma_pg, npages); 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate DEBUG0(DBG_UNMAP_WIN|DBG_CONT, dip, "\n"); 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate if (DVMA_DBG_ON(iommu_p)) 4067c478bd9Sstevel@tonic-gate pci_dvma_free_debug(iommu_p, (char *)mp->dmai_mapping, 4077c478bd9Sstevel@tonic-gate mp->dmai_size, mp); 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate int 4117c478bd9Sstevel@tonic-gate pci_alloc_tsb(pci_t *pci_p) 4127c478bd9Sstevel@tonic-gate { 4137c478bd9Sstevel@tonic-gate uint16_t tsbc; 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate if ((tsbc = iommu_tsb_alloc(pci_p->pci_id)) == IOMMU_TSB_COOKIE_NONE) { 4167c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: Unable to allocate IOMMU TSB.", 4177c478bd9Sstevel@tonic-gate ddi_driver_name(pci_p->pci_dip), 4187c478bd9Sstevel@tonic-gate ddi_get_instance(pci_p->pci_dip)); 4197c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate pci_p->pci_tsb_cookie = tsbc; 4227c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate void 4267c478bd9Sstevel@tonic-gate pci_free_tsb(pci_t *pci_p) 4277c478bd9Sstevel@tonic-gate { 4287c478bd9Sstevel@tonic-gate iommu_tsb_free(pci_p->pci_tsb_cookie); 4297c478bd9Sstevel@tonic-gate } 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate #if 0 4327c478bd9Sstevel@tonic-gate /* 4337c478bd9Sstevel@tonic-gate * The following data structure is used to map a tsb size 4347c478bd9Sstevel@tonic-gate * to a tsb size configuration parameter in the iommu 4357c478bd9Sstevel@tonic-gate * control register. 4367c478bd9Sstevel@tonic-gate * This is a hardware table. It is here for reference only. 4377c478bd9Sstevel@tonic-gate */ 4387c478bd9Sstevel@tonic-gate static int pci_iommu_tsb_sizes[] = { 4397c478bd9Sstevel@tonic-gate 0x2000, /* 0 - 8 mb */ 4407c478bd9Sstevel@tonic-gate 0x4000, /* 1 - 16 mb */ 4417c478bd9Sstevel@tonic-gate 0x8000, /* 2 - 32 mb */ 4427c478bd9Sstevel@tonic-gate 0x10000, /* 3 - 64 mb */ 4437c478bd9Sstevel@tonic-gate 0x20000, /* 4 - 128 mb */ 4447c478bd9Sstevel@tonic-gate 0x40000, /* 5 - 256 mb */ 4457c478bd9Sstevel@tonic-gate 0x80000, /* 6 - 512 mb */ 4467c478bd9Sstevel@tonic-gate 0x100000 /* 7 - 1 gb */ 4477c478bd9Sstevel@tonic-gate }; 4487c478bd9Sstevel@tonic-gate #endif 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate uint_t 4517c478bd9Sstevel@tonic-gate iommu_tsb_size_encode(uint_t tsb_bytes) 4527c478bd9Sstevel@tonic-gate { 4537c478bd9Sstevel@tonic-gate uint_t i; 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate for (i = 7; i && (tsb_bytes < (0x2000 << i)); i--) 4567c478bd9Sstevel@tonic-gate /* empty */; 4577c478bd9Sstevel@tonic-gate return (i); 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* 4617c478bd9Sstevel@tonic-gate * invalidate IOMMU TLB entries through diagnostic registers. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate static void 4647c478bd9Sstevel@tonic-gate iommu_tlb_flushall(iommu_t *iommu_p) 4657c478bd9Sstevel@tonic-gate { 4667c478bd9Sstevel@tonic-gate int i; 4677c478bd9Sstevel@tonic-gate uint64_t base = (uint64_t)(iommu_p->iommu_ctrl_reg) - 4687c478bd9Sstevel@tonic-gate COMMON_IOMMU_CTRL_REG_OFFSET; 4697c478bd9Sstevel@tonic-gate volatile uint64_t *tlb_tag = (volatile uint64_t *) 4707c478bd9Sstevel@tonic-gate (base + COMMON_IOMMU_TLB_TAG_DIAG_ACC_OFFSET); 4717c478bd9Sstevel@tonic-gate volatile uint64_t *tlb_data = (volatile uint64_t *) 4727c478bd9Sstevel@tonic-gate (base + COMMON_IOMMU_TLB_DATA_DIAG_ACC_OFFSET); 4737c478bd9Sstevel@tonic-gate for (i = 0; i < IOMMU_TLB_ENTRIES; i++) 4747c478bd9Sstevel@tonic-gate tlb_tag[i] = tlb_data[i] = 0ull; 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate static void 4787c478bd9Sstevel@tonic-gate iommu_preserve_tsb(iommu_t *iommu_p) 4797c478bd9Sstevel@tonic-gate { 4807c478bd9Sstevel@tonic-gate #ifdef DEBUG 4817c478bd9Sstevel@tonic-gate dev_info_t *dip = iommu_p->iommu_pci_p->pci_dip; 4827c478bd9Sstevel@tonic-gate #endif 4837c478bd9Sstevel@tonic-gate uint_t i, obp_tsb_entries, obp_tsb_size, base_pg_index; 4847c478bd9Sstevel@tonic-gate uint64_t ctl = *iommu_p->iommu_ctrl_reg; 4857c478bd9Sstevel@tonic-gate uint64_t obp_tsb_pa = *iommu_p->iommu_tsb_base_addr_reg; 4867c478bd9Sstevel@tonic-gate uint64_t *base_tte_addr; 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate DEBUG3(DBG_ATTACH, dip, 4897c478bd9Sstevel@tonic-gate "iommu_tsb_base_addr_reg=0x%08x (0x%08x.0x%08x)\n", 4907c478bd9Sstevel@tonic-gate iommu_p->iommu_tsb_base_addr_reg, 4917c478bd9Sstevel@tonic-gate (uint32_t)(*iommu_p->iommu_tsb_base_addr_reg >> 32), 4927c478bd9Sstevel@tonic-gate (uint32_t)(*iommu_p->iommu_tsb_base_addr_reg & 0xffffffff)); 4937c478bd9Sstevel@tonic-gate 4947c478bd9Sstevel@tonic-gate obp_tsb_size = IOMMU_CTL_TO_TSBSIZE(ctl); 4957c478bd9Sstevel@tonic-gate obp_tsb_entries = IOMMU_TSBSIZE_TO_TSBENTRIES(obp_tsb_size); 4967c478bd9Sstevel@tonic-gate base_pg_index = iommu_p->dvma_end_pg - obp_tsb_entries + 1; 4977c478bd9Sstevel@tonic-gate base_tte_addr = iommu_p->iommu_tsb_vaddr + 4987c478bd9Sstevel@tonic-gate (iommu_p->iommu_tsb_entries - obp_tsb_entries); 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate /* 5017c478bd9Sstevel@tonic-gate * old darwin prom does not set tsb size correctly, bail out. 5027c478bd9Sstevel@tonic-gate */ 5037c478bd9Sstevel@tonic-gate if ((obp_tsb_size == IOMMU_DARWIN_BOGUS_TSBSIZE) && 5047c478bd9Sstevel@tonic-gate (CHIP_TYPE(iommu_p->iommu_pci_p) == PCI_CHIP_SABRE)) 5057c478bd9Sstevel@tonic-gate return; 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate DEBUG3(DBG_ATTACH, dip, "iommu_preserve_tsb: kernel info\n" 5087c478bd9Sstevel@tonic-gate "iommu_tsb_vaddr=%08x copy to base_tte_addr=%08x " 5097c478bd9Sstevel@tonic-gate "base_pg_index=%x\n", iommu_p->iommu_tsb_vaddr, 5107c478bd9Sstevel@tonic-gate base_tte_addr, base_pg_index); 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate DEBUG3(DBG_ATTACH | DBG_CONT, dip, "iommu_preserve_tsb: obp info " 5137c478bd9Sstevel@tonic-gate "obp_tsb_entries=0x%x obp_tsb_pa=%08x.%08x\n", obp_tsb_entries, 5147c478bd9Sstevel@tonic-gate (uint32_t)(obp_tsb_pa >> 32), (uint32_t)obp_tsb_pa); 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate for (i = 0; i < obp_tsb_entries; i++) { 5177c478bd9Sstevel@tonic-gate uint64_t tte = lddphys(obp_tsb_pa + i * 8); 5187c478bd9Sstevel@tonic-gate caddr_t va; 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate if (TTE_IS_INVALID(tte)) { 5217c478bd9Sstevel@tonic-gate DEBUG0(DBG_ATTACH | DBG_CONT, dip, "."); 5227c478bd9Sstevel@tonic-gate continue; 5237c478bd9Sstevel@tonic-gate } 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate base_tte_addr[i] = tte; 5267c478bd9Sstevel@tonic-gate DEBUG3(DBG_ATTACH | DBG_CONT, dip, 5277c478bd9Sstevel@tonic-gate "\npreserve_tsb: (%x)=%08x.%08x\n", base_tte_addr + i, 5287c478bd9Sstevel@tonic-gate (uint_t)(tte >> 32), (uint_t)(tte & 0xffffffff)); 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate /* 5317c478bd9Sstevel@tonic-gate * permanantly reserve this page from dvma address space 5327c478bd9Sstevel@tonic-gate * resource map 5337c478bd9Sstevel@tonic-gate */ 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate va = (caddr_t)(IOMMU_PTOB(base_pg_index + i)); 5367c478bd9Sstevel@tonic-gate (void) vmem_xalloc(iommu_p->iommu_dvma_map, IOMMU_PAGE_SIZE, 5377c478bd9Sstevel@tonic-gate IOMMU_PAGE_SIZE, 0, 0, va, va + IOMMU_PAGE_SIZE, 5387c478bd9Sstevel@tonic-gate VM_NOSLEEP | VM_BESTFIT | VM_PANIC); 5397c478bd9Sstevel@tonic-gate } 5407c478bd9Sstevel@tonic-gate } 541