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