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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/systm.h> 28 #include <sys/pci_cfgacc.h> 29 #include <sys/pci_cfgspace.h> 30 #include <sys/pci_cfgspace_impl.h> 31 #include <sys/sunddi.h> 32 #include <sys/sysmacros.h> 33 #include <sys/x86_archext.h> 34 #include <sys/pci.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 if ((pci_cfgacc_virt_base = vmem_alloc(heap_arena, 78 MMU_PAGESIZE, VM_NOSLEEP)) == NULL) 79 return (NULL); 80 } 81 hat_devload(kas.a_hat, pci_cfgacc_virt_base, 82 MMU_PAGESIZE, pfn, PROT_READ | PROT_WRITE | 83 HAT_STRICTORDER, HAT_LOAD_LOCK); 84 } else { 85 paddr_t pa_base = P2ALIGN(phys_addr, MMU_PAGESIZE); 86 87 if (pci_cfgacc_virt_base == NULL) { 88 if ((pci_cfgacc_virt_base = (caddr_t) 89 alloc_vaddr(MMU_PAGESIZE, MMU_PAGESIZE)) == NULL) 90 return (NULL); 91 } 92 kbm_map((uintptr_t)pci_cfgacc_virt_base, pa_base, 0, 0); 93 } 94 95 return (pci_cfgacc_virt_base + (phys_addr & MMU_PAGEOFFSET)); 96 } 97 98 static void 99 pci_cfgacc_unmap() 100 { 101 if (khat_running) 102 hat_unload(kas.a_hat, pci_cfgacc_virt_base, MMU_PAGESIZE, 103 HAT_UNLOAD_UNLOCK); 104 } 105 106 static void 107 pci_cfgacc_io(pci_cfgacc_req_t *req) 108 { 109 uint8_t bus, dev, func, ioacc_offset; 110 111 if (req->offset > 0xff) { 112 if (!req->write) 113 VAL64(req) = (uint64_t)-1; 114 return; 115 } 116 117 bus = PCI_BDF_BUS(req->bdf); 118 dev = PCI_BDF_DEV(req->bdf); 119 func = PCI_BDF_FUNC(req->bdf); 120 ioacc_offset = req->offset; 121 switch (req->size) { 122 case 1: 123 if (req->write) 124 (*pci_putb_func)(bus, dev, func, 125 ioacc_offset, VAL8(req)); 126 else 127 VAL8(req) = (*pci_getb_func)(bus, dev, func, 128 ioacc_offset); 129 break; 130 case 2: 131 if (req->write) 132 (*pci_putw_func)(bus, dev, func, 133 ioacc_offset, VAL16(req)); 134 else 135 VAL16(req) = (*pci_getw_func)(bus, dev, func, 136 ioacc_offset); 137 break; 138 case 4: 139 if (req->write) 140 (*pci_putl_func)(bus, dev, func, 141 ioacc_offset, VAL32(req)); 142 else 143 VAL32(req) = (*pci_getl_func)(bus, dev, func, 144 ioacc_offset); 145 break; 146 default: 147 return; 148 } 149 } 150 151 static int 152 pci_cfgacc_mmio(pci_cfgacc_req_t *req) 153 { 154 caddr_t vaddr; 155 paddr_t paddr; 156 int rval = DDI_SUCCESS; 157 158 paddr = (paddr_t)req->bdf << 12; 159 paddr += mcfg_mem_base + req->offset; 160 161 mutex_enter(&pcicfg_mmio_mutex); 162 if ((vaddr = pci_cfgacc_map(paddr)) == NULL) { 163 mutex_exit(&pcicfg_mmio_mutex); 164 return (DDI_FAILURE); 165 } 166 167 switch (req->size) { 168 case 1: 169 if (req->write) 170 *((uint8_t *)vaddr) = VAL8(req); 171 else 172 VAL8(req) = *((uint8_t *)vaddr); 173 break; 174 case 2: 175 if (req->write) 176 *((uint16_t *)vaddr) = VAL16(req); 177 else 178 VAL16(req) = *((uint16_t *)vaddr); 179 break; 180 case 4: 181 if (req->write) 182 *((uint32_t *)vaddr) = VAL32(req); 183 else 184 VAL32(req) = *((uint32_t *)vaddr); 185 break; 186 case 8: 187 if (req->write) 188 *((uint64_t *)vaddr) = VAL64(req); 189 else 190 VAL64(req) = *((uint64_t *)vaddr); 191 break; 192 default: 193 rval = DDI_FAILURE; 194 } 195 pci_cfgacc_unmap(); 196 mutex_exit(&pcicfg_mmio_mutex); 197 198 return (rval); 199 } 200 201 static boolean_t 202 pci_cfgacc_valid(pci_cfgacc_req_t *req) 203 { 204 return (IS_P2ALIGNED(req->offset, req->size) && 205 (req->offset < PCIE_CFG_SPACE_SIZE)); 206 } 207 208 void 209 pci_cfgacc_acc(pci_cfgacc_req_t *req) 210 { 211 uint8_t bus; 212 213 if (!req->write) 214 VAL64(req) = 0; 215 216 if (!pci_cfgacc_valid(req)) { 217 if (!req->write) 218 VAL64(req) = (uint64_t)-1; 219 return; 220 } 221 222 bus = PCI_BDF_BUS(req->bdf); 223 if (pci_cfgacc_force_io || (mcfg_mem_base == NULL) || 224 (bus < mcfg_bus_start) || (bus > mcfg_bus_end)) 225 goto ioacc; 226 227 if (req->ioacc) 228 goto ioacc; 229 230 /* check if workaround is needed */ 231 if (pci_cfgacc_find_workaround(req->bdf)) 232 goto ioacc; 233 234 if (pci_cfgacc_mmio(req) != DDI_SUCCESS) 235 goto ioacc; 236 237 return; 238 239 ioacc: 240 pci_cfgacc_io(req); 241 req->ioacc = B_TRUE; 242 } 243 244 typedef struct cfgacc_bus_range { 245 struct cfgacc_bus_range *next; 246 uint16_t bdf; 247 uchar_t secbus; 248 uchar_t subbus; 249 } cfgacc_bus_range_t; 250 251 cfgacc_bus_range_t *pci_cfgacc_bus_head = NULL; 252 253 #define BUS_INSERT(prev, el) \ 254 el->next = *prev; \ 255 *prev = el; 256 257 #define BUS_REMOVE(prev, el) \ 258 *prev = el->next; 259 260 /* 261 * This function is only supposed to be called in device tree setup time, 262 * thus no lock is needed. 263 */ 264 void 265 pci_cfgacc_add_workaround(uint16_t bdf, uchar_t secbus, uchar_t subbus) 266 { 267 cfgacc_bus_range_t *entry; 268 269 entry = kmem_zalloc(sizeof (cfgacc_bus_range_t), KM_SLEEP); 270 entry->bdf = bdf; 271 entry->secbus = secbus; 272 entry->subbus = subbus; 273 BUS_INSERT(&pci_cfgacc_bus_head, entry); 274 } 275 276 boolean_t 277 pci_cfgacc_find_workaround(uint16_t bdf) 278 { 279 cfgacc_bus_range_t *entry; 280 uchar_t bus; 281 282 for (entry = pci_cfgacc_bus_head; entry != NULL; 283 entry = entry->next) { 284 if (bdf == entry->bdf) { 285 /* found a device which is known to be broken */ 286 return (B_TRUE); 287 } 288 289 bus = PCI_BDF_BUS(bdf); 290 if ((bus != 0) && (bus >= entry->secbus) && 291 (bus <= entry->subbus)) { 292 /* 293 * found a device whose parent/grandparent is 294 * known to be broken. 295 */ 296 return (B_TRUE); 297 } 298 } 299 300 return (B_FALSE); 301 } 302