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> 462029f493Smrg#if defined(__i386__) || defined(__amd64__) 472029f493Smrg#include <machine/cpufunc.h> 482029f493Smrg#else 492029f493Smrg#include <dev/io/iodev.h> 502029f493Smrg#endif 514f5e7dd7Smrg#include <sys/types.h> 524f5e7dd7Smrg#include <sys/param.h> 534f5e7dd7Smrg#include <sys/pciio.h> 544f5e7dd7Smrg#include <sys/mman.h> 554f5e7dd7Smrg#include <sys/memrange.h> 564f5e7dd7Smrg 574f5e7dd7Smrg#include "pciaccess.h" 584f5e7dd7Smrg#include "pciaccess_private.h" 594f5e7dd7Smrg 602029f493Smrg#define PCIC_DISPLAY 0x03 614f5e7dd7Smrg#define PCIS_DISPLAY_VGA 0x00 624f5e7dd7Smrg#define PCIS_DISPLAY_XGA 0x01 634f5e7dd7Smrg#define PCIS_DISPLAY_3D 0x02 644f5e7dd7Smrg#define PCIS_DISPLAY_OTHER 0x80 654f5e7dd7Smrg 664f5e7dd7Smrg/* Registers taken from pcireg.h */ 672029f493Smrg#define PCIR_COMMAND 0x04 682029f493Smrg#define PCIM_CMD_PORTEN 0x0001 692029f493Smrg#define PCIM_CMD_MEMEN 0x0002 702029f493Smrg#define PCIR_BIOS 0x30 714f5e7dd7Smrg#define PCIM_BIOS_ENABLE 0x01 724f5e7dd7Smrg#define PCIM_BIOS_ADDR_MASK 0xfffff800 734f5e7dd7Smrg 742029f493Smrg#define PCIR_BARS 0x10 752029f493Smrg#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) 762029f493Smrg#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) 772029f493Smrg#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) 782029f493Smrg#define PCIM_BAR_MEM_TYPE 0x00000006 792029f493Smrg#define PCIM_BAR_MEM_64 4 802029f493Smrg#define PCIM_BAR_MEM_PREFETCH 0x00000008 812029f493Smrg#define PCIM_BAR_SPACE 0x00000001 822029f493Smrg#define PCIM_BAR_MEM_SPACE 0 832029f493Smrg#define PCIM_BAR_IO_SPACE 1 842029f493Smrg 854f5e7dd7Smrg/** 864f5e7dd7Smrg * FreeBSD private pci_system structure that extends the base pci_system 874f5e7dd7Smrg * structure. 884f5e7dd7Smrg * 894f5e7dd7Smrg * It is initialized once and used as a global, just as pci_system is used. 904f5e7dd7Smrg */ 914f5e7dd7Smrg_pci_hidden 924f5e7dd7Smrgstruct freebsd_pci_system { 934f5e7dd7Smrg /* This must be the first entry in the structure, as pci_system_cleanup() 944f5e7dd7Smrg * frees pci_sys. 954f5e7dd7Smrg */ 964f5e7dd7Smrg struct pci_system pci_sys; 974f5e7dd7Smrg 984f5e7dd7Smrg int pcidev; /**< fd for /dev/pci */ 994f5e7dd7Smrg} *freebsd_pci_sys; 1004f5e7dd7Smrg 1014f5e7dd7Smrg/** 1024f5e7dd7Smrg * Map a memory region for a device using /dev/mem. 103cad31331Smrg * 1044f5e7dd7Smrg * \param dev Device whose memory region is to be mapped. 1054f5e7dd7Smrg * \param map Parameters of the mapping that is to be created. 106cad31331Smrg * 1074f5e7dd7Smrg * \return 1084f5e7dd7Smrg * Zero on success or an \c errno value on failure. 1094f5e7dd7Smrg */ 1104f5e7dd7Smrgstatic int 1112029f493Smrgpci_device_freebsd_map_range( struct pci_device *dev, 1122029f493Smrg struct pci_device_mapping *map ) 1134f5e7dd7Smrg{ 114cad31331Smrg const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 1154f5e7dd7Smrg ? (PROT_READ | PROT_WRITE) : PROT_READ; 1164f5e7dd7Smrg struct mem_range_desc mrd; 1174f5e7dd7Smrg struct mem_range_op mro; 1184f5e7dd7Smrg 1194f5e7dd7Smrg int fd, err = 0; 1204f5e7dd7Smrg 121cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1224f5e7dd7Smrg if (fd == -1) 1234f5e7dd7Smrg return errno; 1244f5e7dd7Smrg 1254f5e7dd7Smrg map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); 1264f5e7dd7Smrg 1274f5e7dd7Smrg if (map->memory == MAP_FAILED) { 1284f5e7dd7Smrg err = errno; 1294f5e7dd7Smrg } 1304f5e7dd7Smrg 1314f5e7dd7Smrg mrd.mr_base = map->base; 1324f5e7dd7Smrg mrd.mr_len = map->size; 1334f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1344f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 1354f5e7dd7Smrg mrd.mr_flags = MDF_WRITEBACK; 1364f5e7dd7Smrg else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 1374f5e7dd7Smrg mrd.mr_flags = MDF_WRITECOMBINE; 1384f5e7dd7Smrg else 1394f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1404f5e7dd7Smrg mro.mo_desc = &mrd; 1414f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_UPDATE; 1424f5e7dd7Smrg 1434f5e7dd7Smrg /* No need to set an MTRR if it's the default mode. */ 1444f5e7dd7Smrg if (mrd.mr_flags != MDF_UNCACHEABLE) { 1454f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1464f5e7dd7Smrg fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno)); 1474f5e7dd7Smrg } 1484f5e7dd7Smrg } 1494f5e7dd7Smrg 1504f5e7dd7Smrg close(fd); 1514f5e7dd7Smrg 1524f5e7dd7Smrg return err; 1534f5e7dd7Smrg} 1544f5e7dd7Smrg 1554f5e7dd7Smrgstatic int 1564f5e7dd7Smrgpci_device_freebsd_unmap_range( struct pci_device *dev, 1574f5e7dd7Smrg struct pci_device_mapping *map ) 1584f5e7dd7Smrg{ 1594f5e7dd7Smrg struct mem_range_desc mrd; 1604f5e7dd7Smrg struct mem_range_op mro; 1614f5e7dd7Smrg int fd; 1624f5e7dd7Smrg 1634f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1644f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) 1654f5e7dd7Smrg { 166cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1674f5e7dd7Smrg if (fd != -1) { 1684f5e7dd7Smrg mrd.mr_base = map->base; 1694f5e7dd7Smrg mrd.mr_len = map->size; 1704f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1714f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1724f5e7dd7Smrg mro.mo_desc = &mrd; 1734f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_REMOVE; 1744f5e7dd7Smrg 1754f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1764f5e7dd7Smrg fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); 1774f5e7dd7Smrg } 1784f5e7dd7Smrg 1794f5e7dd7Smrg close(fd); 1804f5e7dd7Smrg } else { 1814f5e7dd7Smrg fprintf(stderr, "Failed to open /dev/mem\n"); 1824f5e7dd7Smrg } 1834f5e7dd7Smrg } 1844f5e7dd7Smrg 1854f5e7dd7Smrg return pci_device_generic_unmap_range(dev, map); 1864f5e7dd7Smrg} 1874f5e7dd7Smrg 1884f5e7dd7Smrgstatic int 1894f5e7dd7Smrgpci_device_freebsd_read( struct pci_device * dev, void * data, 1904f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 1914f5e7dd7Smrg pciaddr_t * bytes_read ) 1924f5e7dd7Smrg{ 1934f5e7dd7Smrg struct pci_io io; 1944f5e7dd7Smrg 1954f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 1964f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 1974f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 1984f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 1994f5e7dd7Smrg 2004f5e7dd7Smrg *bytes_read = 0; 2014f5e7dd7Smrg while ( size > 0 ) { 2024f5e7dd7Smrg int toread = (size < 4) ? size : 4; 2034f5e7dd7Smrg 2044f5e7dd7Smrg /* Only power of two allowed. */ 2054f5e7dd7Smrg if (toread == 3) 2064f5e7dd7Smrg toread = 2; 2074f5e7dd7Smrg 2084f5e7dd7Smrg io.pi_reg = offset; 2094f5e7dd7Smrg io.pi_width = toread; 2104f5e7dd7Smrg 211cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) 2124f5e7dd7Smrg return errno; 2134f5e7dd7Smrg 2144f5e7dd7Smrg memcpy(data, &io.pi_data, toread ); 2154f5e7dd7Smrg 2164f5e7dd7Smrg offset += toread; 2174f5e7dd7Smrg data = (char *)data + toread; 2184f5e7dd7Smrg size -= toread; 2194f5e7dd7Smrg *bytes_read += toread; 2204f5e7dd7Smrg } 2214f5e7dd7Smrg 2224f5e7dd7Smrg return 0; 2234f5e7dd7Smrg} 2244f5e7dd7Smrg 2254f5e7dd7Smrg 2264f5e7dd7Smrgstatic int 2274f5e7dd7Smrgpci_device_freebsd_write( struct pci_device * dev, const void * data, 2284f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 2294f5e7dd7Smrg pciaddr_t * bytes_written ) 2304f5e7dd7Smrg{ 2314f5e7dd7Smrg struct pci_io io; 2324f5e7dd7Smrg 2334f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 2344f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 2354f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 2364f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 2374f5e7dd7Smrg 2384f5e7dd7Smrg *bytes_written = 0; 2394f5e7dd7Smrg while ( size > 0 ) { 2404f5e7dd7Smrg int towrite = (size < 4 ? size : 4); 2414f5e7dd7Smrg 2424f5e7dd7Smrg /* Only power of two allowed. */ 2434f5e7dd7Smrg if (towrite == 3) 2444f5e7dd7Smrg towrite = 2; 2454f5e7dd7Smrg 2464f5e7dd7Smrg io.pi_reg = offset; 2474f5e7dd7Smrg io.pi_width = towrite; 2484f5e7dd7Smrg memcpy( &io.pi_data, data, towrite ); 2494f5e7dd7Smrg 250cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) 2514f5e7dd7Smrg return errno; 2524f5e7dd7Smrg 2534f5e7dd7Smrg offset += towrite; 2544f5e7dd7Smrg data = (char *)data + towrite; 2554f5e7dd7Smrg size -= towrite; 2564f5e7dd7Smrg *bytes_written += towrite; 2574f5e7dd7Smrg } 2584f5e7dd7Smrg 2594f5e7dd7Smrg return 0; 2604f5e7dd7Smrg} 2614f5e7dd7Smrg 2624f5e7dd7Smrg/** 2634f5e7dd7Smrg * Read a VGA rom using the 0xc0000 mapping. 2644f5e7dd7Smrg * 2654f5e7dd7Smrg * This function should be extended to handle access through PCI resources, 2664f5e7dd7Smrg * which should be more reliable when available. 2674f5e7dd7Smrg */ 2684f5e7dd7Smrgstatic int 2694f5e7dd7Smrgpci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) 2704f5e7dd7Smrg{ 2714f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 2724f5e7dd7Smrg void *bios; 2734f5e7dd7Smrg pciaddr_t rom_base; 2744f5e7dd7Smrg uint32_t rom; 2754f5e7dd7Smrg uint16_t reg; 2764f5e7dd7Smrg int pci_rom, memfd; 2774f5e7dd7Smrg 2784f5e7dd7Smrg if ( ( dev->device_class & 0x00ffff00 ) != 2794f5e7dd7Smrg ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) 2804f5e7dd7Smrg { 2814f5e7dd7Smrg return ENOSYS; 2824f5e7dd7Smrg } 2834f5e7dd7Smrg 2844f5e7dd7Smrg if (priv->rom_base == 0) { 2854f5e7dd7Smrg#if defined(__amd64__) || defined(__i386__) 2864f5e7dd7Smrg rom_base = 0xc0000; 2874f5e7dd7Smrg pci_rom = 0; 2884f5e7dd7Smrg#else 2894f5e7dd7Smrg return ENOSYS; 2904f5e7dd7Smrg#endif 2914f5e7dd7Smrg } else { 2924f5e7dd7Smrg rom_base = priv->rom_base; 2934f5e7dd7Smrg pci_rom = 1; 2944f5e7dd7Smrg 2954f5e7dd7Smrg pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); 2964f5e7dd7Smrg pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); 2974f5e7dd7Smrg pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); 2984f5e7dd7Smrg pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); 2994f5e7dd7Smrg } 3004f5e7dd7Smrg 3014f5e7dd7Smrg printf("Using rom_base = 0x%lx\n", (long)rom_base); 302cad31331Smrg memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); 3034f5e7dd7Smrg if ( memfd == -1 ) 3044f5e7dd7Smrg return errno; 3054f5e7dd7Smrg 3064f5e7dd7Smrg bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); 3074f5e7dd7Smrg if ( bios == MAP_FAILED ) { 3084f5e7dd7Smrg close( memfd ); 3094f5e7dd7Smrg return errno; 3104f5e7dd7Smrg } 3114f5e7dd7Smrg 3124f5e7dd7Smrg memcpy( buffer, bios, dev->rom_size ); 3134f5e7dd7Smrg 3144f5e7dd7Smrg munmap( bios, dev->rom_size ); 3154f5e7dd7Smrg close( memfd ); 3164f5e7dd7Smrg 3174f5e7dd7Smrg if (pci_rom) { 3184f5e7dd7Smrg pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); 3194f5e7dd7Smrg pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); 3204f5e7dd7Smrg } 3214f5e7dd7Smrg 3224f5e7dd7Smrg return 0; 3234f5e7dd7Smrg} 3244f5e7dd7Smrg 3254f5e7dd7Smrg/** Returns the number of regions (base address registers) the device has */ 3264f5e7dd7Smrg 3274f5e7dd7Smrgstatic int 3284f5e7dd7Smrgpci_device_freebsd_get_num_regions( struct pci_device * dev ) 3294f5e7dd7Smrg{ 3304f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 3314f5e7dd7Smrg 3324f5e7dd7Smrg switch (priv->header_type) { 3334f5e7dd7Smrg case 0: 3344f5e7dd7Smrg return 6; 3354f5e7dd7Smrg case 1: 3364f5e7dd7Smrg return 2; 3374f5e7dd7Smrg case 2: 3384f5e7dd7Smrg return 1; 3394f5e7dd7Smrg default: 3404f5e7dd7Smrg printf("unknown header type %02x\n", priv->header_type); 3414f5e7dd7Smrg return 0; 3424f5e7dd7Smrg } 3434f5e7dd7Smrg} 3444f5e7dd7Smrg 3454f5e7dd7Smrgstatic int 3464f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev ) 3474f5e7dd7Smrg{ 3484f5e7dd7Smrg struct pci_bar_io bar; 3494f5e7dd7Smrg uint8_t irq; 3504f5e7dd7Smrg int err, i; 3514f5e7dd7Smrg 3524f5e7dd7Smrg bar.pbi_sel.pc_domain = dev->domain; 3534f5e7dd7Smrg bar.pbi_sel.pc_bus = dev->bus; 3544f5e7dd7Smrg bar.pbi_sel.pc_dev = dev->dev; 3554f5e7dd7Smrg bar.pbi_sel.pc_func = dev->func; 3564f5e7dd7Smrg 3574f5e7dd7Smrg 3584f5e7dd7Smrg /* Many of the fields were filled in during initial device enumeration. 3594f5e7dd7Smrg * At this point, we need to fill in regions, rom_size, and irq. 3604f5e7dd7Smrg */ 3614f5e7dd7Smrg 3624f5e7dd7Smrg err = pci_device_cfg_read_u8( dev, &irq, 60 ); 3634f5e7dd7Smrg if (err) 3644f5e7dd7Smrg return errno; 3654f5e7dd7Smrg dev->irq = irq; 3664f5e7dd7Smrg 3674f5e7dd7Smrg for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 3684f5e7dd7Smrg bar.pbi_reg = PCIR_BAR(i); 369cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) 3704f5e7dd7Smrg continue; 3714f5e7dd7Smrg 3724f5e7dd7Smrg if (PCI_BAR_IO(bar.pbi_base)) 3734f5e7dd7Smrg dev->regions[i].is_IO = 1; 3744f5e7dd7Smrg 3754f5e7dd7Smrg if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) 3764f5e7dd7Smrg dev->regions[i].is_64 = 1; 3774f5e7dd7Smrg 3784f5e7dd7Smrg if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 3794f5e7dd7Smrg dev->regions[i].is_prefetchable = 1; 3804f5e7dd7Smrg 3814f5e7dd7Smrg dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); 3824f5e7dd7Smrg dev->regions[i].size = bar.pbi_length; 3834f5e7dd7Smrg } 3844f5e7dd7Smrg 3854f5e7dd7Smrg /* If it's a VGA device, set up the rom size for read_rom using the 3864f5e7dd7Smrg * 0xc0000 mapping. 3874f5e7dd7Smrg */ 3884f5e7dd7Smrg if ((dev->device_class & 0x00ffff00) == 3894f5e7dd7Smrg ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { 3904f5e7dd7Smrg dev->rom_size = 64 * 1024; 3914f5e7dd7Smrg } 3924f5e7dd7Smrg 3934f5e7dd7Smrg return 0; 3944f5e7dd7Smrg} 3954f5e7dd7Smrg 3962029f493Smrgstatic void 3972029f493Smrgpci_system_freebsd_destroy( void ) 3984f5e7dd7Smrg{ 3992029f493Smrg close(freebsd_pci_sys->pcidev); 4002029f493Smrg free(freebsd_pci_sys->pci_sys.devices); 4012029f493Smrg freebsd_pci_sys = NULL; 4024f5e7dd7Smrg} 4034f5e7dd7Smrg 4044f5e7dd7Smrgstatic int 4052029f493Smrgpci_device_freebsd_has_kernel_driver( struct pci_device *dev ) 4064f5e7dd7Smrg{ 4072029f493Smrg struct pci_io io; 4084f5e7dd7Smrg 4092029f493Smrg io.pi_sel.pc_domain = dev->domain; 4102029f493Smrg io.pi_sel.pc_bus = dev->bus; 4112029f493Smrg io.pi_sel.pc_dev = dev->dev; 4122029f493Smrg io.pi_sel.pc_func = dev->func; 4132029f493Smrg 4142029f493Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCATTACHED, &io ) < 0 ) { 4152029f493Smrg return 0; 4164f5e7dd7Smrg } 4174f5e7dd7Smrg 4182029f493Smrg /* if io.pi_data is 0, no driver is attached */ 4192029f493Smrg return io.pi_data == 0 ? 0 : 1; 4204f5e7dd7Smrg} 4214f5e7dd7Smrg 4222029f493Smrgstatic struct pci_io_handle * 4232029f493Smrgpci_device_freebsd_open_legacy_io( struct pci_io_handle *ret, 4242029f493Smrg struct pci_device *dev, pciaddr_t base, 4252029f493Smrg pciaddr_t size ) 4264f5e7dd7Smrg{ 4272029f493Smrg ret->fd = open( "/dev/io", O_RDWR | O_CLOEXEC ); 4282029f493Smrg if ( ret->fd < 0 ) 4292029f493Smrg return NULL; 4302029f493Smrg ret->base = base; 4312029f493Smrg ret->size = size; 4322029f493Smrg ret->is_legacy = 1; 4332029f493Smrg return ret; 4344f5e7dd7Smrg} 4354f5e7dd7Smrg 43686ea1d58Smrgstatic struct pci_io_handle * 4372029f493Smrgpci_device_freebsd_open_io( struct pci_io_handle *ret, 4382029f493Smrg struct pci_device *dev, int bar, 4392029f493Smrg pciaddr_t base, pciaddr_t size ) 44086ea1d58Smrg{ 4412029f493Smrg ret = pci_device_freebsd_open_legacy_io( ret, dev, base, size ); 4422029f493Smrg if ( ret != NULL ) 4432029f493Smrg ret->is_legacy = 0; 4442029f493Smrg return ret; 44586ea1d58Smrg} 44686ea1d58Smrg 44786ea1d58Smrgstatic void 4482029f493Smrgpci_device_freebsd_close_io( struct pci_device *dev, 4492029f493Smrg struct pci_io_handle *handle ) 45086ea1d58Smrg{ 4512029f493Smrg if ( handle->fd > -1 ) 4522029f493Smrg close( handle->fd ); 4532029f493Smrg} 45486ea1d58Smrg 45586ea1d58Smrgstatic uint32_t 4562029f493Smrgpci_device_freebsd_read32( struct pci_io_handle *handle, uint32_t reg ) 45786ea1d58Smrg{ 4585ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 4592029f493Smrg return inl( handle->base + reg ); 46086ea1d58Smrg#else 4612029f493Smrg struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 4, 0 }; 4622029f493Smrg if ( handle->fd > -1 ) 4632029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 4642029f493Smrg return req.val; 46586ea1d58Smrg#endif 46686ea1d58Smrg} 46786ea1d58Smrg 46886ea1d58Smrgstatic uint16_t 4692029f493Smrgpci_device_freebsd_read16( struct pci_io_handle *handle, uint32_t reg ) 47086ea1d58Smrg{ 4715ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 4722029f493Smrg return inw( handle->base + reg ); 47386ea1d58Smrg#else 4742029f493Smrg struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 2, 0 }; 4752029f493Smrg if ( handle->fd > -1 ) 4762029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 4772029f493Smrg return req.val; 47886ea1d58Smrg#endif 47986ea1d58Smrg} 48086ea1d58Smrg 48186ea1d58Smrgstatic uint8_t 4822029f493Smrgpci_device_freebsd_read8( struct pci_io_handle *handle, uint32_t reg ) 48386ea1d58Smrg{ 4845ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 4852029f493Smrg return inb( handle->base + reg ); 48686ea1d58Smrg#else 4872029f493Smrg struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 1, 0 }; 4882029f493Smrg if ( handle->fd > -1 ) 4892029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 4902029f493Smrg return req.val; 49186ea1d58Smrg#endif 49286ea1d58Smrg} 49386ea1d58Smrg 49486ea1d58Smrgstatic void 4952029f493Smrgpci_device_freebsd_write32( struct pci_io_handle *handle, uint32_t reg, 4962029f493Smrg uint32_t data ) 49786ea1d58Smrg{ 4985ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 4992029f493Smrg outl( handle->base + reg, data ); 50086ea1d58Smrg#else 5012029f493Smrg struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 4, data }; 5022029f493Smrg if ( handle->fd > -1 ) 5032029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 50486ea1d58Smrg#endif 50586ea1d58Smrg} 50686ea1d58Smrg 50786ea1d58Smrgstatic void 5082029f493Smrgpci_device_freebsd_write16( struct pci_io_handle *handle, uint32_t reg, 5092029f493Smrg uint16_t data ) 51086ea1d58Smrg{ 5115ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 5122029f493Smrg outw( handle->base + reg, data ); 51386ea1d58Smrg#else 5142029f493Smrg struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 2, data }; 5152029f493Smrg if ( handle->fd > -1 ) 5162029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 51786ea1d58Smrg#endif 51886ea1d58Smrg} 51986ea1d58Smrg 52086ea1d58Smrgstatic void 5212029f493Smrgpci_device_freebsd_write8( struct pci_io_handle *handle, uint32_t reg, 5222029f493Smrg uint8_t data ) 52386ea1d58Smrg{ 5245ad99bdfSmrg#if defined(__i386__) || defined(__amd64__) 5252029f493Smrg outb( handle->base + reg, data ); 52686ea1d58Smrg#else 5272029f493Smrg struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 1, data }; 5282029f493Smrg if ( handle->fd > -1 ) 5292029f493Smrg ioctl( handle->fd, IODEV_PIO, &req ); 53086ea1d58Smrg#endif 53186ea1d58Smrg} 53286ea1d58Smrg 53386ea1d58Smrgstatic int 5342029f493Smrgpci_device_freebsd_map_legacy( struct pci_device *dev, pciaddr_t base, 5352029f493Smrg pciaddr_t size, unsigned map_flags, void **addr ) 53686ea1d58Smrg{ 5372029f493Smrg struct pci_device_mapping map; 5382029f493Smrg int err; 53986ea1d58Smrg 5402029f493Smrg map.base = base; 5412029f493Smrg map.size = size; 5422029f493Smrg map.flags = map_flags; 5432029f493Smrg map.memory = NULL; 5442029f493Smrg err = pci_device_freebsd_map_range( dev, &map ); 5452029f493Smrg *addr = map.memory; 54686ea1d58Smrg 5472029f493Smrg return err; 54886ea1d58Smrg} 54986ea1d58Smrg 55086ea1d58Smrgstatic int 5512029f493Smrgpci_device_freebsd_unmap_legacy( struct pci_device *dev, void *addr, 5522029f493Smrg pciaddr_t size ) 55386ea1d58Smrg{ 5542029f493Smrg struct pci_device_mapping map; 55586ea1d58Smrg 5562029f493Smrg map.memory = addr; 5572029f493Smrg map.size = size; 5582029f493Smrg map.flags = 0; 5592029f493Smrg return pci_device_freebsd_unmap_range( dev, &map ); 56086ea1d58Smrg} 56186ea1d58Smrg 5624f5e7dd7Smrgstatic const struct pci_system_methods freebsd_pci_methods = { 5634f5e7dd7Smrg .destroy = pci_system_freebsd_destroy, 5644f5e7dd7Smrg .destroy_device = NULL, /* nothing to do for this */ 5654f5e7dd7Smrg .read_rom = pci_device_freebsd_read_rom, 5664f5e7dd7Smrg .probe = pci_device_freebsd_probe, 5674f5e7dd7Smrg .map_range = pci_device_freebsd_map_range, 5684f5e7dd7Smrg .unmap_range = pci_device_freebsd_unmap_range, 5692029f493Smrg 5704f5e7dd7Smrg .read = pci_device_freebsd_read, 5714f5e7dd7Smrg .write = pci_device_freebsd_write, 5722029f493Smrg 5734f5e7dd7Smrg .fill_capabilities = pci_fill_capabilities_generic, 5742029f493Smrg .enable = NULL, 5752029f493Smrg .boot_vga = NULL, 5762029f493Smrg .has_kernel_driver = pci_device_freebsd_has_kernel_driver, 5772029f493Smrg 5782029f493Smrg .open_device_io = pci_device_freebsd_open_io, 57986ea1d58Smrg .open_legacy_io = pci_device_freebsd_open_legacy_io, 58086ea1d58Smrg .close_io = pci_device_freebsd_close_io, 58186ea1d58Smrg .read32 = pci_device_freebsd_read32, 58286ea1d58Smrg .read16 = pci_device_freebsd_read16, 58386ea1d58Smrg .read8 = pci_device_freebsd_read8, 58486ea1d58Smrg .write32 = pci_device_freebsd_write32, 58586ea1d58Smrg .write16 = pci_device_freebsd_write16, 58686ea1d58Smrg .write8 = pci_device_freebsd_write8, 5872029f493Smrg 58886ea1d58Smrg .map_legacy = pci_device_freebsd_map_legacy, 58986ea1d58Smrg .unmap_legacy = pci_device_freebsd_unmap_legacy, 5904f5e7dd7Smrg}; 5914f5e7dd7Smrg 5924f5e7dd7Smrg/** 5934f5e7dd7Smrg * Attempt to access the FreeBSD PCI interface. 5944f5e7dd7Smrg */ 5954f5e7dd7Smrg_pci_hidden int 5964f5e7dd7Smrgpci_system_freebsd_create( void ) 5974f5e7dd7Smrg{ 5984f5e7dd7Smrg struct pci_conf_io pciconfio; 5994f5e7dd7Smrg struct pci_conf pciconf[255]; 6004f5e7dd7Smrg int pcidev; 6014f5e7dd7Smrg int i; 6024f5e7dd7Smrg 6034f5e7dd7Smrg /* Try to open the PCI device */ 604cad31331Smrg pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); 6054f5e7dd7Smrg if ( pcidev == -1 ) 6064f5e7dd7Smrg return ENXIO; 6074f5e7dd7Smrg 6084f5e7dd7Smrg freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) ); 6094f5e7dd7Smrg if ( freebsd_pci_sys == NULL ) { 6104f5e7dd7Smrg close( pcidev ); 6114f5e7dd7Smrg return ENOMEM; 6124f5e7dd7Smrg } 6134f5e7dd7Smrg pci_sys = &freebsd_pci_sys->pci_sys; 6144f5e7dd7Smrg 6154f5e7dd7Smrg pci_sys->methods = & freebsd_pci_methods; 6164f5e7dd7Smrg freebsd_pci_sys->pcidev = pcidev; 6174f5e7dd7Smrg 6184f5e7dd7Smrg /* Probe the list of devices known by the system */ 6194f5e7dd7Smrg bzero( &pciconfio, sizeof( struct pci_conf_io ) ); 6204f5e7dd7Smrg pciconfio.match_buf_len = sizeof(pciconf); 6214f5e7dd7Smrg pciconfio.matches = pciconf; 6224f5e7dd7Smrg 6234f5e7dd7Smrg if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) { 6244f5e7dd7Smrg free( pci_sys ); 62548becaf0Smrg pci_sys = NULL; 6264f5e7dd7Smrg close( pcidev ); 6274f5e7dd7Smrg return errno; 6284f5e7dd7Smrg } 6294f5e7dd7Smrg 6304f5e7dd7Smrg if (pciconfio.status == PCI_GETCONF_ERROR ) { 6314f5e7dd7Smrg free( pci_sys ); 63248becaf0Smrg pci_sys = NULL; 6334f5e7dd7Smrg close( pcidev ); 6344f5e7dd7Smrg return EINVAL; 6354f5e7dd7Smrg } 6364f5e7dd7Smrg 6374f5e7dd7Smrg /* Translate the list of devices into pciaccess's format. */ 6384f5e7dd7Smrg pci_sys->num_devices = pciconfio.num_matches; 6394f5e7dd7Smrg pci_sys->devices = calloc( pciconfio.num_matches, 6404f5e7dd7Smrg sizeof( struct pci_device_private ) ); 6414f5e7dd7Smrg 6424f5e7dd7Smrg for ( i = 0; i < pciconfio.num_matches; i++ ) { 6434f5e7dd7Smrg struct pci_conf *p = &pciconf[ i ]; 6444f5e7dd7Smrg 6454f5e7dd7Smrg pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; 6464f5e7dd7Smrg pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; 6474f5e7dd7Smrg pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; 6484f5e7dd7Smrg pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; 6494f5e7dd7Smrg pci_sys->devices[ i ].base.vendor_id = p->pc_vendor; 6504f5e7dd7Smrg pci_sys->devices[ i ].base.device_id = p->pc_device; 6514f5e7dd7Smrg pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; 6524f5e7dd7Smrg pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; 6534f5e7dd7Smrg pci_sys->devices[ i ].base.revision = p->pc_revid; 6544f5e7dd7Smrg pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | 6554f5e7dd7Smrg (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; 6564f5e7dd7Smrg pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; 6574f5e7dd7Smrg } 6584f5e7dd7Smrg 6594f5e7dd7Smrg return 0; 6604f5e7dd7Smrg} 661