netbsd_pci.c revision 9e884b7e
1/* 2 * Copyright (c) 2008 Juan Romero Pardines 3 * Copyright (c) 2008 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/ioctl.h> 20#include <sys/mman.h> 21#include <sys/types.h> 22 23#ifdef HAVE_MTRR 24#include <machine/sysarch.h> 25#include <machine/mtrr.h> 26#define netbsd_set_mtrr(mr, num) _X86_SYSARCH_L(set_mtrr)(mr, num) 27#endif 28 29#include <dev/pci/pcidevs.h> 30#include <dev/pci/pciio.h> 31#include <dev/pci/pcireg.h> 32 33#include <errno.h> 34#include <fcntl.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39 40 41#include <pci.h> 42 43#include "pciaccess.h" 44#include "pciaccess_private.h" 45 46#define PCIR_COMMAND 0x04 47#define PCIM_CMD_PORTEN 0x0001 48#define PCIM_CMD_MEMEN 0x0002 49#define PCIR_BIOS 0x30 50#define PCIM_BIOS_ENABLE 0x01 51#define PCIM_BIOS_ADDR_MASK 0xfffff800 52 53static int pcifd; 54 55static int 56pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val) 57{ 58 uint32_t rval; 59 60 if (pcibus_conf_read(pcifd, (unsigned int)bus, (unsigned int)dev, 61 (unsigned int)func, reg, &rval) == -1) 62 return (-1); 63 64 *val = rval; 65 66 return 0; 67} 68 69static int 70pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val) 71{ 72 return pcibus_conf_write(pcifd, (unsigned int)bus, (unsigned int)dev, 73 (unsigned int)func, reg, val); 74} 75 76static int 77pci_nfuncs(int bus, int dev) 78{ 79 uint32_t hdr; 80 81 if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) 82 return -1; 83 84 return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); 85} 86 87/*ARGSUSED*/ 88static int 89pci_device_netbsd_map_range(struct pci_device *dev, 90 struct pci_device_mapping *map) 91{ 92#ifdef HAVE_MTRR 93 struct mtrr m; 94 int n = 1; 95#endif 96 int prot, fd, ret = 0; 97 98 prot = PROT_READ; 99 100 if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) 101 prot |= PROT_WRITE; 102 103 fd = open("/dev/mem", O_RDWR); 104 if (fd == -1) 105 return errno; 106 map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, 107 (off_t)map->base); 108 if (map->memory == MAP_FAILED) 109 return errno; 110 111#ifdef HAVE_MTRR 112 memset(&m, 0, sizeof(m)); 113 114 /* No need to set an MTRR if it's the default mode. */ 115 if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 116 (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 117 m.base = base; 118 m.flags = MTRR_VALID | MTRR_PRIVATE; 119 m.len = size; 120 m.owner = getpid(); 121 if (map->flags & PCI_DEV_MAP_FLAG_CACHEABLE) 122 m.type = MTRR_TYPE_WB; 123 if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 124 m.type = MTRR_TYPE_WC; 125 126 if ((netbsd_set_mtrr(&m, &n)) == -1) 127 ret = errno; 128 } 129#endif 130 131 close(fd); 132 133 return ret; 134} 135 136static int 137pci_device_netbsd_unmap_range(struct pci_device *dev, 138 struct pci_device_mapping *map) 139{ 140#ifdef HAVE_MTRR 141 struct mtrr m; 142 int n = 1; 143 144 memset(&m, 0, sizeof(m)); 145 146 if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 147 (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 148 m.base = map->base; 149 m.flags = 0; 150 m.len = size; 151 m.type = MTRR_TYPE_UC; 152 (void)netbsd_set_mtrr(&m, &n); 153 } 154#endif 155 156 return pci_device_generic_unmap_range(dev, map); 157} 158 159static int 160pci_device_netbsd_read(struct pci_device *dev, void *data, 161 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 162{ 163 u_int reg, rval; 164 165 *bytes_read = 0; 166 while (size > 0) { 167 size_t toread = MIN(size, 4 - (offset & 0x3)); 168 169 reg = (u_int)(offset & ~0x3); 170 171 if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus, 172 (unsigned int)dev->dev, (unsigned int)dev->func, 173 reg, &rval)) == -1) 174 return errno; 175 176 rval = htole32(rval); 177 rval >>= ((offset & 0x3) * 8); 178 179 memcpy(data, &rval, toread); 180 181 offset += toread; 182 data = (char *)data + toread; 183 size -= toread; 184 *bytes_read += toread; 185 } 186 187 return 0; 188} 189 190static int 191pci_device_netbsd_write(struct pci_device *dev, const void *data, 192 pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 193{ 194 u_int reg, val; 195 196 if ((offset % 4) != 0 || (size % 4) != 0) 197 return EINVAL; 198 199 *bytes_written = 0; 200 while (size > 0) { 201 reg = (u_int)offset; 202 memcpy(&val, data, 4); 203 204 if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus, 205 (unsigned int)dev->dev, (unsigned int)dev->func, 206 reg, val)) == -1) 207 return errno; 208 209 offset += 4; 210 data = (const char *)data + 4; 211 size -= 4; 212 *bytes_written += 4; 213 } 214 215 return 0; 216} 217 218static void 219pci_system_netbsd_destroy(void) 220{ 221 close(pcifd); 222 free(pci_sys); 223 pci_sys = NULL; 224} 225 226static int 227pci_device_netbsd_probe(struct pci_device *device) 228{ 229 struct pci_device_private *priv = 230 (struct pci_device_private *)(void *)device; 231 struct pci_mem_region *region; 232 uint64_t reg64, size64; 233 uint32_t bar, reg, size; 234 int bus, dev, func, err; 235 236 bus = device->bus; 237 dev = device->dev; 238 func = device->func; 239 240 err = pci_read(bus, dev, func, PCI_BHLC_REG, ®); 241 if (err) 242 return err; 243 244 priv->header_type = PCI_HDRTYPE_TYPE(reg); 245 if (priv->header_type != 0) 246 return 0; 247 248 region = device->regions; 249 for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; 250 bar += sizeof(uint32_t), region++) { 251 err = pci_read(bus, dev, func, bar, ®); 252 if (err) 253 return err; 254 255 /* Probe the size of the region. */ 256 err = pci_write(bus, dev, func, bar, (unsigned int)~0); 257 if (err) 258 return err; 259 pci_read(bus, dev, func, bar, &size); 260 pci_write(bus, dev, func, bar, reg); 261 262 if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { 263 region->is_IO = 1; 264 region->base_addr = PCI_MAPREG_IO_ADDR(reg); 265 region->size = PCI_MAPREG_IO_SIZE(size); 266 } else { 267 if (PCI_MAPREG_MEM_PREFETCHABLE(reg)) 268 region->is_prefetchable = 1; 269 switch(PCI_MAPREG_MEM_TYPE(reg)) { 270 case PCI_MAPREG_MEM_TYPE_32BIT: 271 case PCI_MAPREG_MEM_TYPE_32BIT_1M: 272 region->base_addr = PCI_MAPREG_MEM_ADDR(reg); 273 region->size = PCI_MAPREG_MEM_SIZE(size); 274 break; 275 case PCI_MAPREG_MEM_TYPE_64BIT: 276 region->is_64 = 1; 277 278 reg64 = reg; 279 size64 = size; 280 281 bar += sizeof(uint32_t); 282 283 err = pci_read(bus, dev, func, bar, ®); 284 if (err) 285 return err; 286 reg64 |= (uint64_t)reg << 32; 287 288 err = pci_write(bus, dev, func, bar, 289 (unsigned int)~0); 290 if (err) 291 return err; 292 pci_read(bus, dev, func, bar, &size); 293 pci_write(bus, dev, func, bar, 294 (unsigned int)(reg64 >> 32)); 295 size64 |= (uint64_t)size << 32; 296 297 region->base_addr = 298 (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64); 299 region->size = 300 (unsigned long)PCI_MAPREG_MEM64_SIZE(size64); 301 region++; 302 break; 303 } 304 } 305 } 306 307 return 0; 308} 309 310/** 311 * Read a VGA rom using the 0xc0000 mapping. 312 * 313 * This function should be extended to handle access through PCI resources, 314 * which should be more reliable when available. 315 */ 316static int 317pci_device_netbsd_read_rom(struct pci_device *dev, void *buffer) 318{ 319 struct pci_device_private *priv = (struct pci_device_private *)(void *)dev; 320 void *bios; 321 pciaddr_t rom_base; 322 size_t rom_size; 323 uint32_t bios_val, command_val; 324 int pci_rom, memfd; 325 326 if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY || 327 ((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA) 328 return ENOSYS; 329 330 if (priv->rom_base == 0) { 331#if defined(__amd64__) || defined(__i386__) 332 rom_base = 0xc0000; 333 rom_size = 0x10000; 334 pci_rom = 0; 335#else 336 return ENOSYS; 337#endif 338 } else { 339 rom_base = priv->rom_base; 340 rom_size = dev->rom_size; 341 pci_rom = 1; 342 if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus, 343 (unsigned int)dev->dev, (unsigned int)dev->func, 344 PCIR_COMMAND, &command_val)) == -1) 345 return errno; 346 if ((command_val & PCIM_CMD_MEMEN) == 0) { 347 if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus, 348 (unsigned int)dev->dev, (unsigned int)dev->func, 349 PCIR_COMMAND, command_val | PCIM_CMD_MEMEN)) == -1) 350 return errno; 351 } 352 if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus, 353 (unsigned int)dev->dev, (unsigned int)dev->func, 354 PCIR_BIOS, &bios_val)) == -1) 355 return errno; 356 if ((bios_val & PCIM_BIOS_ENABLE) == 0) { 357 if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus, 358 (unsigned int)dev->dev, (unsigned int)dev->func, 359 PCIR_BIOS, bios_val | PCIM_BIOS_ENABLE)) == -1) 360 return errno; 361 } 362 } 363 364 fprintf(stderr, "Using rom_base = 0x%lx (pci_rom=%d)\n", (long)rom_base, 365 pci_rom); 366 memfd = open("/dev/mem", O_RDONLY); 367 if (memfd == -1) 368 return errno; 369 370 bios = mmap(NULL, rom_size, PROT_READ, 0, memfd, (off_t)rom_base); 371 if (bios == MAP_FAILED) { 372 int serrno = errno; 373 close(memfd); 374 return serrno; 375 } 376 377 memcpy(buffer, bios, rom_size); 378 379 munmap(bios, rom_size); 380 close(memfd); 381 382 if (pci_rom) { 383 if ((command_val & PCIM_CMD_MEMEN) == 0) { 384 if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus, 385 (unsigned int)dev->dev, (unsigned int)dev->func, 386 PCIR_COMMAND, command_val)) == -1) 387 return errno; 388 } 389 if ((bios_val & PCIM_BIOS_ENABLE) == 0) { 390 if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus, 391 (unsigned int)dev->dev, (unsigned int)dev->func, 392 PCIR_BIOS, bios_val)) == -1) 393 return errno; 394 } 395 } 396 397 return 0; 398} 399 400static const struct pci_system_methods netbsd_pci_methods = { 401 .destroy = pci_system_netbsd_destroy, 402 .destroy_device = NULL, 403 .read_rom = pci_device_netbsd_read_rom, 404 .probe = pci_device_netbsd_probe, 405 .map_range = pci_device_netbsd_map_range, 406 .unmap_range = pci_device_netbsd_unmap_range, 407 .read = pci_device_netbsd_read, 408 .write = pci_device_netbsd_write, 409 .fill_capabilities = pci_fill_capabilities_generic 410}; 411 412int 413pci_system_netbsd_create(void) 414{ 415 struct pci_device_private *device; 416 int bus, dev, func, ndevs, nfuncs; 417 uint32_t reg; 418 419 pcifd = open("/dev/pci0", O_RDWR); 420 if (pcifd == -1) 421 return ENXIO; 422 423 pci_sys = calloc(1, sizeof(struct pci_system)); 424 if (pci_sys == NULL) { 425 close(pcifd); 426 return ENOMEM; 427 } 428 429 pci_sys->methods = &netbsd_pci_methods; 430 431 ndevs = 0; 432 for (bus = 0; bus < 256; bus++) { 433 for (dev = 0; dev < 32; dev++) { 434 nfuncs = pci_nfuncs(bus, dev); 435 for (func = 0; func < nfuncs; func++) { 436 if (pci_read(bus, dev, func, PCI_ID_REG, 437 ®) != 0) 438 continue; 439 if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 440 PCI_VENDOR(reg) == 0) 441 continue; 442 443 ndevs++; 444 } 445 } 446 } 447 448 pci_sys->num_devices = ndevs; 449 pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); 450 if (pci_sys->devices == NULL) { 451 free(pci_sys); 452 close(pcifd); 453 return ENOMEM; 454 } 455 456 device = pci_sys->devices; 457 for (bus = 0; bus < 256; bus++) { 458 for (dev = 0; dev < 32; dev++) { 459 nfuncs = pci_nfuncs(bus, dev); 460 for (func = 0; func < nfuncs; func++) { 461 if (pci_read(bus, dev, func, PCI_ID_REG, 462 ®) != 0) 463 continue; 464 if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 465 PCI_VENDOR(reg) == 0) 466 continue; 467 468 device->base.domain = 0; 469 device->base.bus = bus; 470 device->base.dev = dev; 471 device->base.func = func; 472 device->base.vendor_id = PCI_VENDOR(reg); 473 device->base.device_id = PCI_PRODUCT(reg); 474 475 if (pci_read(bus, dev, func, PCI_CLASS_REG, 476 ®) != 0) 477 continue; 478 479 device->base.device_class = 480 PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 | 481 PCI_SUBCLASS(reg) << 8; 482 device->base.revision = PCI_REVISION(reg); 483 484 if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG, 485 ®) != 0) 486 continue; 487 488 device->base.subvendor_id = PCI_VENDOR(reg); 489 device->base.subdevice_id = PCI_PRODUCT(reg); 490 491 device++; 492 } 493 } 494 } 495 496 return 0; 497} 498