freebsd_pci.c revision cad31331
1/* 2 * (C) Copyright Eric Anholt 2006 3 * (C) Copyright IBM Corporation 2006 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * on the rights to use, copy, modify, merge, publish, distribute, sub 10 * license, and/or sell copies of the Software, and to permit persons to whom 11 * the Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 */ 25 26/** 27 * \file freebsd_pci.c 28 * 29 * Access the kernel PCI support using /dev/pci's ioctl and mmap interface. 30 * 31 * \author Eric Anholt <eric@anholt.net> 32 */ 33 34#include <stdlib.h> 35#include <stdio.h> 36#include <string.h> 37#include <unistd.h> 38#include <fcntl.h> 39#include <errno.h> 40#include <sys/types.h> 41#include <sys/param.h> 42#include <sys/pciio.h> 43#include <sys/mman.h> 44#include <sys/memrange.h> 45 46#include "config.h" 47#include "pciaccess.h" 48#include "pciaccess_private.h" 49 50#define PCIC_DISPLAY 0x03 51#define PCIS_DISPLAY_VGA 0x00 52#define PCIS_DISPLAY_XGA 0x01 53#define PCIS_DISPLAY_3D 0x02 54#define PCIS_DISPLAY_OTHER 0x80 55 56/* Registers taken from pcireg.h */ 57#define PCIR_COMMAND 0x04 58#define PCIM_CMD_PORTEN 0x0001 59#define PCIM_CMD_MEMEN 0x0002 60#define PCIR_BIOS 0x30 61#define PCIM_BIOS_ENABLE 0x01 62#define PCIM_BIOS_ADDR_MASK 0xfffff800 63 64#define PCIR_BARS 0x10 65#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) 66#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) 67#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) 68#define PCIM_BAR_MEM_TYPE 0x00000006 69#define PCIM_BAR_MEM_64 4 70#define PCIM_BAR_MEM_PREFETCH 0x00000008 71#define PCIM_BAR_SPACE 0x00000001 72#define PCIM_BAR_MEM_SPACE 0 73#define PCIM_BAR_IO_SPACE 1 74 75/** 76 * FreeBSD private pci_system structure that extends the base pci_system 77 * structure. 78 * 79 * It is initialized once and used as a global, just as pci_system is used. 80 */ 81_pci_hidden 82struct freebsd_pci_system { 83 /* This must be the first entry in the structure, as pci_system_cleanup() 84 * frees pci_sys. 85 */ 86 struct pci_system pci_sys; 87 88 int pcidev; /**< fd for /dev/pci */ 89} *freebsd_pci_sys; 90 91/** 92 * Map a memory region for a device using /dev/mem. 93 * 94 * \param dev Device whose memory region is to be mapped. 95 * \param map Parameters of the mapping that is to be created. 96 * 97 * \return 98 * Zero on success or an \c errno value on failure. 99 */ 100static int 101pci_device_freebsd_map_range(struct pci_device *dev, 102 struct pci_device_mapping *map) 103{ 104 const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 105 ? (PROT_READ | PROT_WRITE) : PROT_READ; 106 struct mem_range_desc mrd; 107 struct mem_range_op mro; 108 109 int fd, err = 0; 110 111 fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 112 if (fd == -1) 113 return errno; 114 115 map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); 116 117 if (map->memory == MAP_FAILED) { 118 err = errno; 119 } 120 121 mrd.mr_base = map->base; 122 mrd.mr_len = map->size; 123 strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 124 if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 125 mrd.mr_flags = MDF_WRITEBACK; 126 else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 127 mrd.mr_flags = MDF_WRITECOMBINE; 128 else 129 mrd.mr_flags = MDF_UNCACHEABLE; 130 mro.mo_desc = &mrd; 131 mro.mo_arg[0] = MEMRANGE_SET_UPDATE; 132 133 /* No need to set an MTRR if it's the default mode. */ 134 if (mrd.mr_flags != MDF_UNCACHEABLE) { 135 if (ioctl(fd, MEMRANGE_SET, &mro)) { 136 fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno)); 137 } 138 } 139 140 close(fd); 141 142 return err; 143} 144 145static int 146pci_device_freebsd_unmap_range( struct pci_device *dev, 147 struct pci_device_mapping *map ) 148{ 149 struct mem_range_desc mrd; 150 struct mem_range_op mro; 151 int fd; 152 153 if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 154 (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) 155 { 156 fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 157 if (fd != -1) { 158 mrd.mr_base = map->base; 159 mrd.mr_len = map->size; 160 strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 161 mrd.mr_flags = MDF_UNCACHEABLE; 162 mro.mo_desc = &mrd; 163 mro.mo_arg[0] = MEMRANGE_SET_REMOVE; 164 165 if (ioctl(fd, MEMRANGE_SET, &mro)) { 166 fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); 167 } 168 169 close(fd); 170 } else { 171 fprintf(stderr, "Failed to open /dev/mem\n"); 172 } 173 } 174 175 return pci_device_generic_unmap_range(dev, map); 176} 177 178static int 179pci_device_freebsd_read( struct pci_device * dev, void * data, 180 pciaddr_t offset, pciaddr_t size, 181 pciaddr_t * bytes_read ) 182{ 183 struct pci_io io; 184 185#if HAVE_PCI_IO_PC_DOMAIN 186 io.pi_sel.pc_domain = dev->domain; 187#endif 188 io.pi_sel.pc_bus = dev->bus; 189 io.pi_sel.pc_dev = dev->dev; 190 io.pi_sel.pc_func = dev->func; 191 192 *bytes_read = 0; 193 while ( size > 0 ) { 194 int toread = (size < 4) ? size : 4; 195 196 /* Only power of two allowed. */ 197 if (toread == 3) 198 toread = 2; 199 200 io.pi_reg = offset; 201 io.pi_width = toread; 202 203 if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) 204 return errno; 205 206 memcpy(data, &io.pi_data, toread ); 207 208 offset += toread; 209 data = (char *)data + toread; 210 size -= toread; 211 *bytes_read += toread; 212 } 213 214 return 0; 215} 216 217 218static int 219pci_device_freebsd_write( struct pci_device * dev, const void * data, 220 pciaddr_t offset, pciaddr_t size, 221 pciaddr_t * bytes_written ) 222{ 223 struct pci_io io; 224 225#if HAVE_PCI_IO_PC_DOMAIN 226 io.pi_sel.pc_domain = dev->domain; 227#endif 228 io.pi_sel.pc_bus = dev->bus; 229 io.pi_sel.pc_dev = dev->dev; 230 io.pi_sel.pc_func = dev->func; 231 232 *bytes_written = 0; 233 while ( size > 0 ) { 234 int towrite = (size < 4 ? size : 4); 235 236 /* Only power of two allowed. */ 237 if (towrite == 3) 238 towrite = 2; 239 240 io.pi_reg = offset; 241 io.pi_width = towrite; 242 memcpy( &io.pi_data, data, towrite ); 243 244 if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) 245 return errno; 246 247 offset += towrite; 248 data = (char *)data + towrite; 249 size -= towrite; 250 *bytes_written += towrite; 251 } 252 253 return 0; 254} 255 256/** 257 * Read a VGA rom using the 0xc0000 mapping. 258 * 259 * This function should be extended to handle access through PCI resources, 260 * which should be more reliable when available. 261 */ 262static int 263pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) 264{ 265 struct pci_device_private *priv = (struct pci_device_private *) dev; 266 void *bios; 267 pciaddr_t rom_base; 268 uint32_t rom; 269 uint16_t reg; 270 int pci_rom, memfd; 271 272 if ( ( dev->device_class & 0x00ffff00 ) != 273 ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) 274 { 275 return ENOSYS; 276 } 277 278 if (priv->rom_base == 0) { 279#if defined(__amd64__) || defined(__i386__) 280 rom_base = 0xc0000; 281 pci_rom = 0; 282#else 283 return ENOSYS; 284#endif 285 } else { 286 rom_base = priv->rom_base; 287 pci_rom = 1; 288 289 pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); 290 pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); 291 pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); 292 pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); 293 } 294 295 printf("Using rom_base = 0x%lx\n", (long)rom_base); 296 memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); 297 if ( memfd == -1 ) 298 return errno; 299 300 bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); 301 if ( bios == MAP_FAILED ) { 302 close( memfd ); 303 return errno; 304 } 305 306 memcpy( buffer, bios, dev->rom_size ); 307 308 munmap( bios, dev->rom_size ); 309 close( memfd ); 310 311 if (pci_rom) { 312 pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); 313 pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); 314 } 315 316 return 0; 317} 318 319/** Returns the number of regions (base address registers) the device has */ 320 321static int 322pci_device_freebsd_get_num_regions( struct pci_device * dev ) 323{ 324 struct pci_device_private *priv = (struct pci_device_private *) dev; 325 326 switch (priv->header_type) { 327 case 0: 328 return 6; 329 case 1: 330 return 2; 331 case 2: 332 return 1; 333 default: 334 printf("unknown header type %02x\n", priv->header_type); 335 return 0; 336 } 337} 338 339#ifdef PCIOCGETBAR 340 341static int 342pci_device_freebsd_probe( struct pci_device * dev ) 343{ 344 struct pci_device_private *priv = (struct pci_device_private *) dev; 345 struct pci_bar_io bar; 346 uint8_t irq; 347 int err, i; 348 349#if HAVE_PCI_IO_PC_DOMAIN 350 bar.pbi_sel.pc_domain = dev->domain; 351#endif 352 bar.pbi_sel.pc_bus = dev->bus; 353 bar.pbi_sel.pc_dev = dev->dev; 354 bar.pbi_sel.pc_func = dev->func; 355 356 357 /* Many of the fields were filled in during initial device enumeration. 358 * At this point, we need to fill in regions, rom_size, and irq. 359 */ 360 361 err = pci_device_cfg_read_u8( dev, &irq, 60 ); 362 if (err) 363 return errno; 364 dev->irq = irq; 365 366 for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 367 bar.pbi_reg = PCIR_BAR(i); 368 if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) 369 continue; 370 371 if (PCI_BAR_IO(bar.pbi_base)) 372 dev->regions[i].is_IO = 1; 373 374 if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) 375 dev->regions[i].is_64 = 1; 376 377 if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 378 dev->regions[i].is_prefetchable = 1; 379 380 dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); 381 dev->regions[i].size = bar.pbi_length; 382 } 383 384 /* If it's a VGA device, set up the rom size for read_rom using the 385 * 0xc0000 mapping. 386 */ 387 if ((dev->device_class & 0x00ffff00) == 388 ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { 389 dev->rom_size = 64 * 1024; 390 } 391 392 return 0; 393} 394 395#else 396 397/** Masks out the flag bigs of the base address register value */ 398static uint32_t 399get_map_base( uint32_t val ) 400{ 401 if (val & 0x01) 402 return val & ~0x03; 403 else 404 return val & ~0x0f; 405} 406 407/** Returns the size of a region based on the all-ones test value */ 408static int 409get_test_val_size( uint32_t testval ) 410{ 411 if (testval == 0) 412 return 0; 413 414 /* Mask out the flag bits */ 415 testval = get_map_base( testval ); 416 417 return 1 << (ffs(testval) - 1); 418} 419 420/** 421 * Sets the address and size information for the region from config space 422 * registers. 423 * 424 * This would be much better provided by a kernel interface. 425 * 426 * \return 0 on success, or an errno value. 427 */ 428static int 429pci_device_freebsd_get_region_info( struct pci_device * dev, int region, 430 int bar ) 431{ 432 uint32_t addr, testval; 433 uint16_t cmd; 434 int err; 435 436 /* Get the base address */ 437 err = pci_device_cfg_read_u32( dev, &addr, bar ); 438 if (err != 0) 439 return err; 440 441 /* 442 * We are going to be doing evil things to the registers here 443 * so disable them via the command register first. 444 */ 445 err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND ); 446 if (err != 0) 447 return err; 448 449 err = pci_device_cfg_write_u16( dev, 450 cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 451 PCIR_COMMAND ); 452 if (err != 0) 453 return err; 454 455 /* Test write all ones to the register, then restore it. */ 456 err = pci_device_cfg_write_u32( dev, 0xffffffff, bar ); 457 if (err != 0) 458 return err; 459 err = pci_device_cfg_read_u32( dev, &testval, bar ); 460 if (err != 0) 461 return err; 462 err = pci_device_cfg_write_u32( dev, addr, bar ); 463 if (err != 0) 464 return err; 465 466 /* Restore the command register */ 467 err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND ); 468 if (err != 0) 469 return err; 470 471 if (addr & 0x01) 472 dev->regions[region].is_IO = 1; 473 if (addr & 0x04) 474 dev->regions[region].is_64 = 1; 475 if (addr & 0x08) 476 dev->regions[region].is_prefetchable = 1; 477 478 /* Set the size */ 479 dev->regions[region].size = get_test_val_size( testval ); 480 printf("size = 0x%lx\n", (long)dev->regions[region].size); 481 482 /* Set the base address value */ 483 if (dev->regions[region].is_64) { 484 uint32_t top; 485 486 err = pci_device_cfg_read_u32( dev, &top, bar + 4 ); 487 if (err != 0) 488 return err; 489 490 dev->regions[region].base_addr = ((uint64_t)top << 32) | 491 get_map_base(addr); 492 } else { 493 dev->regions[region].base_addr = get_map_base(addr); 494 } 495 496 return 0; 497} 498 499static int 500pci_device_freebsd_probe( struct pci_device * dev ) 501{ 502 struct pci_device_private *priv = (struct pci_device_private *) dev; 503 uint32_t reg, size; 504 uint8_t irq; 505 int err, i, bar; 506 507 /* Many of the fields were filled in during initial device enumeration. 508 * At this point, we need to fill in regions, rom_size, and irq. 509 */ 510 511 err = pci_device_cfg_read_u8( dev, &irq, 60 ); 512 if (err) 513 return errno; 514 dev->irq = irq; 515 516 bar = 0x10; 517 for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 518 pci_device_freebsd_get_region_info( dev, i, bar ); 519 if (dev->regions[i].is_64) { 520 bar += 0x08; 521 i++; 522 } else 523 bar += 0x04; 524 } 525 526 /* If it's a VGA device, set up the rom size for read_rom */ 527 if ((dev->device_class & 0x00ffff00) == 528 ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) 529 { 530 err = pci_device_cfg_read_u32( dev, ®, PCIR_BIOS ); 531 if (err) 532 return errno; 533 534 if (reg == 0) { 535 dev->rom_size = 0x10000; 536 return 0; 537 } 538 539 err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS ); 540 if (err) 541 return errno; 542 pci_device_cfg_read_u32( dev, &size, PCIR_BIOS ); 543 pci_device_cfg_write_u32( dev, reg, PCIR_BIOS ); 544 545 if ((reg & PCIM_BIOS_ADDR_MASK) != 0) { 546 priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK); 547 dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK); 548 } 549 } 550 551 return 0; 552} 553 554#endif 555 556static void 557pci_system_freebsd_destroy(void) 558{ 559 close(freebsd_pci_sys->pcidev); 560 free(freebsd_pci_sys->pci_sys.devices); 561 freebsd_pci_sys = NULL; 562} 563 564static const struct pci_system_methods freebsd_pci_methods = { 565 .destroy = pci_system_freebsd_destroy, 566 .destroy_device = NULL, /* nothing to do for this */ 567 .read_rom = pci_device_freebsd_read_rom, 568 .probe = pci_device_freebsd_probe, 569 .map_range = pci_device_freebsd_map_range, 570 .unmap_range = pci_device_freebsd_unmap_range, 571 .read = pci_device_freebsd_read, 572 .write = pci_device_freebsd_write, 573 .fill_capabilities = pci_fill_capabilities_generic, 574}; 575 576/** 577 * Attempt to access the FreeBSD PCI interface. 578 */ 579_pci_hidden int 580pci_system_freebsd_create( void ) 581{ 582 struct pci_conf_io pciconfio; 583 struct pci_conf pciconf[255]; 584 int pcidev; 585 int i; 586 587 /* Try to open the PCI device */ 588 pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); 589 if ( pcidev == -1 ) 590 return ENXIO; 591 592 freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) ); 593 if ( freebsd_pci_sys == NULL ) { 594 close( pcidev ); 595 return ENOMEM; 596 } 597 pci_sys = &freebsd_pci_sys->pci_sys; 598 599 pci_sys->methods = & freebsd_pci_methods; 600 freebsd_pci_sys->pcidev = pcidev; 601 602 /* Probe the list of devices known by the system */ 603 bzero( &pciconfio, sizeof( struct pci_conf_io ) ); 604 pciconfio.match_buf_len = sizeof(pciconf); 605 pciconfio.matches = pciconf; 606 607 if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) { 608 free( pci_sys ); 609 close( pcidev ); 610 return errno; 611 } 612 613 if (pciconfio.status == PCI_GETCONF_ERROR ) { 614 free( pci_sys ); 615 close( pcidev ); 616 return EINVAL; 617 } 618 619 /* Translate the list of devices into pciaccess's format. */ 620 pci_sys->num_devices = pciconfio.num_matches; 621 pci_sys->devices = calloc( pciconfio.num_matches, 622 sizeof( struct pci_device_private ) ); 623 624 for ( i = 0; i < pciconfio.num_matches; i++ ) { 625 struct pci_conf *p = &pciconf[ i ]; 626 627#if HAVE_PCI_IO_PC_DOMAIN 628 pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; 629#else 630 pci_sys->devices[ i ].base.domain = 0; 631#endif 632 pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; 633 pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; 634 pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; 635 pci_sys->devices[ i ].base.vendor_id = p->pc_vendor; 636 pci_sys->devices[ i ].base.device_id = p->pc_device; 637 pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; 638 pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; 639 pci_sys->devices[ i ].base.revision = p->pc_revid; 640 pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | 641 (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; 642 pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; 643 } 644 645 return 0; 646} 647