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