1c0da6274SZhi-Jun Robin Fu /* 2c0da6274SZhi-Jun Robin Fu * CDDL HEADER START 3c0da6274SZhi-Jun Robin Fu * 4c0da6274SZhi-Jun Robin Fu * The contents of this file are subject to the terms of the 5c0da6274SZhi-Jun Robin Fu * Common Development and Distribution License (the "License"). 6c0da6274SZhi-Jun Robin Fu * You may not use this file except in compliance with the License. 7c0da6274SZhi-Jun Robin Fu * 8c0da6274SZhi-Jun Robin Fu * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9c0da6274SZhi-Jun Robin Fu * or http://www.opensolaris.org/os/licensing. 10c0da6274SZhi-Jun Robin Fu * See the License for the specific language governing permissions 11c0da6274SZhi-Jun Robin Fu * and limitations under the License. 12c0da6274SZhi-Jun Robin Fu * 13c0da6274SZhi-Jun Robin Fu * When distributing Covered Code, include this CDDL HEADER in each 14c0da6274SZhi-Jun Robin Fu * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15c0da6274SZhi-Jun Robin Fu * If applicable, add the following below this CDDL HEADER, with the 16c0da6274SZhi-Jun Robin Fu * fields enclosed by brackets "[]" replaced with your own identifying 17c0da6274SZhi-Jun Robin Fu * information: Portions Copyright [yyyy] [name of copyright owner] 18c0da6274SZhi-Jun Robin Fu * 19c0da6274SZhi-Jun Robin Fu * CDDL HEADER END 20c0da6274SZhi-Jun Robin Fu */ 21c0da6274SZhi-Jun Robin Fu 22c0da6274SZhi-Jun Robin Fu /* 238d7fafffSZhi-Jun Robin Fu * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24c0da6274SZhi-Jun Robin Fu */ 25c0da6274SZhi-Jun Robin Fu 26c0da6274SZhi-Jun Robin Fu #include <sys/systm.h> 27c0da6274SZhi-Jun Robin Fu #include <sys/pci_cfgacc.h> 28c0da6274SZhi-Jun Robin Fu #include <sys/pci_cfgspace.h> 29c0da6274SZhi-Jun Robin Fu #include <sys/pci_cfgspace_impl.h> 30c0da6274SZhi-Jun Robin Fu #include <sys/sunddi.h> 31c0da6274SZhi-Jun Robin Fu #include <sys/sysmacros.h> 32c0da6274SZhi-Jun Robin Fu #include <sys/x86_archext.h> 33c0da6274SZhi-Jun Robin Fu #include <sys/pci.h> 348d7fafffSZhi-Jun Robin Fu #include <sys/cmn_err.h> 35c0da6274SZhi-Jun Robin Fu #include <vm/hat_i86.h> 36c0da6274SZhi-Jun Robin Fu #include <vm/seg_kmem.h> 37c0da6274SZhi-Jun Robin Fu #include <vm/kboot_mmu.h> 38c0da6274SZhi-Jun Robin Fu 39c0da6274SZhi-Jun Robin Fu #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) 40c0da6274SZhi-Jun Robin Fu #define PCI_BDF_BUS(bdf) ((((uint16_t)bdf) & 0xff00) >> 8) 41c0da6274SZhi-Jun Robin Fu #define PCI_BDF_DEV(bdf) ((((uint16_t)bdf) & 0xf8) >> 3) 42c0da6274SZhi-Jun Robin Fu #define PCI_BDF_FUNC(bdf) (((uint16_t)bdf) & 0x7) 43c0da6274SZhi-Jun Robin Fu 44c0da6274SZhi-Jun Robin Fu /* patchable variables */ 45c0da6274SZhi-Jun Robin Fu volatile boolean_t pci_cfgacc_force_io = B_FALSE; 46c0da6274SZhi-Jun Robin Fu 47c0da6274SZhi-Jun Robin Fu extern uintptr_t alloc_vaddr(size_t, paddr_t); 48c0da6274SZhi-Jun Robin Fu 49c0da6274SZhi-Jun Robin Fu void pci_cfgacc_acc(pci_cfgacc_req_t *); 50c0da6274SZhi-Jun Robin Fu 51c0da6274SZhi-Jun Robin Fu boolean_t pci_cfgacc_find_workaround(uint16_t); 52c0da6274SZhi-Jun Robin Fu /* 53c0da6274SZhi-Jun Robin Fu * IS_P2ALIGNED() is used to make sure offset is 'size'-aligned, so 54c0da6274SZhi-Jun Robin Fu * it's guaranteed that the access will not cross 4k page boundary. 55c0da6274SZhi-Jun Robin Fu * Thus only 1 page is allocated for all config space access, and the 56c0da6274SZhi-Jun Robin Fu * virtual address of that page is cached in pci_cfgacc_virt_base. 57c0da6274SZhi-Jun Robin Fu */ 58c0da6274SZhi-Jun Robin Fu static caddr_t pci_cfgacc_virt_base = NULL; 59c0da6274SZhi-Jun Robin Fu 60c0da6274SZhi-Jun Robin Fu static caddr_t 61c0da6274SZhi-Jun Robin Fu pci_cfgacc_map(paddr_t phys_addr) 62c0da6274SZhi-Jun Robin Fu { 63c0da6274SZhi-Jun Robin Fu #ifdef __xpv 64c0da6274SZhi-Jun Robin Fu phys_addr = pfn_to_pa(xen_assign_pfn(mmu_btop(phys_addr))) | 65c0da6274SZhi-Jun Robin Fu (phys_addr & MMU_PAGEOFFSET); 66c0da6274SZhi-Jun Robin Fu #endif 67c0da6274SZhi-Jun Robin Fu if (khat_running) { 68c0da6274SZhi-Jun Robin Fu pfn_t pfn = mmu_btop(phys_addr); 69c0da6274SZhi-Jun Robin Fu /* 70c0da6274SZhi-Jun Robin Fu * pci_cfgacc_virt_base may hold address left from early 71c0da6274SZhi-Jun Robin Fu * boot, which points to low mem. Realloc virtual address 72c0da6274SZhi-Jun Robin Fu * in kernel space since it's already late in boot now. 73c0da6274SZhi-Jun Robin Fu * Note: no need to unmap first, clear_boot_mappings() will 74c0da6274SZhi-Jun Robin Fu * do that for us. 75c0da6274SZhi-Jun Robin Fu */ 768d7fafffSZhi-Jun Robin Fu if (pci_cfgacc_virt_base < (caddr_t)kernelbase) 778d7fafffSZhi-Jun Robin Fu pci_cfgacc_virt_base = vmem_alloc(heap_arena, 788d7fafffSZhi-Jun Robin Fu MMU_PAGESIZE, VM_SLEEP); 798d7fafffSZhi-Jun Robin Fu 80c0da6274SZhi-Jun Robin Fu hat_devload(kas.a_hat, pci_cfgacc_virt_base, 81c0da6274SZhi-Jun Robin Fu MMU_PAGESIZE, pfn, PROT_READ | PROT_WRITE | 82c0da6274SZhi-Jun Robin Fu HAT_STRICTORDER, HAT_LOAD_LOCK); 83c0da6274SZhi-Jun Robin Fu } else { 84c0da6274SZhi-Jun Robin Fu paddr_t pa_base = P2ALIGN(phys_addr, MMU_PAGESIZE); 85c0da6274SZhi-Jun Robin Fu 868d7fafffSZhi-Jun Robin Fu if (pci_cfgacc_virt_base == NULL) 878d7fafffSZhi-Jun Robin Fu pci_cfgacc_virt_base = 888d7fafffSZhi-Jun Robin Fu (caddr_t)alloc_vaddr(MMU_PAGESIZE, MMU_PAGESIZE); 898d7fafffSZhi-Jun Robin Fu 90c0da6274SZhi-Jun Robin Fu kbm_map((uintptr_t)pci_cfgacc_virt_base, pa_base, 0, 0); 91c0da6274SZhi-Jun Robin Fu } 92c0da6274SZhi-Jun Robin Fu 93c0da6274SZhi-Jun Robin Fu return (pci_cfgacc_virt_base + (phys_addr & MMU_PAGEOFFSET)); 94c0da6274SZhi-Jun Robin Fu } 95c0da6274SZhi-Jun Robin Fu 96c0da6274SZhi-Jun Robin Fu static void 97c0da6274SZhi-Jun Robin Fu pci_cfgacc_unmap() 98c0da6274SZhi-Jun Robin Fu { 99c0da6274SZhi-Jun Robin Fu if (khat_running) 100c0da6274SZhi-Jun Robin Fu hat_unload(kas.a_hat, pci_cfgacc_virt_base, MMU_PAGESIZE, 101c0da6274SZhi-Jun Robin Fu HAT_UNLOAD_UNLOCK); 102c0da6274SZhi-Jun Robin Fu } 103c0da6274SZhi-Jun Robin Fu 104c0da6274SZhi-Jun Robin Fu static void 105c0da6274SZhi-Jun Robin Fu pci_cfgacc_io(pci_cfgacc_req_t *req) 106c0da6274SZhi-Jun Robin Fu { 1078d7fafffSZhi-Jun Robin Fu uint8_t bus, dev, func; 1088d7fafffSZhi-Jun Robin Fu uint16_t ioacc_offset; /* 4K config access with IO ECS */ 109c0da6274SZhi-Jun Robin Fu 110c0da6274SZhi-Jun Robin Fu bus = PCI_BDF_BUS(req->bdf); 111c0da6274SZhi-Jun Robin Fu dev = PCI_BDF_DEV(req->bdf); 112c0da6274SZhi-Jun Robin Fu func = PCI_BDF_FUNC(req->bdf); 113c0da6274SZhi-Jun Robin Fu ioacc_offset = req->offset; 1148d7fafffSZhi-Jun Robin Fu 115c0da6274SZhi-Jun Robin Fu switch (req->size) { 116c0da6274SZhi-Jun Robin Fu case 1: 117c0da6274SZhi-Jun Robin Fu if (req->write) 118c0da6274SZhi-Jun Robin Fu (*pci_putb_func)(bus, dev, func, 119c0da6274SZhi-Jun Robin Fu ioacc_offset, VAL8(req)); 120c0da6274SZhi-Jun Robin Fu else 121c0da6274SZhi-Jun Robin Fu VAL8(req) = (*pci_getb_func)(bus, dev, func, 122c0da6274SZhi-Jun Robin Fu ioacc_offset); 123c0da6274SZhi-Jun Robin Fu break; 124c0da6274SZhi-Jun Robin Fu case 2: 125c0da6274SZhi-Jun Robin Fu if (req->write) 126c0da6274SZhi-Jun Robin Fu (*pci_putw_func)(bus, dev, func, 127c0da6274SZhi-Jun Robin Fu ioacc_offset, VAL16(req)); 128c0da6274SZhi-Jun Robin Fu else 129c0da6274SZhi-Jun Robin Fu VAL16(req) = (*pci_getw_func)(bus, dev, func, 130c0da6274SZhi-Jun Robin Fu ioacc_offset); 131c0da6274SZhi-Jun Robin Fu break; 132c0da6274SZhi-Jun Robin Fu case 4: 133c0da6274SZhi-Jun Robin Fu if (req->write) 134c0da6274SZhi-Jun Robin Fu (*pci_putl_func)(bus, dev, func, 135c0da6274SZhi-Jun Robin Fu ioacc_offset, VAL32(req)); 136c0da6274SZhi-Jun Robin Fu else 137c0da6274SZhi-Jun Robin Fu VAL32(req) = (*pci_getl_func)(bus, dev, func, 138c0da6274SZhi-Jun Robin Fu ioacc_offset); 139c0da6274SZhi-Jun Robin Fu break; 1408d7fafffSZhi-Jun Robin Fu case 8: 1418d7fafffSZhi-Jun Robin Fu if (req->write) { 1428d7fafffSZhi-Jun Robin Fu (*pci_putl_func)(bus, dev, func, 1438d7fafffSZhi-Jun Robin Fu ioacc_offset, VAL64(req) & 0xffffffff); 1448d7fafffSZhi-Jun Robin Fu (*pci_putl_func)(bus, dev, func, 1458d7fafffSZhi-Jun Robin Fu ioacc_offset + 4, VAL64(req) >> 32); 1468d7fafffSZhi-Jun Robin Fu } else { 1478d7fafffSZhi-Jun Robin Fu VAL64(req) = (*pci_getl_func)(bus, dev, func, 1488d7fafffSZhi-Jun Robin Fu ioacc_offset); 1498d7fafffSZhi-Jun Robin Fu VAL64(req) |= (uint64_t)(*pci_getl_func)(bus, dev, func, 1508d7fafffSZhi-Jun Robin Fu ioacc_offset + 4) << 32; 1518d7fafffSZhi-Jun Robin Fu } 1528d7fafffSZhi-Jun Robin Fu break; 153c0da6274SZhi-Jun Robin Fu } 154c0da6274SZhi-Jun Robin Fu } 155c0da6274SZhi-Jun Robin Fu 1568d7fafffSZhi-Jun Robin Fu static void 157c0da6274SZhi-Jun Robin Fu pci_cfgacc_mmio(pci_cfgacc_req_t *req) 158c0da6274SZhi-Jun Robin Fu { 159c0da6274SZhi-Jun Robin Fu caddr_t vaddr; 160c0da6274SZhi-Jun Robin Fu paddr_t paddr; 161c0da6274SZhi-Jun Robin Fu 162c0da6274SZhi-Jun Robin Fu paddr = (paddr_t)req->bdf << 12; 163c0da6274SZhi-Jun Robin Fu paddr += mcfg_mem_base + req->offset; 164c0da6274SZhi-Jun Robin Fu 16513763277SGuoli Shu mutex_enter(&pcicfg_mmio_mutex); 1668d7fafffSZhi-Jun Robin Fu vaddr = pci_cfgacc_map(paddr); 167c0da6274SZhi-Jun Robin Fu 168c0da6274SZhi-Jun Robin Fu switch (req->size) { 169c0da6274SZhi-Jun Robin Fu case 1: 170c0da6274SZhi-Jun Robin Fu if (req->write) 171c0da6274SZhi-Jun Robin Fu *((uint8_t *)vaddr) = VAL8(req); 172c0da6274SZhi-Jun Robin Fu else 173c0da6274SZhi-Jun Robin Fu VAL8(req) = *((uint8_t *)vaddr); 174c0da6274SZhi-Jun Robin Fu break; 175c0da6274SZhi-Jun Robin Fu case 2: 176c0da6274SZhi-Jun Robin Fu if (req->write) 177c0da6274SZhi-Jun Robin Fu *((uint16_t *)vaddr) = VAL16(req); 178c0da6274SZhi-Jun Robin Fu else 179c0da6274SZhi-Jun Robin Fu VAL16(req) = *((uint16_t *)vaddr); 180c0da6274SZhi-Jun Robin Fu break; 181c0da6274SZhi-Jun Robin Fu case 4: 182c0da6274SZhi-Jun Robin Fu if (req->write) 183c0da6274SZhi-Jun Robin Fu *((uint32_t *)vaddr) = VAL32(req); 184c0da6274SZhi-Jun Robin Fu else 185c0da6274SZhi-Jun Robin Fu VAL32(req) = *((uint32_t *)vaddr); 186c0da6274SZhi-Jun Robin Fu break; 187c0da6274SZhi-Jun Robin Fu case 8: 188c0da6274SZhi-Jun Robin Fu if (req->write) 189c0da6274SZhi-Jun Robin Fu *((uint64_t *)vaddr) = VAL64(req); 190c0da6274SZhi-Jun Robin Fu else 191c0da6274SZhi-Jun Robin Fu VAL64(req) = *((uint64_t *)vaddr); 192c0da6274SZhi-Jun Robin Fu break; 193c0da6274SZhi-Jun Robin Fu } 194c0da6274SZhi-Jun Robin Fu pci_cfgacc_unmap(); 19513763277SGuoli Shu mutex_exit(&pcicfg_mmio_mutex); 196c0da6274SZhi-Jun Robin Fu } 197c0da6274SZhi-Jun Robin Fu 198c0da6274SZhi-Jun Robin Fu static boolean_t 199*586fe2c2SZhi-Jun Robin Fu pci_cfgacc_valid(pci_cfgacc_req_t *req, uint16_t cfgspc_size) 200c0da6274SZhi-Jun Robin Fu { 2018d7fafffSZhi-Jun Robin Fu int sz = req->size; 2028d7fafffSZhi-Jun Robin Fu 2038d7fafffSZhi-Jun Robin Fu if (IS_P2ALIGNED(req->offset, sz) && 204*586fe2c2SZhi-Jun Robin Fu (req->offset + sz - 1 < cfgspc_size) && 2058d7fafffSZhi-Jun Robin Fu ((sz & 0xf) && ISP2(sz))) 2068d7fafffSZhi-Jun Robin Fu return (B_TRUE); 2078d7fafffSZhi-Jun Robin Fu 2088d7fafffSZhi-Jun Robin Fu cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d", 2098d7fafffSZhi-Jun Robin Fu req->offset, sz); 2108d7fafffSZhi-Jun Robin Fu return (B_FALSE); 2118d7fafffSZhi-Jun Robin Fu } 2128d7fafffSZhi-Jun Robin Fu 2138d7fafffSZhi-Jun Robin Fu void 2148d7fafffSZhi-Jun Robin Fu pci_cfgacc_check_io(pci_cfgacc_req_t *req) 2158d7fafffSZhi-Jun Robin Fu { 2168d7fafffSZhi-Jun Robin Fu uint8_t bus; 2178d7fafffSZhi-Jun Robin Fu 2188d7fafffSZhi-Jun Robin Fu bus = PCI_BDF_BUS(req->bdf); 2198d7fafffSZhi-Jun Robin Fu 2208d7fafffSZhi-Jun Robin Fu if (pci_cfgacc_force_io || (mcfg_mem_base == NULL) || 2218d7fafffSZhi-Jun Robin Fu (bus < mcfg_bus_start) || (bus > mcfg_bus_end) || 2228d7fafffSZhi-Jun Robin Fu pci_cfgacc_find_workaround(req->bdf)) 2238d7fafffSZhi-Jun Robin Fu req->ioacc = B_TRUE; 224c0da6274SZhi-Jun Robin Fu } 225c0da6274SZhi-Jun Robin Fu 226c0da6274SZhi-Jun Robin Fu void 227c0da6274SZhi-Jun Robin Fu pci_cfgacc_acc(pci_cfgacc_req_t *req) 228c0da6274SZhi-Jun Robin Fu { 2298d7fafffSZhi-Jun Robin Fu extern uint_t pci_iocfg_max_offset; 230c0da6274SZhi-Jun Robin Fu 231c0da6274SZhi-Jun Robin Fu if (!req->write) 232c0da6274SZhi-Jun Robin Fu VAL64(req) = (uint64_t)-1; 233c0da6274SZhi-Jun Robin Fu 2348d7fafffSZhi-Jun Robin Fu pci_cfgacc_check_io(req); 235c0da6274SZhi-Jun Robin Fu 2368d7fafffSZhi-Jun Robin Fu if (req->ioacc) { 237*586fe2c2SZhi-Jun Robin Fu if (pci_cfgacc_valid(req, pci_iocfg_max_offset + 1)) 238c0da6274SZhi-Jun Robin Fu pci_cfgacc_io(req); 2398d7fafffSZhi-Jun Robin Fu } else { 2408d7fafffSZhi-Jun Robin Fu if (pci_cfgacc_valid(req, PCIE_CFG_SPACE_SIZE)) 2418d7fafffSZhi-Jun Robin Fu pci_cfgacc_mmio(req); 2428d7fafffSZhi-Jun Robin Fu } 243c0da6274SZhi-Jun Robin Fu } 244c0da6274SZhi-Jun Robin Fu 245c0da6274SZhi-Jun Robin Fu typedef struct cfgacc_bus_range { 246c0da6274SZhi-Jun Robin Fu struct cfgacc_bus_range *next; 247c0da6274SZhi-Jun Robin Fu uint16_t bdf; 248c0da6274SZhi-Jun Robin Fu uchar_t secbus; 249c0da6274SZhi-Jun Robin Fu uchar_t subbus; 250c0da6274SZhi-Jun Robin Fu } cfgacc_bus_range_t; 251c0da6274SZhi-Jun Robin Fu 252c0da6274SZhi-Jun Robin Fu cfgacc_bus_range_t *pci_cfgacc_bus_head = NULL; 253c0da6274SZhi-Jun Robin Fu 254c0da6274SZhi-Jun Robin Fu #define BUS_INSERT(prev, el) \ 255c0da6274SZhi-Jun Robin Fu el->next = *prev; \ 256c0da6274SZhi-Jun Robin Fu *prev = el; 257c0da6274SZhi-Jun Robin Fu 258c0da6274SZhi-Jun Robin Fu #define BUS_REMOVE(prev, el) \ 259c0da6274SZhi-Jun Robin Fu *prev = el->next; 260c0da6274SZhi-Jun Robin Fu 261c0da6274SZhi-Jun Robin Fu /* 262c0da6274SZhi-Jun Robin Fu * This function is only supposed to be called in device tree setup time, 263c0da6274SZhi-Jun Robin Fu * thus no lock is needed. 264c0da6274SZhi-Jun Robin Fu */ 265c0da6274SZhi-Jun Robin Fu void 266c0da6274SZhi-Jun Robin Fu pci_cfgacc_add_workaround(uint16_t bdf, uchar_t secbus, uchar_t subbus) 267c0da6274SZhi-Jun Robin Fu { 268c0da6274SZhi-Jun Robin Fu cfgacc_bus_range_t *entry; 269c0da6274SZhi-Jun Robin Fu 270c0da6274SZhi-Jun Robin Fu entry = kmem_zalloc(sizeof (cfgacc_bus_range_t), KM_SLEEP); 271c0da6274SZhi-Jun Robin Fu entry->bdf = bdf; 272c0da6274SZhi-Jun Robin Fu entry->secbus = secbus; 273c0da6274SZhi-Jun Robin Fu entry->subbus = subbus; 274c0da6274SZhi-Jun Robin Fu BUS_INSERT(&pci_cfgacc_bus_head, entry); 275c0da6274SZhi-Jun Robin Fu } 276c0da6274SZhi-Jun Robin Fu 277c0da6274SZhi-Jun Robin Fu boolean_t 278c0da6274SZhi-Jun Robin Fu pci_cfgacc_find_workaround(uint16_t bdf) 279c0da6274SZhi-Jun Robin Fu { 280c0da6274SZhi-Jun Robin Fu cfgacc_bus_range_t *entry; 281c0da6274SZhi-Jun Robin Fu uchar_t bus; 282c0da6274SZhi-Jun Robin Fu 283c0da6274SZhi-Jun Robin Fu for (entry = pci_cfgacc_bus_head; entry != NULL; 284c0da6274SZhi-Jun Robin Fu entry = entry->next) { 285c0da6274SZhi-Jun Robin Fu if (bdf == entry->bdf) { 286c0da6274SZhi-Jun Robin Fu /* found a device which is known to be broken */ 287c0da6274SZhi-Jun Robin Fu return (B_TRUE); 288c0da6274SZhi-Jun Robin Fu } 289c0da6274SZhi-Jun Robin Fu 290c0da6274SZhi-Jun Robin Fu bus = PCI_BDF_BUS(bdf); 291c0da6274SZhi-Jun Robin Fu if ((bus != 0) && (bus >= entry->secbus) && 292c0da6274SZhi-Jun Robin Fu (bus <= entry->subbus)) { 293c0da6274SZhi-Jun Robin Fu /* 294c0da6274SZhi-Jun Robin Fu * found a device whose parent/grandparent is 295c0da6274SZhi-Jun Robin Fu * known to be broken. 296c0da6274SZhi-Jun Robin Fu */ 297c0da6274SZhi-Jun Robin Fu return (B_TRUE); 298c0da6274SZhi-Jun Robin Fu } 299c0da6274SZhi-Jun Robin Fu } 300c0da6274SZhi-Jun Robin Fu 301c0da6274SZhi-Jun Robin Fu return (B_FALSE); 302c0da6274SZhi-Jun Robin Fu } 303