1 /* $NetBSD: pci_addr_fixup.c,v 1.10 2017/07/28 14:26:50 maxv Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: pci_addr_fixup.c,v 1.10 2017/07/28 14:26:50 maxv Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/extent.h> 37 38 #include <sys/bus.h> 39 40 #include <dev/pci/pcireg.h> 41 #include <dev/pci/pcivar.h> 42 #include <dev/pci/pcidevs.h> 43 44 #include <x86/pci/pci_addr_fixup.h> 45 46 struct pciaddr pciaddr; 47 48 static void pciaddr_resource_reserve(pci_chipset_tag_t, pcitag_t, void *); 49 static int pciaddr_do_resource_reserve(pci_chipset_tag_t, pcitag_t, int, 50 void *, int, bus_addr_t *, bus_size_t); 51 static void pciaddr_resource_allocate(pci_chipset_tag_t, pcitag_t, void *); 52 static int pciaddr_do_resource_allocate(pci_chipset_tag_t, pcitag_t, int, 53 void *, int, bus_addr_t *, bus_size_t); 54 static int device_is_agp(pci_chipset_tag_t, pcitag_t); 55 56 #define PCIADDR_MEM_START 0x0 57 #define PCIADDR_MEM_END 0xffffffff 58 #define PCIADDR_PORT_START 0x0 59 #define PCIADDR_PORT_END 0xffff 60 61 /* for ISA devices */ 62 #define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */ 63 #define PCIADDR_ISAMEM_RESERVE (16 * 1024 * 1024) 64 65 void 66 pci_addr_fixup(pci_chipset_tag_t pc, int maxbus) 67 { 68 extern paddr_t avail_end; 69 const char *verbose_header = 70 "[%s]-----------------------\n" 71 " device vendor product\n" 72 " register space address size\n" 73 "--------------------------------------------\n"; 74 const char *verbose_footer = 75 "--------------------------[%3d devices bogus]\n"; 76 const struct { 77 bus_addr_t start; 78 bus_size_t size; 79 const char *name; 80 } system_reserve [] = { 81 { 0xfec00000, 0x100000, "I/O APIC" }, 82 { 0xfee00000, 0x100000, "Local APIC" }, 83 { 0xfffe0000, 0x20000, "BIOS PROM" }, 84 { 0, 0, 0 }, /* terminator */ 85 }, *srp; 86 paddr_t start; 87 int error; 88 89 pciaddr.extent_mem = extent_create("PCI I/O memory space", 90 PCIADDR_MEM_START, 91 PCIADDR_MEM_END, 92 0, 0, EX_NOWAIT); 93 KASSERT(pciaddr.extent_mem); 94 pciaddr.extent_port = extent_create("PCI I/O port space", 95 PCIADDR_PORT_START, 96 PCIADDR_PORT_END, 97 0, 0, EX_NOWAIT); 98 KASSERT(pciaddr.extent_port); 99 100 /* 101 * 1. check & reserve system BIOS setting. 102 */ 103 aprint_debug(verbose_header, "System BIOS Setting"); 104 pci_device_foreach(pc, maxbus, pciaddr_resource_reserve, NULL); 105 aprint_debug(verbose_footer, pciaddr.nbogus); 106 107 /* 108 * 2. reserve non-PCI area. 109 */ 110 for (srp = system_reserve; srp->size; srp++) { 111 error = extent_alloc_region(pciaddr.extent_mem, srp->start, 112 srp->size, 113 EX_NOWAIT| EX_MALLOCOK); 114 if (error != 0) { 115 aprint_error("WARNING: can't reserve area for %s.\n", 116 srp->name); 117 } 118 } 119 120 /* 121 * 3. determine allocation space 122 */ 123 start = x86_round_page(avail_end + 1); 124 if (start < PCIADDR_ISAMEM_RESERVE) 125 start = PCIADDR_ISAMEM_RESERVE; 126 pciaddr.mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1); 127 pciaddr.port_alloc_start = PCIADDR_ISAPORT_RESERVE; 128 aprint_debug(" Physical memory end: 0x%08x\n PCI memory mapped I/O " 129 "space start: 0x%08x\n", (unsigned)avail_end, 130 (unsigned)pciaddr.mem_alloc_start); 131 132 if (pciaddr.nbogus == 0) 133 return; /* no need to fixup */ 134 135 /* 136 * 4. do fixup 137 */ 138 aprint_debug(verbose_header, "PCIBIOS fixup stage"); 139 pciaddr.nbogus = 0; 140 pci_device_foreach_min(pc, 0, maxbus, pciaddr_resource_allocate, NULL); 141 aprint_debug(verbose_footer, pciaddr.nbogus); 142 143 } 144 145 static void 146 pciaddr_resource_reserve(pci_chipset_tag_t pc, pcitag_t tag, 147 void *context) 148 { 149 pciaddr_print_devid(pc, tag); 150 pciaddr_resource_manage(pc, tag, 151 pciaddr_do_resource_reserve, 152 &pciaddr); 153 } 154 155 static void 156 pciaddr_resource_allocate(pci_chipset_tag_t pc, pcitag_t tag, 157 void *context) 158 { 159 pciaddr_print_devid(pc, tag); 160 pciaddr_resource_manage(pc, tag, 161 pciaddr_do_resource_allocate, 162 &pciaddr); 163 } 164 165 void 166 pciaddr_resource_manage(pci_chipset_tag_t pc, pcitag_t tag, 167 pciaddr_resource_manage_func_t func, void *ctx) 168 { 169 pcireg_t val, mask; 170 bus_addr_t addr; 171 bus_size_t size; 172 int error, useport, usemem, mapreg, type, reg_start, reg_end, width; 173 174 val = pci_conf_read(pc, tag, PCI_BHLC_REG); 175 switch (PCI_HDRTYPE_TYPE(val)) { 176 default: 177 aprint_error("WARNING: unknown PCI device header."); 178 pciaddr.nbogus++; 179 return; 180 case PCI_HDRTYPE_DEVICE: 181 reg_start = PCI_MAPREG_START; 182 reg_end = PCI_MAPREG_END; 183 break; 184 case PCI_HDRTYPE_PPB: /* PCI-PCI bridge */ 185 reg_start = PCI_MAPREG_START; 186 reg_end = PCI_MAPREG_PPB_END; 187 break; 188 case PCI_HDRTYPE_PCB: /* PCI-CardBus bridge */ 189 reg_start = PCI_MAPREG_START; 190 reg_end = PCI_MAPREG_PCB_END; 191 break; 192 } 193 error = useport = usemem = 0; 194 195 for (mapreg = reg_start; mapreg < reg_end; mapreg += width) { 196 /* inquire PCI device bus space requirement */ 197 val = pci_conf_read(pc, tag, mapreg); 198 pci_conf_write(pc, tag, mapreg, ~0); 199 200 mask = pci_conf_read(pc, tag, mapreg); 201 pci_conf_write(pc, tag, mapreg, val); 202 203 type = PCI_MAPREG_TYPE(val); 204 width = 4; 205 if (type == PCI_MAPREG_TYPE_MEM) { 206 if (PCI_MAPREG_MEM_TYPE(val) == 207 PCI_MAPREG_MEM_TYPE_64BIT) { 208 /* XXX We could examine the upper 32 bits 209 * XXX of the BAR here, but we are totally 210 * XXX unprepared to handle a non-zero value, 211 * XXX either here or anywhere else in 212 * XXX i386-land. 213 * XXX So just arrange to not look at the 214 * XXX upper 32 bits, lest we misinterpret 215 * XXX it as a 32-bit BAR set to zero. 216 */ 217 width = 8; 218 } 219 size = PCI_MAPREG_MEM_SIZE(mask); 220 } else { 221 size = PCI_MAPREG_IO_SIZE(mask); 222 } 223 addr = pciaddr_ioaddr(val); 224 225 if (size == 0) /* unused register */ 226 continue; 227 228 if (type == PCI_MAPREG_TYPE_MEM) 229 ++usemem; 230 else 231 ++useport; 232 233 /* reservation/allocation phase */ 234 error += (*func) (pc, tag, mapreg, ctx, type, &addr, size); 235 236 aprint_debug("\n\t%02xh %s 0x%08x 0x%08x", 237 mapreg, type ? "port" : "mem ", 238 (unsigned int)addr, (unsigned int)size); 239 } 240 241 /* enable/disable PCI device */ 242 val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 243 if (error == 0) 244 val |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | 245 PCI_COMMAND_MASTER_ENABLE); 246 else 247 val &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | 248 PCI_COMMAND_MASTER_ENABLE); 249 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, val); 250 251 if (error != 0) 252 pciaddr.nbogus++; 253 254 aprint_debug("\n\t\t[%s]\n", error ? "NG" : "OK"); 255 } 256 257 static int 258 pciaddr_do_resource_allocate(pci_chipset_tag_t pc, pcitag_t tag, 259 int mapreg, void *ctx, int type, bus_addr_t *addr, bus_size_t size) 260 { 261 struct pciaddr *pciaddrmap = (struct pciaddr *)ctx; 262 bus_addr_t start; 263 int error; 264 struct extent *ex; 265 266 if (*addr != 0) /* no need to allocate */ 267 return 0; 268 269 ex = (type == PCI_MAPREG_TYPE_MEM ? 270 pciaddrmap->extent_mem : pciaddrmap->extent_port); 271 272 /* XXX Don't allocate if device is AGP device to avoid conflict. */ 273 if (device_is_agp(pc, tag)) 274 return 0; 275 276 start = (type == PCI_MAPREG_TYPE_MEM ? 277 pciaddrmap->mem_alloc_start : pciaddrmap->port_alloc_start); 278 279 if (start < ex->ex_start || start + size - 1 >= ex->ex_end) { 280 aprint_debug("No available resources. fixup failed\n"); 281 return 1; 282 } 283 error = extent_alloc_subregion(ex, start, ex->ex_end, size, 284 size, 0, 285 EX_FAST|EX_NOWAIT|EX_MALLOCOK, 286 (u_long *)addr); 287 if (error) { 288 aprint_debug("No available resources. fixup failed\n"); 289 return 1; 290 } 291 292 /* write new address to PCI device configuration header */ 293 pci_conf_write(pc, tag, mapreg, *addr); 294 /* check */ 295 aprint_debug("pci_addr_fixup: "); 296 pciaddr_print_devid(pc, tag); 297 if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) { 298 pci_conf_write(pc, tag, mapreg, 0); /* clear */ 299 aprint_error("fixup failed. (new address=%#x)\n", (unsigned)*addr); 300 return 1; 301 } 302 aprint_debug("new address 0x%08x\n", (unsigned)*addr); 303 304 return 0; 305 } 306 307 int 308 pciaddr_do_resource_reserve(pci_chipset_tag_t pc, pcitag_t tag, 309 int mapreg, void *ctx, int type, bus_addr_t *addr, bus_size_t size) 310 { 311 struct extent *ex; 312 struct pciaddr *pciaddrmap = (struct pciaddr *)ctx; 313 int error; 314 315 if (*addr == 0) 316 return 1; 317 318 ex = (type == PCI_MAPREG_TYPE_MEM ? 319 pciaddrmap->extent_mem : pciaddrmap->extent_port); 320 321 error = extent_alloc_region(ex, *addr, size, EX_NOWAIT| EX_MALLOCOK); 322 if (error) { 323 aprint_debug("Resource conflict.\n"); 324 pci_conf_write(pc, tag, mapreg, 0); /* clear */ 325 return 1; 326 } 327 328 return 0; 329 } 330 331 bus_addr_t 332 pciaddr_ioaddr(uint32_t val) 333 { 334 return (PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM) 335 ? PCI_MAPREG_MEM_ADDR(val) 336 : PCI_MAPREG_IO_ADDR(val); 337 } 338 339 void 340 pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag) 341 { 342 int bus, device, function; 343 pcireg_t id; 344 345 id = pci_conf_read(pc, tag, PCI_ID_REG); 346 pci_decompose_tag(pc, tag, &bus, &device, &function); 347 aprint_debug("%03d:%02d:%d 0x%04x 0x%04x ", bus, device, function, 348 PCI_VENDOR(id), PCI_PRODUCT(id)); 349 } 350 351 static int 352 device_is_agp(pci_chipset_tag_t pc, pcitag_t tag) 353 { 354 pcireg_t class, status, rval; 355 int off; 356 357 /* Check AGP device. */ 358 class = pci_conf_read(pc, tag, PCI_CLASS_REG); 359 if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) { 360 status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 361 if (status & PCI_STATUS_CAPLIST_SUPPORT) { 362 rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG); 363 for (off = PCI_CAPLIST_PTR(rval); 364 off != 0; 365 off = PCI_CAPLIST_NEXT(rval) ) { 366 rval = pci_conf_read(pc, tag, off); 367 if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP) 368 return 1; 369 } 370 } 371 } 372 return 0; 373 } 374