freebsd_pci.c revision 6a94483f
14f5e7dd7Smrg/* 24f5e7dd7Smrg * (C) Copyright Eric Anholt 2006 34f5e7dd7Smrg * (C) Copyright IBM Corporation 2006 486ea1d58Smrg * (C) Copyright Mark Kettenis 2011 586ea1d58Smrg * (C) Copyright Robert Millan 2012 64f5e7dd7Smrg * All Rights Reserved. 74f5e7dd7Smrg * 84f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a 94f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"), 104f5e7dd7Smrg * to deal in the Software without restriction, including without limitation 114f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 124f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom 134f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions: 144f5e7dd7Smrg * 154f5e7dd7Smrg * The above copyright notice and this permission notice (including the next 164f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the 174f5e7dd7Smrg * Software. 184f5e7dd7Smrg * 194f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 204f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 214f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 224f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 234f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 244f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 254f5e7dd7Smrg * DEALINGS IN THE SOFTWARE. 264f5e7dd7Smrg */ 274f5e7dd7Smrg 284f5e7dd7Smrg/** 294f5e7dd7Smrg * \file freebsd_pci.c 304f5e7dd7Smrg * 314f5e7dd7Smrg * Access the kernel PCI support using /dev/pci's ioctl and mmap interface. 324f5e7dd7Smrg * 334f5e7dd7Smrg * \author Eric Anholt <eric@anholt.net> 344f5e7dd7Smrg */ 354f5e7dd7Smrg 366a94483fSmrg#ifdef HAVE_CONFIG_H 376a94483fSmrg#include "config.h" 386a94483fSmrg#endif 396a94483fSmrg 404f5e7dd7Smrg#include <stdlib.h> 414f5e7dd7Smrg#include <stdio.h> 424f5e7dd7Smrg#include <string.h> 434f5e7dd7Smrg#include <unistd.h> 444f5e7dd7Smrg#include <fcntl.h> 454f5e7dd7Smrg#include <errno.h> 464f5e7dd7Smrg#include <sys/types.h> 474f5e7dd7Smrg#include <sys/param.h> 484f5e7dd7Smrg#include <sys/pciio.h> 494f5e7dd7Smrg#include <sys/mman.h> 504f5e7dd7Smrg#include <sys/memrange.h> 514f5e7dd7Smrg 524f5e7dd7Smrg#include "pciaccess.h" 534f5e7dd7Smrg#include "pciaccess_private.h" 544f5e7dd7Smrg 554f5e7dd7Smrg#define PCIC_DISPLAY 0x03 564f5e7dd7Smrg#define PCIS_DISPLAY_VGA 0x00 574f5e7dd7Smrg#define PCIS_DISPLAY_XGA 0x01 584f5e7dd7Smrg#define PCIS_DISPLAY_3D 0x02 594f5e7dd7Smrg#define PCIS_DISPLAY_OTHER 0x80 604f5e7dd7Smrg 614f5e7dd7Smrg/* Registers taken from pcireg.h */ 624f5e7dd7Smrg#define PCIR_COMMAND 0x04 634f5e7dd7Smrg#define PCIM_CMD_PORTEN 0x0001 644f5e7dd7Smrg#define PCIM_CMD_MEMEN 0x0002 654f5e7dd7Smrg#define PCIR_BIOS 0x30 664f5e7dd7Smrg#define PCIM_BIOS_ENABLE 0x01 674f5e7dd7Smrg#define PCIM_BIOS_ADDR_MASK 0xfffff800 684f5e7dd7Smrg 694f5e7dd7Smrg#define PCIR_BARS 0x10 704f5e7dd7Smrg#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) 714f5e7dd7Smrg#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) 724f5e7dd7Smrg#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) 734f5e7dd7Smrg#define PCIM_BAR_MEM_TYPE 0x00000006 744f5e7dd7Smrg#define PCIM_BAR_MEM_64 4 754f5e7dd7Smrg#define PCIM_BAR_MEM_PREFETCH 0x00000008 764f5e7dd7Smrg#define PCIM_BAR_SPACE 0x00000001 774f5e7dd7Smrg#define PCIM_BAR_MEM_SPACE 0 784f5e7dd7Smrg#define PCIM_BAR_IO_SPACE 1 794f5e7dd7Smrg 804f5e7dd7Smrg/** 814f5e7dd7Smrg * FreeBSD private pci_system structure that extends the base pci_system 824f5e7dd7Smrg * structure. 834f5e7dd7Smrg * 844f5e7dd7Smrg * It is initialized once and used as a global, just as pci_system is used. 854f5e7dd7Smrg */ 864f5e7dd7Smrg_pci_hidden 874f5e7dd7Smrgstruct freebsd_pci_system { 884f5e7dd7Smrg /* This must be the first entry in the structure, as pci_system_cleanup() 894f5e7dd7Smrg * frees pci_sys. 904f5e7dd7Smrg */ 914f5e7dd7Smrg struct pci_system pci_sys; 924f5e7dd7Smrg 934f5e7dd7Smrg int pcidev; /**< fd for /dev/pci */ 944f5e7dd7Smrg} *freebsd_pci_sys; 954f5e7dd7Smrg 964f5e7dd7Smrg/** 974f5e7dd7Smrg * Map a memory region for a device using /dev/mem. 98cad31331Smrg * 994f5e7dd7Smrg * \param dev Device whose memory region is to be mapped. 1004f5e7dd7Smrg * \param map Parameters of the mapping that is to be created. 101cad31331Smrg * 1024f5e7dd7Smrg * \return 1034f5e7dd7Smrg * Zero on success or an \c errno value on failure. 1044f5e7dd7Smrg */ 1054f5e7dd7Smrgstatic int 1064f5e7dd7Smrgpci_device_freebsd_map_range(struct pci_device *dev, 1074f5e7dd7Smrg struct pci_device_mapping *map) 1084f5e7dd7Smrg{ 109cad31331Smrg const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 1104f5e7dd7Smrg ? (PROT_READ | PROT_WRITE) : PROT_READ; 1114f5e7dd7Smrg struct mem_range_desc mrd; 1124f5e7dd7Smrg struct mem_range_op mro; 1134f5e7dd7Smrg 1144f5e7dd7Smrg int fd, err = 0; 1154f5e7dd7Smrg 116cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1174f5e7dd7Smrg if (fd == -1) 1184f5e7dd7Smrg return errno; 1194f5e7dd7Smrg 1204f5e7dd7Smrg map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); 1214f5e7dd7Smrg 1224f5e7dd7Smrg if (map->memory == MAP_FAILED) { 1234f5e7dd7Smrg err = errno; 1244f5e7dd7Smrg } 1254f5e7dd7Smrg 1264f5e7dd7Smrg mrd.mr_base = map->base; 1274f5e7dd7Smrg mrd.mr_len = map->size; 1284f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1294f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 1304f5e7dd7Smrg mrd.mr_flags = MDF_WRITEBACK; 1314f5e7dd7Smrg else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 1324f5e7dd7Smrg mrd.mr_flags = MDF_WRITECOMBINE; 1334f5e7dd7Smrg else 1344f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1354f5e7dd7Smrg mro.mo_desc = &mrd; 1364f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_UPDATE; 1374f5e7dd7Smrg 1384f5e7dd7Smrg /* No need to set an MTRR if it's the default mode. */ 1394f5e7dd7Smrg if (mrd.mr_flags != MDF_UNCACHEABLE) { 1404f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1414f5e7dd7Smrg fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno)); 1424f5e7dd7Smrg } 1434f5e7dd7Smrg } 1444f5e7dd7Smrg 1454f5e7dd7Smrg close(fd); 1464f5e7dd7Smrg 1474f5e7dd7Smrg return err; 1484f5e7dd7Smrg} 1494f5e7dd7Smrg 1504f5e7dd7Smrgstatic int 1514f5e7dd7Smrgpci_device_freebsd_unmap_range( struct pci_device *dev, 1524f5e7dd7Smrg struct pci_device_mapping *map ) 1534f5e7dd7Smrg{ 1544f5e7dd7Smrg struct mem_range_desc mrd; 1554f5e7dd7Smrg struct mem_range_op mro; 1564f5e7dd7Smrg int fd; 1574f5e7dd7Smrg 1584f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1594f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) 1604f5e7dd7Smrg { 161cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1624f5e7dd7Smrg if (fd != -1) { 1634f5e7dd7Smrg mrd.mr_base = map->base; 1644f5e7dd7Smrg mrd.mr_len = map->size; 1654f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1664f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1674f5e7dd7Smrg mro.mo_desc = &mrd; 1684f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_REMOVE; 1694f5e7dd7Smrg 1704f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1714f5e7dd7Smrg fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); 1724f5e7dd7Smrg } 1734f5e7dd7Smrg 1744f5e7dd7Smrg close(fd); 1754f5e7dd7Smrg } else { 1764f5e7dd7Smrg fprintf(stderr, "Failed to open /dev/mem\n"); 1774f5e7dd7Smrg } 1784f5e7dd7Smrg } 1794f5e7dd7Smrg 1804f5e7dd7Smrg return pci_device_generic_unmap_range(dev, map); 1814f5e7dd7Smrg} 1824f5e7dd7Smrg 1834f5e7dd7Smrgstatic int 1844f5e7dd7Smrgpci_device_freebsd_read( struct pci_device * dev, void * data, 1854f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 1864f5e7dd7Smrg pciaddr_t * bytes_read ) 1874f5e7dd7Smrg{ 1884f5e7dd7Smrg struct pci_io io; 1894f5e7dd7Smrg 1904f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 1914f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 1924f5e7dd7Smrg#endif 1934f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 1944f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 1954f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 1964f5e7dd7Smrg 1974f5e7dd7Smrg *bytes_read = 0; 1984f5e7dd7Smrg while ( size > 0 ) { 1994f5e7dd7Smrg int toread = (size < 4) ? size : 4; 2004f5e7dd7Smrg 2014f5e7dd7Smrg /* Only power of two allowed. */ 2024f5e7dd7Smrg if (toread == 3) 2034f5e7dd7Smrg toread = 2; 2044f5e7dd7Smrg 2054f5e7dd7Smrg io.pi_reg = offset; 2064f5e7dd7Smrg io.pi_width = toread; 2074f5e7dd7Smrg 208cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) 2094f5e7dd7Smrg return errno; 2104f5e7dd7Smrg 2114f5e7dd7Smrg memcpy(data, &io.pi_data, toread ); 2124f5e7dd7Smrg 2134f5e7dd7Smrg offset += toread; 2144f5e7dd7Smrg data = (char *)data + toread; 2154f5e7dd7Smrg size -= toread; 2164f5e7dd7Smrg *bytes_read += toread; 2174f5e7dd7Smrg } 2184f5e7dd7Smrg 2194f5e7dd7Smrg return 0; 2204f5e7dd7Smrg} 2214f5e7dd7Smrg 2224f5e7dd7Smrg 2234f5e7dd7Smrgstatic int 2244f5e7dd7Smrgpci_device_freebsd_write( struct pci_device * dev, const void * data, 2254f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 2264f5e7dd7Smrg pciaddr_t * bytes_written ) 2274f5e7dd7Smrg{ 2284f5e7dd7Smrg struct pci_io io; 2294f5e7dd7Smrg 2304f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 2314f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 2324f5e7dd7Smrg#endif 2334f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 2344f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 2354f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 2364f5e7dd7Smrg 2374f5e7dd7Smrg *bytes_written = 0; 2384f5e7dd7Smrg while ( size > 0 ) { 2394f5e7dd7Smrg int towrite = (size < 4 ? size : 4); 2404f5e7dd7Smrg 2414f5e7dd7Smrg /* Only power of two allowed. */ 2424f5e7dd7Smrg if (towrite == 3) 2434f5e7dd7Smrg towrite = 2; 2444f5e7dd7Smrg 2454f5e7dd7Smrg io.pi_reg = offset; 2464f5e7dd7Smrg io.pi_width = towrite; 2474f5e7dd7Smrg memcpy( &io.pi_data, data, towrite ); 2484f5e7dd7Smrg 249cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) 2504f5e7dd7Smrg return errno; 2514f5e7dd7Smrg 2524f5e7dd7Smrg offset += towrite; 2534f5e7dd7Smrg data = (char *)data + towrite; 2544f5e7dd7Smrg size -= towrite; 2554f5e7dd7Smrg *bytes_written += towrite; 2564f5e7dd7Smrg } 2574f5e7dd7Smrg 2584f5e7dd7Smrg return 0; 2594f5e7dd7Smrg} 2604f5e7dd7Smrg 2614f5e7dd7Smrg/** 2624f5e7dd7Smrg * Read a VGA rom using the 0xc0000 mapping. 2634f5e7dd7Smrg * 2644f5e7dd7Smrg * This function should be extended to handle access through PCI resources, 2654f5e7dd7Smrg * which should be more reliable when available. 2664f5e7dd7Smrg */ 2674f5e7dd7Smrgstatic int 2684f5e7dd7Smrgpci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) 2694f5e7dd7Smrg{ 2704f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 2714f5e7dd7Smrg void *bios; 2724f5e7dd7Smrg pciaddr_t rom_base; 2734f5e7dd7Smrg uint32_t rom; 2744f5e7dd7Smrg uint16_t reg; 2754f5e7dd7Smrg int pci_rom, memfd; 2764f5e7dd7Smrg 2774f5e7dd7Smrg if ( ( dev->device_class & 0x00ffff00 ) != 2784f5e7dd7Smrg ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) 2794f5e7dd7Smrg { 2804f5e7dd7Smrg return ENOSYS; 2814f5e7dd7Smrg } 2824f5e7dd7Smrg 2834f5e7dd7Smrg if (priv->rom_base == 0) { 2844f5e7dd7Smrg#if defined(__amd64__) || defined(__i386__) 2854f5e7dd7Smrg rom_base = 0xc0000; 2864f5e7dd7Smrg pci_rom = 0; 2874f5e7dd7Smrg#else 2884f5e7dd7Smrg return ENOSYS; 2894f5e7dd7Smrg#endif 2904f5e7dd7Smrg } else { 2914f5e7dd7Smrg rom_base = priv->rom_base; 2924f5e7dd7Smrg pci_rom = 1; 2934f5e7dd7Smrg 2944f5e7dd7Smrg pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); 2954f5e7dd7Smrg pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); 2964f5e7dd7Smrg pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); 2974f5e7dd7Smrg pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); 2984f5e7dd7Smrg } 2994f5e7dd7Smrg 3004f5e7dd7Smrg printf("Using rom_base = 0x%lx\n", (long)rom_base); 301cad31331Smrg memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); 3024f5e7dd7Smrg if ( memfd == -1 ) 3034f5e7dd7Smrg return errno; 3044f5e7dd7Smrg 3054f5e7dd7Smrg bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); 3064f5e7dd7Smrg if ( bios == MAP_FAILED ) { 3074f5e7dd7Smrg close( memfd ); 3084f5e7dd7Smrg return errno; 3094f5e7dd7Smrg } 3104f5e7dd7Smrg 3114f5e7dd7Smrg memcpy( buffer, bios, dev->rom_size ); 3124f5e7dd7Smrg 3134f5e7dd7Smrg munmap( bios, dev->rom_size ); 3144f5e7dd7Smrg close( memfd ); 3154f5e7dd7Smrg 3164f5e7dd7Smrg if (pci_rom) { 3174f5e7dd7Smrg pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); 3184f5e7dd7Smrg pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); 3194f5e7dd7Smrg } 3204f5e7dd7Smrg 3214f5e7dd7Smrg return 0; 3224f5e7dd7Smrg} 3234f5e7dd7Smrg 3244f5e7dd7Smrg/** Returns the number of regions (base address registers) the device has */ 3254f5e7dd7Smrg 3264f5e7dd7Smrgstatic int 3274f5e7dd7Smrgpci_device_freebsd_get_num_regions( struct pci_device * dev ) 3284f5e7dd7Smrg{ 3294f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 3304f5e7dd7Smrg 3314f5e7dd7Smrg switch (priv->header_type) { 3324f5e7dd7Smrg case 0: 3334f5e7dd7Smrg return 6; 3344f5e7dd7Smrg case 1: 3354f5e7dd7Smrg return 2; 3364f5e7dd7Smrg case 2: 3374f5e7dd7Smrg return 1; 3384f5e7dd7Smrg default: 3394f5e7dd7Smrg printf("unknown header type %02x\n", priv->header_type); 3404f5e7dd7Smrg return 0; 3414f5e7dd7Smrg } 3424f5e7dd7Smrg} 3434f5e7dd7Smrg 3444f5e7dd7Smrg#ifdef PCIOCGETBAR 3454f5e7dd7Smrg 3464f5e7dd7Smrgstatic int 3474f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev ) 3484f5e7dd7Smrg{ 3494f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 3504f5e7dd7Smrg struct pci_bar_io bar; 3514f5e7dd7Smrg uint8_t irq; 3524f5e7dd7Smrg int err, i; 3534f5e7dd7Smrg 3544f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 3554f5e7dd7Smrg bar.pbi_sel.pc_domain = dev->domain; 3564f5e7dd7Smrg#endif 3574f5e7dd7Smrg bar.pbi_sel.pc_bus = dev->bus; 3584f5e7dd7Smrg bar.pbi_sel.pc_dev = dev->dev; 3594f5e7dd7Smrg bar.pbi_sel.pc_func = dev->func; 3604f5e7dd7Smrg 3614f5e7dd7Smrg 3624f5e7dd7Smrg /* Many of the fields were filled in during initial device enumeration. 3634f5e7dd7Smrg * At this point, we need to fill in regions, rom_size, and irq. 3644f5e7dd7Smrg */ 3654f5e7dd7Smrg 3664f5e7dd7Smrg err = pci_device_cfg_read_u8( dev, &irq, 60 ); 3674f5e7dd7Smrg if (err) 3684f5e7dd7Smrg return errno; 3694f5e7dd7Smrg dev->irq = irq; 3704f5e7dd7Smrg 3714f5e7dd7Smrg for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 3724f5e7dd7Smrg bar.pbi_reg = PCIR_BAR(i); 373cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) 3744f5e7dd7Smrg continue; 3754f5e7dd7Smrg 3764f5e7dd7Smrg if (PCI_BAR_IO(bar.pbi_base)) 3774f5e7dd7Smrg dev->regions[i].is_IO = 1; 3784f5e7dd7Smrg 3794f5e7dd7Smrg if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) 3804f5e7dd7Smrg dev->regions[i].is_64 = 1; 3814f5e7dd7Smrg 3824f5e7dd7Smrg if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 3834f5e7dd7Smrg dev->regions[i].is_prefetchable = 1; 3844f5e7dd7Smrg 3854f5e7dd7Smrg dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); 3864f5e7dd7Smrg dev->regions[i].size = bar.pbi_length; 3874f5e7dd7Smrg } 3884f5e7dd7Smrg 3894f5e7dd7Smrg /* If it's a VGA device, set up the rom size for read_rom using the 3904f5e7dd7Smrg * 0xc0000 mapping. 3914f5e7dd7Smrg */ 3924f5e7dd7Smrg if ((dev->device_class & 0x00ffff00) == 3934f5e7dd7Smrg ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { 3944f5e7dd7Smrg dev->rom_size = 64 * 1024; 3954f5e7dd7Smrg } 3964f5e7dd7Smrg 3974f5e7dd7Smrg return 0; 3984f5e7dd7Smrg} 3994f5e7dd7Smrg 4004f5e7dd7Smrg#else 4014f5e7dd7Smrg 4024f5e7dd7Smrg/** Masks out the flag bigs of the base address register value */ 4034f5e7dd7Smrgstatic uint32_t 4044f5e7dd7Smrgget_map_base( uint32_t val ) 4054f5e7dd7Smrg{ 4064f5e7dd7Smrg if (val & 0x01) 4074f5e7dd7Smrg return val & ~0x03; 4084f5e7dd7Smrg else 4094f5e7dd7Smrg return val & ~0x0f; 4104f5e7dd7Smrg} 4114f5e7dd7Smrg 4124f5e7dd7Smrg/** Returns the size of a region based on the all-ones test value */ 4134f5e7dd7Smrgstatic int 4144f5e7dd7Smrgget_test_val_size( uint32_t testval ) 4154f5e7dd7Smrg{ 4164f5e7dd7Smrg if (testval == 0) 4174f5e7dd7Smrg return 0; 4184f5e7dd7Smrg 4194f5e7dd7Smrg /* Mask out the flag bits */ 4204f5e7dd7Smrg testval = get_map_base( testval ); 4214f5e7dd7Smrg 4224f5e7dd7Smrg return 1 << (ffs(testval) - 1); 4234f5e7dd7Smrg} 4244f5e7dd7Smrg 4254f5e7dd7Smrg/** 4264f5e7dd7Smrg * Sets the address and size information for the region from config space 4274f5e7dd7Smrg * registers. 4284f5e7dd7Smrg * 4294f5e7dd7Smrg * This would be much better provided by a kernel interface. 4304f5e7dd7Smrg * 4314f5e7dd7Smrg * \return 0 on success, or an errno value. 4324f5e7dd7Smrg */ 4334f5e7dd7Smrgstatic int 4344f5e7dd7Smrgpci_device_freebsd_get_region_info( struct pci_device * dev, int region, 4354f5e7dd7Smrg int bar ) 4364f5e7dd7Smrg{ 4374f5e7dd7Smrg uint32_t addr, testval; 4384f5e7dd7Smrg uint16_t cmd; 4394f5e7dd7Smrg int err; 4404f5e7dd7Smrg 4414f5e7dd7Smrg /* Get the base address */ 4424f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &addr, bar ); 4434f5e7dd7Smrg if (err != 0) 4444f5e7dd7Smrg return err; 4454f5e7dd7Smrg 4464f5e7dd7Smrg /* 4474f5e7dd7Smrg * We are going to be doing evil things to the registers here 448cad31331Smrg * so disable them via the command register first. 4494f5e7dd7Smrg */ 4504f5e7dd7Smrg err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND ); 4514f5e7dd7Smrg if (err != 0) 4524f5e7dd7Smrg return err; 4534f5e7dd7Smrg 4544f5e7dd7Smrg err = pci_device_cfg_write_u16( dev, 4554f5e7dd7Smrg cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 4564f5e7dd7Smrg PCIR_COMMAND ); 4574f5e7dd7Smrg if (err != 0) 4584f5e7dd7Smrg return err; 4594f5e7dd7Smrg 4604f5e7dd7Smrg /* Test write all ones to the register, then restore it. */ 4614f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, 0xffffffff, bar ); 4624f5e7dd7Smrg if (err != 0) 4634f5e7dd7Smrg return err; 4644f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &testval, bar ); 4654f5e7dd7Smrg if (err != 0) 4664f5e7dd7Smrg return err; 4674f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, addr, bar ); 4684f5e7dd7Smrg if (err != 0) 4694f5e7dd7Smrg return err; 4704f5e7dd7Smrg 4714f5e7dd7Smrg /* Restore the command register */ 4724f5e7dd7Smrg err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND ); 4734f5e7dd7Smrg if (err != 0) 4744f5e7dd7Smrg return err; 4754f5e7dd7Smrg 4764f5e7dd7Smrg if (addr & 0x01) 4774f5e7dd7Smrg dev->regions[region].is_IO = 1; 4784f5e7dd7Smrg if (addr & 0x04) 4794f5e7dd7Smrg dev->regions[region].is_64 = 1; 4804f5e7dd7Smrg if (addr & 0x08) 4814f5e7dd7Smrg dev->regions[region].is_prefetchable = 1; 4824f5e7dd7Smrg 4834f5e7dd7Smrg /* Set the size */ 4844f5e7dd7Smrg dev->regions[region].size = get_test_val_size( testval ); 4854f5e7dd7Smrg printf("size = 0x%lx\n", (long)dev->regions[region].size); 4864f5e7dd7Smrg 4874f5e7dd7Smrg /* Set the base address value */ 4884f5e7dd7Smrg if (dev->regions[region].is_64) { 4894f5e7dd7Smrg uint32_t top; 4904f5e7dd7Smrg 4914f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &top, bar + 4 ); 4924f5e7dd7Smrg if (err != 0) 4934f5e7dd7Smrg return err; 4944f5e7dd7Smrg 4954f5e7dd7Smrg dev->regions[region].base_addr = ((uint64_t)top << 32) | 4964f5e7dd7Smrg get_map_base(addr); 4974f5e7dd7Smrg } else { 4984f5e7dd7Smrg dev->regions[region].base_addr = get_map_base(addr); 4994f5e7dd7Smrg } 5004f5e7dd7Smrg 5014f5e7dd7Smrg return 0; 5024f5e7dd7Smrg} 5034f5e7dd7Smrg 5044f5e7dd7Smrgstatic int 5054f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev ) 5064f5e7dd7Smrg{ 5074f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 5084f5e7dd7Smrg uint32_t reg, size; 5094f5e7dd7Smrg uint8_t irq; 5104f5e7dd7Smrg int err, i, bar; 5114f5e7dd7Smrg 5124f5e7dd7Smrg /* Many of the fields were filled in during initial device enumeration. 5134f5e7dd7Smrg * At this point, we need to fill in regions, rom_size, and irq. 5144f5e7dd7Smrg */ 5154f5e7dd7Smrg 5164f5e7dd7Smrg err = pci_device_cfg_read_u8( dev, &irq, 60 ); 5174f5e7dd7Smrg if (err) 5184f5e7dd7Smrg return errno; 5194f5e7dd7Smrg dev->irq = irq; 5204f5e7dd7Smrg 5214f5e7dd7Smrg bar = 0x10; 5224f5e7dd7Smrg for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 5234f5e7dd7Smrg pci_device_freebsd_get_region_info( dev, i, bar ); 5244f5e7dd7Smrg if (dev->regions[i].is_64) { 5254f5e7dd7Smrg bar += 0x08; 5264f5e7dd7Smrg i++; 5274f5e7dd7Smrg } else 5284f5e7dd7Smrg bar += 0x04; 5294f5e7dd7Smrg } 5304f5e7dd7Smrg 5314f5e7dd7Smrg /* If it's a VGA device, set up the rom size for read_rom */ 5324f5e7dd7Smrg if ((dev->device_class & 0x00ffff00) == 5334f5e7dd7Smrg ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) 5344f5e7dd7Smrg { 5354f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, ®, PCIR_BIOS ); 5364f5e7dd7Smrg if (err) 5374f5e7dd7Smrg return errno; 5384f5e7dd7Smrg 5394f5e7dd7Smrg if (reg == 0) { 5404f5e7dd7Smrg dev->rom_size = 0x10000; 5414f5e7dd7Smrg return 0; 5424f5e7dd7Smrg } 5434f5e7dd7Smrg 5444f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS ); 5454f5e7dd7Smrg if (err) 5464f5e7dd7Smrg return errno; 5474f5e7dd7Smrg pci_device_cfg_read_u32( dev, &size, PCIR_BIOS ); 5484f5e7dd7Smrg pci_device_cfg_write_u32( dev, reg, PCIR_BIOS ); 5494f5e7dd7Smrg 5504f5e7dd7Smrg if ((reg & PCIM_BIOS_ADDR_MASK) != 0) { 5514f5e7dd7Smrg priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK); 5524f5e7dd7Smrg dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK); 5534f5e7dd7Smrg } 5544f5e7dd7Smrg } 5554f5e7dd7Smrg 5564f5e7dd7Smrg return 0; 5574f5e7dd7Smrg} 5584f5e7dd7Smrg 5594f5e7dd7Smrg#endif 5604f5e7dd7Smrg 5614f5e7dd7Smrgstatic void 5624f5e7dd7Smrgpci_system_freebsd_destroy(void) 5634f5e7dd7Smrg{ 5644f5e7dd7Smrg close(freebsd_pci_sys->pcidev); 5654f5e7dd7Smrg free(freebsd_pci_sys->pci_sys.devices); 5664f5e7dd7Smrg freebsd_pci_sys = NULL; 5674f5e7dd7Smrg} 5684f5e7dd7Smrg 56986ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 57086ea1d58Smrg#include <machine/cpufunc.h> 57186ea1d58Smrg#endif 57286ea1d58Smrg 57386ea1d58Smrgstatic struct pci_io_handle * 57486ea1d58Smrgpci_device_freebsd_open_legacy_io(struct pci_io_handle *ret, 57586ea1d58Smrg struct pci_device *dev, pciaddr_t base, pciaddr_t size) 57686ea1d58Smrg{ 57786ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 57886ea1d58Smrg ret->fd = open("/dev/io", O_RDWR | O_CLOEXEC); 57986ea1d58Smrg 58086ea1d58Smrg if (ret->fd < 0) 58186ea1d58Smrg return NULL; 58286ea1d58Smrg 58386ea1d58Smrg ret->base = base; 58486ea1d58Smrg ret->size = size; 5856a94483fSmrg ret->is_legacy = 1; 58686ea1d58Smrg return ret; 58786ea1d58Smrg#elif defined(PCI_MAGIC_IO_RANGE) 58886ea1d58Smrg ret->memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, 58986ea1d58Smrg aperturefd, PCI_MAGIC_IO_RANGE + base); 59086ea1d58Smrg if (ret->memory == MAP_FAILED) 59186ea1d58Smrg return NULL; 59286ea1d58Smrg 59386ea1d58Smrg ret->base = base; 59486ea1d58Smrg ret->size = size; 5956a94483fSmrg ret->is_legacy = 1; 59686ea1d58Smrg return ret; 59786ea1d58Smrg#else 59886ea1d58Smrg return NULL; 59986ea1d58Smrg#endif 60086ea1d58Smrg} 60186ea1d58Smrg 60286ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 60386ea1d58Smrgstatic void 60486ea1d58Smrgpci_device_freebsd_close_io(struct pci_device *dev, struct pci_io_handle *handle) 60586ea1d58Smrg{ 60686ea1d58Smrg if (handle->fd > -1) 60786ea1d58Smrg close(handle->fd); 60886ea1d58Smrg} 60986ea1d58Smrg#endif 61086ea1d58Smrg 61186ea1d58Smrgstatic uint32_t 61286ea1d58Smrgpci_device_freebsd_read32(struct pci_io_handle *handle, uint32_t reg) 61386ea1d58Smrg{ 61486ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 61586ea1d58Smrg return inl(handle->base + reg); 61686ea1d58Smrg#else 61786ea1d58Smrg return *(uint32_t *)((uintptr_t)handle->memory + reg); 61886ea1d58Smrg#endif 61986ea1d58Smrg} 62086ea1d58Smrg 62186ea1d58Smrgstatic uint16_t 62286ea1d58Smrgpci_device_freebsd_read16(struct pci_io_handle *handle, uint32_t reg) 62386ea1d58Smrg{ 62486ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 62586ea1d58Smrg return inw(handle->base + reg); 62686ea1d58Smrg#else 62786ea1d58Smrg return *(uint16_t *)((uintptr_t)handle->memory + reg); 62886ea1d58Smrg#endif 62986ea1d58Smrg} 63086ea1d58Smrg 63186ea1d58Smrgstatic uint8_t 63286ea1d58Smrgpci_device_freebsd_read8(struct pci_io_handle *handle, uint32_t reg) 63386ea1d58Smrg{ 63486ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 63586ea1d58Smrg return inb(handle->base + reg); 63686ea1d58Smrg#else 63786ea1d58Smrg return *(uint8_t *)((uintptr_t)handle->memory + reg); 63886ea1d58Smrg#endif 63986ea1d58Smrg} 64086ea1d58Smrg 64186ea1d58Smrgstatic void 64286ea1d58Smrgpci_device_freebsd_write32(struct pci_io_handle *handle, uint32_t reg, 64386ea1d58Smrg uint32_t data) 64486ea1d58Smrg{ 64586ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 64686ea1d58Smrg outl(handle->base + reg, data); 64786ea1d58Smrg#else 64886ea1d58Smrg *(uint16_t *)((uintptr_t)handle->memory + reg) = data; 64986ea1d58Smrg#endif 65086ea1d58Smrg} 65186ea1d58Smrg 65286ea1d58Smrgstatic void 65386ea1d58Smrgpci_device_freebsd_write16(struct pci_io_handle *handle, uint32_t reg, 65486ea1d58Smrg uint16_t data) 65586ea1d58Smrg{ 65686ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 65786ea1d58Smrg outw(handle->base + reg, data); 65886ea1d58Smrg#else 65986ea1d58Smrg *(uint8_t *)((uintptr_t)handle->memory + reg) = data; 66086ea1d58Smrg#endif 66186ea1d58Smrg} 66286ea1d58Smrg 66386ea1d58Smrgstatic void 66486ea1d58Smrgpci_device_freebsd_write8(struct pci_io_handle *handle, uint32_t reg, 66586ea1d58Smrg uint8_t data) 66686ea1d58Smrg{ 66786ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 66886ea1d58Smrg outb(handle->base + reg, data); 66986ea1d58Smrg#else 67086ea1d58Smrg *(uint32_t *)((uintptr_t)handle->memory + reg) = data; 67186ea1d58Smrg#endif 67286ea1d58Smrg} 67386ea1d58Smrg 67486ea1d58Smrgstatic int 67586ea1d58Smrgpci_device_freebsd_map_legacy(struct pci_device *dev, pciaddr_t base, 67686ea1d58Smrg pciaddr_t size, unsigned map_flags, void **addr) 67786ea1d58Smrg{ 67886ea1d58Smrg struct pci_device_mapping map; 67986ea1d58Smrg int err; 68086ea1d58Smrg 68186ea1d58Smrg map.base = base; 68286ea1d58Smrg map.size = size; 68386ea1d58Smrg map.flags = map_flags; 68486ea1d58Smrg map.memory = NULL; 68586ea1d58Smrg err = pci_device_freebsd_map_range(dev, &map); 68686ea1d58Smrg *addr = map.memory; 68786ea1d58Smrg 68886ea1d58Smrg return err; 68986ea1d58Smrg} 69086ea1d58Smrg 69186ea1d58Smrgstatic int 69286ea1d58Smrgpci_device_freebsd_unmap_legacy(struct pci_device *dev, void *addr, 69386ea1d58Smrg pciaddr_t size) 69486ea1d58Smrg{ 69586ea1d58Smrg struct pci_device_mapping map; 69686ea1d58Smrg 69786ea1d58Smrg map.memory = addr; 69886ea1d58Smrg map.size = size; 69986ea1d58Smrg map.flags = 0; 70086ea1d58Smrg return pci_device_freebsd_unmap_range(dev, &map); 70186ea1d58Smrg} 70286ea1d58Smrg 7034f5e7dd7Smrgstatic const struct pci_system_methods freebsd_pci_methods = { 7044f5e7dd7Smrg .destroy = pci_system_freebsd_destroy, 7054f5e7dd7Smrg .destroy_device = NULL, /* nothing to do for this */ 7064f5e7dd7Smrg .read_rom = pci_device_freebsd_read_rom, 7074f5e7dd7Smrg .probe = pci_device_freebsd_probe, 7084f5e7dd7Smrg .map_range = pci_device_freebsd_map_range, 7094f5e7dd7Smrg .unmap_range = pci_device_freebsd_unmap_range, 7104f5e7dd7Smrg .read = pci_device_freebsd_read, 7114f5e7dd7Smrg .write = pci_device_freebsd_write, 7124f5e7dd7Smrg .fill_capabilities = pci_fill_capabilities_generic, 71386ea1d58Smrg .open_legacy_io = pci_device_freebsd_open_legacy_io, 71486ea1d58Smrg#if defined(__i386__) || defined(__amd64__) 71586ea1d58Smrg .close_io = pci_device_freebsd_close_io, 71686ea1d58Smrg#endif 71786ea1d58Smrg .read32 = pci_device_freebsd_read32, 71886ea1d58Smrg .read16 = pci_device_freebsd_read16, 71986ea1d58Smrg .read8 = pci_device_freebsd_read8, 72086ea1d58Smrg .write32 = pci_device_freebsd_write32, 72186ea1d58Smrg .write16 = pci_device_freebsd_write16, 72286ea1d58Smrg .write8 = pci_device_freebsd_write8, 72386ea1d58Smrg .map_legacy = pci_device_freebsd_map_legacy, 72486ea1d58Smrg .unmap_legacy = pci_device_freebsd_unmap_legacy, 7254f5e7dd7Smrg}; 7264f5e7dd7Smrg 7274f5e7dd7Smrg/** 7284f5e7dd7Smrg * Attempt to access the FreeBSD PCI interface. 7294f5e7dd7Smrg */ 7304f5e7dd7Smrg_pci_hidden int 7314f5e7dd7Smrgpci_system_freebsd_create( void ) 7324f5e7dd7Smrg{ 7334f5e7dd7Smrg struct pci_conf_io pciconfio; 7344f5e7dd7Smrg struct pci_conf pciconf[255]; 7354f5e7dd7Smrg int pcidev; 7364f5e7dd7Smrg int i; 7374f5e7dd7Smrg 7384f5e7dd7Smrg /* Try to open the PCI device */ 739cad31331Smrg pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); 7404f5e7dd7Smrg if ( pcidev == -1 ) 7414f5e7dd7Smrg return ENXIO; 7424f5e7dd7Smrg 7434f5e7dd7Smrg freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) ); 7444f5e7dd7Smrg if ( freebsd_pci_sys == NULL ) { 7454f5e7dd7Smrg close( pcidev ); 7464f5e7dd7Smrg return ENOMEM; 7474f5e7dd7Smrg } 7484f5e7dd7Smrg pci_sys = &freebsd_pci_sys->pci_sys; 7494f5e7dd7Smrg 7504f5e7dd7Smrg pci_sys->methods = & freebsd_pci_methods; 7514f5e7dd7Smrg freebsd_pci_sys->pcidev = pcidev; 7524f5e7dd7Smrg 7534f5e7dd7Smrg /* Probe the list of devices known by the system */ 7544f5e7dd7Smrg bzero( &pciconfio, sizeof( struct pci_conf_io ) ); 7554f5e7dd7Smrg pciconfio.match_buf_len = sizeof(pciconf); 7564f5e7dd7Smrg pciconfio.matches = pciconf; 7574f5e7dd7Smrg 7584f5e7dd7Smrg if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) { 7594f5e7dd7Smrg free( pci_sys ); 7604f5e7dd7Smrg close( pcidev ); 7614f5e7dd7Smrg return errno; 7624f5e7dd7Smrg } 7634f5e7dd7Smrg 7644f5e7dd7Smrg if (pciconfio.status == PCI_GETCONF_ERROR ) { 7654f5e7dd7Smrg free( pci_sys ); 7664f5e7dd7Smrg close( pcidev ); 7674f5e7dd7Smrg return EINVAL; 7684f5e7dd7Smrg } 7694f5e7dd7Smrg 7704f5e7dd7Smrg /* Translate the list of devices into pciaccess's format. */ 7714f5e7dd7Smrg pci_sys->num_devices = pciconfio.num_matches; 7724f5e7dd7Smrg pci_sys->devices = calloc( pciconfio.num_matches, 7734f5e7dd7Smrg sizeof( struct pci_device_private ) ); 7744f5e7dd7Smrg 7754f5e7dd7Smrg for ( i = 0; i < pciconfio.num_matches; i++ ) { 7764f5e7dd7Smrg struct pci_conf *p = &pciconf[ i ]; 7774f5e7dd7Smrg 7784f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 7794f5e7dd7Smrg pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; 7804f5e7dd7Smrg#else 7814f5e7dd7Smrg pci_sys->devices[ i ].base.domain = 0; 7824f5e7dd7Smrg#endif 7834f5e7dd7Smrg pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; 7844f5e7dd7Smrg pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; 7854f5e7dd7Smrg pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; 7864f5e7dd7Smrg pci_sys->devices[ i ].base.vendor_id = p->pc_vendor; 7874f5e7dd7Smrg pci_sys->devices[ i ].base.device_id = p->pc_device; 7884f5e7dd7Smrg pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; 7894f5e7dd7Smrg pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; 7904f5e7dd7Smrg pci_sys->devices[ i ].base.revision = p->pc_revid; 7914f5e7dd7Smrg pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | 7924f5e7dd7Smrg (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; 7934f5e7dd7Smrg pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; 7944f5e7dd7Smrg } 7954f5e7dd7Smrg 7964f5e7dd7Smrg return 0; 7974f5e7dd7Smrg} 798