freebsd_pci.c revision cad31331
14f5e7dd7Smrg/* 24f5e7dd7Smrg * (C) Copyright Eric Anholt 2006 34f5e7dd7Smrg * (C) Copyright IBM Corporation 2006 44f5e7dd7Smrg * All Rights Reserved. 54f5e7dd7Smrg * 64f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a 74f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"), 84f5e7dd7Smrg * to deal in the Software without restriction, including without limitation 94f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 104f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom 114f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions: 124f5e7dd7Smrg * 134f5e7dd7Smrg * The above copyright notice and this permission notice (including the next 144f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the 154f5e7dd7Smrg * Software. 164f5e7dd7Smrg * 174f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 184f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 194f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 204f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 214f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 224f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 234f5e7dd7Smrg * DEALINGS IN THE SOFTWARE. 244f5e7dd7Smrg */ 254f5e7dd7Smrg 264f5e7dd7Smrg/** 274f5e7dd7Smrg * \file freebsd_pci.c 284f5e7dd7Smrg * 294f5e7dd7Smrg * Access the kernel PCI support using /dev/pci's ioctl and mmap interface. 304f5e7dd7Smrg * 314f5e7dd7Smrg * \author Eric Anholt <eric@anholt.net> 324f5e7dd7Smrg */ 334f5e7dd7Smrg 344f5e7dd7Smrg#include <stdlib.h> 354f5e7dd7Smrg#include <stdio.h> 364f5e7dd7Smrg#include <string.h> 374f5e7dd7Smrg#include <unistd.h> 384f5e7dd7Smrg#include <fcntl.h> 394f5e7dd7Smrg#include <errno.h> 404f5e7dd7Smrg#include <sys/types.h> 414f5e7dd7Smrg#include <sys/param.h> 424f5e7dd7Smrg#include <sys/pciio.h> 434f5e7dd7Smrg#include <sys/mman.h> 444f5e7dd7Smrg#include <sys/memrange.h> 454f5e7dd7Smrg 464f5e7dd7Smrg#include "config.h" 474f5e7dd7Smrg#include "pciaccess.h" 484f5e7dd7Smrg#include "pciaccess_private.h" 494f5e7dd7Smrg 504f5e7dd7Smrg#define PCIC_DISPLAY 0x03 514f5e7dd7Smrg#define PCIS_DISPLAY_VGA 0x00 524f5e7dd7Smrg#define PCIS_DISPLAY_XGA 0x01 534f5e7dd7Smrg#define PCIS_DISPLAY_3D 0x02 544f5e7dd7Smrg#define PCIS_DISPLAY_OTHER 0x80 554f5e7dd7Smrg 564f5e7dd7Smrg/* Registers taken from pcireg.h */ 574f5e7dd7Smrg#define PCIR_COMMAND 0x04 584f5e7dd7Smrg#define PCIM_CMD_PORTEN 0x0001 594f5e7dd7Smrg#define PCIM_CMD_MEMEN 0x0002 604f5e7dd7Smrg#define PCIR_BIOS 0x30 614f5e7dd7Smrg#define PCIM_BIOS_ENABLE 0x01 624f5e7dd7Smrg#define PCIM_BIOS_ADDR_MASK 0xfffff800 634f5e7dd7Smrg 644f5e7dd7Smrg#define PCIR_BARS 0x10 654f5e7dd7Smrg#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) 664f5e7dd7Smrg#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) 674f5e7dd7Smrg#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) 684f5e7dd7Smrg#define PCIM_BAR_MEM_TYPE 0x00000006 694f5e7dd7Smrg#define PCIM_BAR_MEM_64 4 704f5e7dd7Smrg#define PCIM_BAR_MEM_PREFETCH 0x00000008 714f5e7dd7Smrg#define PCIM_BAR_SPACE 0x00000001 724f5e7dd7Smrg#define PCIM_BAR_MEM_SPACE 0 734f5e7dd7Smrg#define PCIM_BAR_IO_SPACE 1 744f5e7dd7Smrg 754f5e7dd7Smrg/** 764f5e7dd7Smrg * FreeBSD private pci_system structure that extends the base pci_system 774f5e7dd7Smrg * structure. 784f5e7dd7Smrg * 794f5e7dd7Smrg * It is initialized once and used as a global, just as pci_system is used. 804f5e7dd7Smrg */ 814f5e7dd7Smrg_pci_hidden 824f5e7dd7Smrgstruct freebsd_pci_system { 834f5e7dd7Smrg /* This must be the first entry in the structure, as pci_system_cleanup() 844f5e7dd7Smrg * frees pci_sys. 854f5e7dd7Smrg */ 864f5e7dd7Smrg struct pci_system pci_sys; 874f5e7dd7Smrg 884f5e7dd7Smrg int pcidev; /**< fd for /dev/pci */ 894f5e7dd7Smrg} *freebsd_pci_sys; 904f5e7dd7Smrg 914f5e7dd7Smrg/** 924f5e7dd7Smrg * Map a memory region for a device using /dev/mem. 93cad31331Smrg * 944f5e7dd7Smrg * \param dev Device whose memory region is to be mapped. 954f5e7dd7Smrg * \param map Parameters of the mapping that is to be created. 96cad31331Smrg * 974f5e7dd7Smrg * \return 984f5e7dd7Smrg * Zero on success or an \c errno value on failure. 994f5e7dd7Smrg */ 1004f5e7dd7Smrgstatic int 1014f5e7dd7Smrgpci_device_freebsd_map_range(struct pci_device *dev, 1024f5e7dd7Smrg struct pci_device_mapping *map) 1034f5e7dd7Smrg{ 104cad31331Smrg const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 1054f5e7dd7Smrg ? (PROT_READ | PROT_WRITE) : PROT_READ; 1064f5e7dd7Smrg struct mem_range_desc mrd; 1074f5e7dd7Smrg struct mem_range_op mro; 1084f5e7dd7Smrg 1094f5e7dd7Smrg int fd, err = 0; 1104f5e7dd7Smrg 111cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1124f5e7dd7Smrg if (fd == -1) 1134f5e7dd7Smrg return errno; 1144f5e7dd7Smrg 1154f5e7dd7Smrg map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); 1164f5e7dd7Smrg 1174f5e7dd7Smrg if (map->memory == MAP_FAILED) { 1184f5e7dd7Smrg err = errno; 1194f5e7dd7Smrg } 1204f5e7dd7Smrg 1214f5e7dd7Smrg mrd.mr_base = map->base; 1224f5e7dd7Smrg mrd.mr_len = map->size; 1234f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1244f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 1254f5e7dd7Smrg mrd.mr_flags = MDF_WRITEBACK; 1264f5e7dd7Smrg else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 1274f5e7dd7Smrg mrd.mr_flags = MDF_WRITECOMBINE; 1284f5e7dd7Smrg else 1294f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1304f5e7dd7Smrg mro.mo_desc = &mrd; 1314f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_UPDATE; 1324f5e7dd7Smrg 1334f5e7dd7Smrg /* No need to set an MTRR if it's the default mode. */ 1344f5e7dd7Smrg if (mrd.mr_flags != MDF_UNCACHEABLE) { 1354f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1364f5e7dd7Smrg fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno)); 1374f5e7dd7Smrg } 1384f5e7dd7Smrg } 1394f5e7dd7Smrg 1404f5e7dd7Smrg close(fd); 1414f5e7dd7Smrg 1424f5e7dd7Smrg return err; 1434f5e7dd7Smrg} 1444f5e7dd7Smrg 1454f5e7dd7Smrgstatic int 1464f5e7dd7Smrgpci_device_freebsd_unmap_range( struct pci_device *dev, 1474f5e7dd7Smrg struct pci_device_mapping *map ) 1484f5e7dd7Smrg{ 1494f5e7dd7Smrg struct mem_range_desc mrd; 1504f5e7dd7Smrg struct mem_range_op mro; 1514f5e7dd7Smrg int fd; 1524f5e7dd7Smrg 1534f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1544f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) 1554f5e7dd7Smrg { 156cad31331Smrg fd = open("/dev/mem", O_RDWR | O_CLOEXEC); 1574f5e7dd7Smrg if (fd != -1) { 1584f5e7dd7Smrg mrd.mr_base = map->base; 1594f5e7dd7Smrg mrd.mr_len = map->size; 1604f5e7dd7Smrg strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); 1614f5e7dd7Smrg mrd.mr_flags = MDF_UNCACHEABLE; 1624f5e7dd7Smrg mro.mo_desc = &mrd; 1634f5e7dd7Smrg mro.mo_arg[0] = MEMRANGE_SET_REMOVE; 1644f5e7dd7Smrg 1654f5e7dd7Smrg if (ioctl(fd, MEMRANGE_SET, &mro)) { 1664f5e7dd7Smrg fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); 1674f5e7dd7Smrg } 1684f5e7dd7Smrg 1694f5e7dd7Smrg close(fd); 1704f5e7dd7Smrg } else { 1714f5e7dd7Smrg fprintf(stderr, "Failed to open /dev/mem\n"); 1724f5e7dd7Smrg } 1734f5e7dd7Smrg } 1744f5e7dd7Smrg 1754f5e7dd7Smrg return pci_device_generic_unmap_range(dev, map); 1764f5e7dd7Smrg} 1774f5e7dd7Smrg 1784f5e7dd7Smrgstatic int 1794f5e7dd7Smrgpci_device_freebsd_read( struct pci_device * dev, void * data, 1804f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 1814f5e7dd7Smrg pciaddr_t * bytes_read ) 1824f5e7dd7Smrg{ 1834f5e7dd7Smrg struct pci_io io; 1844f5e7dd7Smrg 1854f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 1864f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 1874f5e7dd7Smrg#endif 1884f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 1894f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 1904f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 1914f5e7dd7Smrg 1924f5e7dd7Smrg *bytes_read = 0; 1934f5e7dd7Smrg while ( size > 0 ) { 1944f5e7dd7Smrg int toread = (size < 4) ? size : 4; 1954f5e7dd7Smrg 1964f5e7dd7Smrg /* Only power of two allowed. */ 1974f5e7dd7Smrg if (toread == 3) 1984f5e7dd7Smrg toread = 2; 1994f5e7dd7Smrg 2004f5e7dd7Smrg io.pi_reg = offset; 2014f5e7dd7Smrg io.pi_width = toread; 2024f5e7dd7Smrg 203cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) 2044f5e7dd7Smrg return errno; 2054f5e7dd7Smrg 2064f5e7dd7Smrg memcpy(data, &io.pi_data, toread ); 2074f5e7dd7Smrg 2084f5e7dd7Smrg offset += toread; 2094f5e7dd7Smrg data = (char *)data + toread; 2104f5e7dd7Smrg size -= toread; 2114f5e7dd7Smrg *bytes_read += toread; 2124f5e7dd7Smrg } 2134f5e7dd7Smrg 2144f5e7dd7Smrg return 0; 2154f5e7dd7Smrg} 2164f5e7dd7Smrg 2174f5e7dd7Smrg 2184f5e7dd7Smrgstatic int 2194f5e7dd7Smrgpci_device_freebsd_write( struct pci_device * dev, const void * data, 2204f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 2214f5e7dd7Smrg pciaddr_t * bytes_written ) 2224f5e7dd7Smrg{ 2234f5e7dd7Smrg struct pci_io io; 2244f5e7dd7Smrg 2254f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 2264f5e7dd7Smrg io.pi_sel.pc_domain = dev->domain; 2274f5e7dd7Smrg#endif 2284f5e7dd7Smrg io.pi_sel.pc_bus = dev->bus; 2294f5e7dd7Smrg io.pi_sel.pc_dev = dev->dev; 2304f5e7dd7Smrg io.pi_sel.pc_func = dev->func; 2314f5e7dd7Smrg 2324f5e7dd7Smrg *bytes_written = 0; 2334f5e7dd7Smrg while ( size > 0 ) { 2344f5e7dd7Smrg int towrite = (size < 4 ? size : 4); 2354f5e7dd7Smrg 2364f5e7dd7Smrg /* Only power of two allowed. */ 2374f5e7dd7Smrg if (towrite == 3) 2384f5e7dd7Smrg towrite = 2; 2394f5e7dd7Smrg 2404f5e7dd7Smrg io.pi_reg = offset; 2414f5e7dd7Smrg io.pi_width = towrite; 2424f5e7dd7Smrg memcpy( &io.pi_data, data, towrite ); 2434f5e7dd7Smrg 244cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) 2454f5e7dd7Smrg return errno; 2464f5e7dd7Smrg 2474f5e7dd7Smrg offset += towrite; 2484f5e7dd7Smrg data = (char *)data + towrite; 2494f5e7dd7Smrg size -= towrite; 2504f5e7dd7Smrg *bytes_written += towrite; 2514f5e7dd7Smrg } 2524f5e7dd7Smrg 2534f5e7dd7Smrg return 0; 2544f5e7dd7Smrg} 2554f5e7dd7Smrg 2564f5e7dd7Smrg/** 2574f5e7dd7Smrg * Read a VGA rom using the 0xc0000 mapping. 2584f5e7dd7Smrg * 2594f5e7dd7Smrg * This function should be extended to handle access through PCI resources, 2604f5e7dd7Smrg * which should be more reliable when available. 2614f5e7dd7Smrg */ 2624f5e7dd7Smrgstatic int 2634f5e7dd7Smrgpci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) 2644f5e7dd7Smrg{ 2654f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 2664f5e7dd7Smrg void *bios; 2674f5e7dd7Smrg pciaddr_t rom_base; 2684f5e7dd7Smrg uint32_t rom; 2694f5e7dd7Smrg uint16_t reg; 2704f5e7dd7Smrg int pci_rom, memfd; 2714f5e7dd7Smrg 2724f5e7dd7Smrg if ( ( dev->device_class & 0x00ffff00 ) != 2734f5e7dd7Smrg ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) 2744f5e7dd7Smrg { 2754f5e7dd7Smrg return ENOSYS; 2764f5e7dd7Smrg } 2774f5e7dd7Smrg 2784f5e7dd7Smrg if (priv->rom_base == 0) { 2794f5e7dd7Smrg#if defined(__amd64__) || defined(__i386__) 2804f5e7dd7Smrg rom_base = 0xc0000; 2814f5e7dd7Smrg pci_rom = 0; 2824f5e7dd7Smrg#else 2834f5e7dd7Smrg return ENOSYS; 2844f5e7dd7Smrg#endif 2854f5e7dd7Smrg } else { 2864f5e7dd7Smrg rom_base = priv->rom_base; 2874f5e7dd7Smrg pci_rom = 1; 2884f5e7dd7Smrg 2894f5e7dd7Smrg pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); 2904f5e7dd7Smrg pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); 2914f5e7dd7Smrg pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); 2924f5e7dd7Smrg pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); 2934f5e7dd7Smrg } 2944f5e7dd7Smrg 2954f5e7dd7Smrg printf("Using rom_base = 0x%lx\n", (long)rom_base); 296cad31331Smrg memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); 2974f5e7dd7Smrg if ( memfd == -1 ) 2984f5e7dd7Smrg return errno; 2994f5e7dd7Smrg 3004f5e7dd7Smrg bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); 3014f5e7dd7Smrg if ( bios == MAP_FAILED ) { 3024f5e7dd7Smrg close( memfd ); 3034f5e7dd7Smrg return errno; 3044f5e7dd7Smrg } 3054f5e7dd7Smrg 3064f5e7dd7Smrg memcpy( buffer, bios, dev->rom_size ); 3074f5e7dd7Smrg 3084f5e7dd7Smrg munmap( bios, dev->rom_size ); 3094f5e7dd7Smrg close( memfd ); 3104f5e7dd7Smrg 3114f5e7dd7Smrg if (pci_rom) { 3124f5e7dd7Smrg pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); 3134f5e7dd7Smrg pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); 3144f5e7dd7Smrg } 3154f5e7dd7Smrg 3164f5e7dd7Smrg return 0; 3174f5e7dd7Smrg} 3184f5e7dd7Smrg 3194f5e7dd7Smrg/** Returns the number of regions (base address registers) the device has */ 3204f5e7dd7Smrg 3214f5e7dd7Smrgstatic int 3224f5e7dd7Smrgpci_device_freebsd_get_num_regions( struct pci_device * dev ) 3234f5e7dd7Smrg{ 3244f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 3254f5e7dd7Smrg 3264f5e7dd7Smrg switch (priv->header_type) { 3274f5e7dd7Smrg case 0: 3284f5e7dd7Smrg return 6; 3294f5e7dd7Smrg case 1: 3304f5e7dd7Smrg return 2; 3314f5e7dd7Smrg case 2: 3324f5e7dd7Smrg return 1; 3334f5e7dd7Smrg default: 3344f5e7dd7Smrg printf("unknown header type %02x\n", priv->header_type); 3354f5e7dd7Smrg return 0; 3364f5e7dd7Smrg } 3374f5e7dd7Smrg} 3384f5e7dd7Smrg 3394f5e7dd7Smrg#ifdef PCIOCGETBAR 3404f5e7dd7Smrg 3414f5e7dd7Smrgstatic int 3424f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev ) 3434f5e7dd7Smrg{ 3444f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 3454f5e7dd7Smrg struct pci_bar_io bar; 3464f5e7dd7Smrg uint8_t irq; 3474f5e7dd7Smrg int err, i; 3484f5e7dd7Smrg 3494f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 3504f5e7dd7Smrg bar.pbi_sel.pc_domain = dev->domain; 3514f5e7dd7Smrg#endif 3524f5e7dd7Smrg bar.pbi_sel.pc_bus = dev->bus; 3534f5e7dd7Smrg bar.pbi_sel.pc_dev = dev->dev; 3544f5e7dd7Smrg bar.pbi_sel.pc_func = dev->func; 3554f5e7dd7Smrg 3564f5e7dd7Smrg 3574f5e7dd7Smrg /* Many of the fields were filled in during initial device enumeration. 3584f5e7dd7Smrg * At this point, we need to fill in regions, rom_size, and irq. 3594f5e7dd7Smrg */ 3604f5e7dd7Smrg 3614f5e7dd7Smrg err = pci_device_cfg_read_u8( dev, &irq, 60 ); 3624f5e7dd7Smrg if (err) 3634f5e7dd7Smrg return errno; 3644f5e7dd7Smrg dev->irq = irq; 3654f5e7dd7Smrg 3664f5e7dd7Smrg for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 3674f5e7dd7Smrg bar.pbi_reg = PCIR_BAR(i); 368cad31331Smrg if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) 3694f5e7dd7Smrg continue; 3704f5e7dd7Smrg 3714f5e7dd7Smrg if (PCI_BAR_IO(bar.pbi_base)) 3724f5e7dd7Smrg dev->regions[i].is_IO = 1; 3734f5e7dd7Smrg 3744f5e7dd7Smrg if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) 3754f5e7dd7Smrg dev->regions[i].is_64 = 1; 3764f5e7dd7Smrg 3774f5e7dd7Smrg if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) 3784f5e7dd7Smrg dev->regions[i].is_prefetchable = 1; 3794f5e7dd7Smrg 3804f5e7dd7Smrg dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); 3814f5e7dd7Smrg dev->regions[i].size = bar.pbi_length; 3824f5e7dd7Smrg } 3834f5e7dd7Smrg 3844f5e7dd7Smrg /* If it's a VGA device, set up the rom size for read_rom using the 3854f5e7dd7Smrg * 0xc0000 mapping. 3864f5e7dd7Smrg */ 3874f5e7dd7Smrg if ((dev->device_class & 0x00ffff00) == 3884f5e7dd7Smrg ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { 3894f5e7dd7Smrg dev->rom_size = 64 * 1024; 3904f5e7dd7Smrg } 3914f5e7dd7Smrg 3924f5e7dd7Smrg return 0; 3934f5e7dd7Smrg} 3944f5e7dd7Smrg 3954f5e7dd7Smrg#else 3964f5e7dd7Smrg 3974f5e7dd7Smrg/** Masks out the flag bigs of the base address register value */ 3984f5e7dd7Smrgstatic uint32_t 3994f5e7dd7Smrgget_map_base( uint32_t val ) 4004f5e7dd7Smrg{ 4014f5e7dd7Smrg if (val & 0x01) 4024f5e7dd7Smrg return val & ~0x03; 4034f5e7dd7Smrg else 4044f5e7dd7Smrg return val & ~0x0f; 4054f5e7dd7Smrg} 4064f5e7dd7Smrg 4074f5e7dd7Smrg/** Returns the size of a region based on the all-ones test value */ 4084f5e7dd7Smrgstatic int 4094f5e7dd7Smrgget_test_val_size( uint32_t testval ) 4104f5e7dd7Smrg{ 4114f5e7dd7Smrg if (testval == 0) 4124f5e7dd7Smrg return 0; 4134f5e7dd7Smrg 4144f5e7dd7Smrg /* Mask out the flag bits */ 4154f5e7dd7Smrg testval = get_map_base( testval ); 4164f5e7dd7Smrg 4174f5e7dd7Smrg return 1 << (ffs(testval) - 1); 4184f5e7dd7Smrg} 4194f5e7dd7Smrg 4204f5e7dd7Smrg/** 4214f5e7dd7Smrg * Sets the address and size information for the region from config space 4224f5e7dd7Smrg * registers. 4234f5e7dd7Smrg * 4244f5e7dd7Smrg * This would be much better provided by a kernel interface. 4254f5e7dd7Smrg * 4264f5e7dd7Smrg * \return 0 on success, or an errno value. 4274f5e7dd7Smrg */ 4284f5e7dd7Smrgstatic int 4294f5e7dd7Smrgpci_device_freebsd_get_region_info( struct pci_device * dev, int region, 4304f5e7dd7Smrg int bar ) 4314f5e7dd7Smrg{ 4324f5e7dd7Smrg uint32_t addr, testval; 4334f5e7dd7Smrg uint16_t cmd; 4344f5e7dd7Smrg int err; 4354f5e7dd7Smrg 4364f5e7dd7Smrg /* Get the base address */ 4374f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &addr, bar ); 4384f5e7dd7Smrg if (err != 0) 4394f5e7dd7Smrg return err; 4404f5e7dd7Smrg 4414f5e7dd7Smrg /* 4424f5e7dd7Smrg * We are going to be doing evil things to the registers here 443cad31331Smrg * so disable them via the command register first. 4444f5e7dd7Smrg */ 4454f5e7dd7Smrg err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND ); 4464f5e7dd7Smrg if (err != 0) 4474f5e7dd7Smrg return err; 4484f5e7dd7Smrg 4494f5e7dd7Smrg err = pci_device_cfg_write_u16( dev, 4504f5e7dd7Smrg cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 4514f5e7dd7Smrg PCIR_COMMAND ); 4524f5e7dd7Smrg if (err != 0) 4534f5e7dd7Smrg return err; 4544f5e7dd7Smrg 4554f5e7dd7Smrg /* Test write all ones to the register, then restore it. */ 4564f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, 0xffffffff, bar ); 4574f5e7dd7Smrg if (err != 0) 4584f5e7dd7Smrg return err; 4594f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &testval, bar ); 4604f5e7dd7Smrg if (err != 0) 4614f5e7dd7Smrg return err; 4624f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, addr, bar ); 4634f5e7dd7Smrg if (err != 0) 4644f5e7dd7Smrg return err; 4654f5e7dd7Smrg 4664f5e7dd7Smrg /* Restore the command register */ 4674f5e7dd7Smrg err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND ); 4684f5e7dd7Smrg if (err != 0) 4694f5e7dd7Smrg return err; 4704f5e7dd7Smrg 4714f5e7dd7Smrg if (addr & 0x01) 4724f5e7dd7Smrg dev->regions[region].is_IO = 1; 4734f5e7dd7Smrg if (addr & 0x04) 4744f5e7dd7Smrg dev->regions[region].is_64 = 1; 4754f5e7dd7Smrg if (addr & 0x08) 4764f5e7dd7Smrg dev->regions[region].is_prefetchable = 1; 4774f5e7dd7Smrg 4784f5e7dd7Smrg /* Set the size */ 4794f5e7dd7Smrg dev->regions[region].size = get_test_val_size( testval ); 4804f5e7dd7Smrg printf("size = 0x%lx\n", (long)dev->regions[region].size); 4814f5e7dd7Smrg 4824f5e7dd7Smrg /* Set the base address value */ 4834f5e7dd7Smrg if (dev->regions[region].is_64) { 4844f5e7dd7Smrg uint32_t top; 4854f5e7dd7Smrg 4864f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, &top, bar + 4 ); 4874f5e7dd7Smrg if (err != 0) 4884f5e7dd7Smrg return err; 4894f5e7dd7Smrg 4904f5e7dd7Smrg dev->regions[region].base_addr = ((uint64_t)top << 32) | 4914f5e7dd7Smrg get_map_base(addr); 4924f5e7dd7Smrg } else { 4934f5e7dd7Smrg dev->regions[region].base_addr = get_map_base(addr); 4944f5e7dd7Smrg } 4954f5e7dd7Smrg 4964f5e7dd7Smrg return 0; 4974f5e7dd7Smrg} 4984f5e7dd7Smrg 4994f5e7dd7Smrgstatic int 5004f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev ) 5014f5e7dd7Smrg{ 5024f5e7dd7Smrg struct pci_device_private *priv = (struct pci_device_private *) dev; 5034f5e7dd7Smrg uint32_t reg, size; 5044f5e7dd7Smrg uint8_t irq; 5054f5e7dd7Smrg int err, i, bar; 5064f5e7dd7Smrg 5074f5e7dd7Smrg /* Many of the fields were filled in during initial device enumeration. 5084f5e7dd7Smrg * At this point, we need to fill in regions, rom_size, and irq. 5094f5e7dd7Smrg */ 5104f5e7dd7Smrg 5114f5e7dd7Smrg err = pci_device_cfg_read_u8( dev, &irq, 60 ); 5124f5e7dd7Smrg if (err) 5134f5e7dd7Smrg return errno; 5144f5e7dd7Smrg dev->irq = irq; 5154f5e7dd7Smrg 5164f5e7dd7Smrg bar = 0x10; 5174f5e7dd7Smrg for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { 5184f5e7dd7Smrg pci_device_freebsd_get_region_info( dev, i, bar ); 5194f5e7dd7Smrg if (dev->regions[i].is_64) { 5204f5e7dd7Smrg bar += 0x08; 5214f5e7dd7Smrg i++; 5224f5e7dd7Smrg } else 5234f5e7dd7Smrg bar += 0x04; 5244f5e7dd7Smrg } 5254f5e7dd7Smrg 5264f5e7dd7Smrg /* If it's a VGA device, set up the rom size for read_rom */ 5274f5e7dd7Smrg if ((dev->device_class & 0x00ffff00) == 5284f5e7dd7Smrg ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) 5294f5e7dd7Smrg { 5304f5e7dd7Smrg err = pci_device_cfg_read_u32( dev, ®, PCIR_BIOS ); 5314f5e7dd7Smrg if (err) 5324f5e7dd7Smrg return errno; 5334f5e7dd7Smrg 5344f5e7dd7Smrg if (reg == 0) { 5354f5e7dd7Smrg dev->rom_size = 0x10000; 5364f5e7dd7Smrg return 0; 5374f5e7dd7Smrg } 5384f5e7dd7Smrg 5394f5e7dd7Smrg err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS ); 5404f5e7dd7Smrg if (err) 5414f5e7dd7Smrg return errno; 5424f5e7dd7Smrg pci_device_cfg_read_u32( dev, &size, PCIR_BIOS ); 5434f5e7dd7Smrg pci_device_cfg_write_u32( dev, reg, PCIR_BIOS ); 5444f5e7dd7Smrg 5454f5e7dd7Smrg if ((reg & PCIM_BIOS_ADDR_MASK) != 0) { 5464f5e7dd7Smrg priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK); 5474f5e7dd7Smrg dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK); 5484f5e7dd7Smrg } 5494f5e7dd7Smrg } 5504f5e7dd7Smrg 5514f5e7dd7Smrg return 0; 5524f5e7dd7Smrg} 5534f5e7dd7Smrg 5544f5e7dd7Smrg#endif 5554f5e7dd7Smrg 5564f5e7dd7Smrgstatic void 5574f5e7dd7Smrgpci_system_freebsd_destroy(void) 5584f5e7dd7Smrg{ 5594f5e7dd7Smrg close(freebsd_pci_sys->pcidev); 5604f5e7dd7Smrg free(freebsd_pci_sys->pci_sys.devices); 5614f5e7dd7Smrg freebsd_pci_sys = NULL; 5624f5e7dd7Smrg} 5634f5e7dd7Smrg 5644f5e7dd7Smrgstatic const struct pci_system_methods freebsd_pci_methods = { 5654f5e7dd7Smrg .destroy = pci_system_freebsd_destroy, 5664f5e7dd7Smrg .destroy_device = NULL, /* nothing to do for this */ 5674f5e7dd7Smrg .read_rom = pci_device_freebsd_read_rom, 5684f5e7dd7Smrg .probe = pci_device_freebsd_probe, 5694f5e7dd7Smrg .map_range = pci_device_freebsd_map_range, 5704f5e7dd7Smrg .unmap_range = pci_device_freebsd_unmap_range, 5714f5e7dd7Smrg .read = pci_device_freebsd_read, 5724f5e7dd7Smrg .write = pci_device_freebsd_write, 5734f5e7dd7Smrg .fill_capabilities = pci_fill_capabilities_generic, 5744f5e7dd7Smrg}; 5754f5e7dd7Smrg 5764f5e7dd7Smrg/** 5774f5e7dd7Smrg * Attempt to access the FreeBSD PCI interface. 5784f5e7dd7Smrg */ 5794f5e7dd7Smrg_pci_hidden int 5804f5e7dd7Smrgpci_system_freebsd_create( void ) 5814f5e7dd7Smrg{ 5824f5e7dd7Smrg struct pci_conf_io pciconfio; 5834f5e7dd7Smrg struct pci_conf pciconf[255]; 5844f5e7dd7Smrg int pcidev; 5854f5e7dd7Smrg int i; 5864f5e7dd7Smrg 5874f5e7dd7Smrg /* Try to open the PCI device */ 588cad31331Smrg pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); 5894f5e7dd7Smrg if ( pcidev == -1 ) 5904f5e7dd7Smrg return ENXIO; 5914f5e7dd7Smrg 5924f5e7dd7Smrg freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) ); 5934f5e7dd7Smrg if ( freebsd_pci_sys == NULL ) { 5944f5e7dd7Smrg close( pcidev ); 5954f5e7dd7Smrg return ENOMEM; 5964f5e7dd7Smrg } 5974f5e7dd7Smrg pci_sys = &freebsd_pci_sys->pci_sys; 5984f5e7dd7Smrg 5994f5e7dd7Smrg pci_sys->methods = & freebsd_pci_methods; 6004f5e7dd7Smrg freebsd_pci_sys->pcidev = pcidev; 6014f5e7dd7Smrg 6024f5e7dd7Smrg /* Probe the list of devices known by the system */ 6034f5e7dd7Smrg bzero( &pciconfio, sizeof( struct pci_conf_io ) ); 6044f5e7dd7Smrg pciconfio.match_buf_len = sizeof(pciconf); 6054f5e7dd7Smrg pciconfio.matches = pciconf; 6064f5e7dd7Smrg 6074f5e7dd7Smrg if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) { 6084f5e7dd7Smrg free( pci_sys ); 6094f5e7dd7Smrg close( pcidev ); 6104f5e7dd7Smrg return errno; 6114f5e7dd7Smrg } 6124f5e7dd7Smrg 6134f5e7dd7Smrg if (pciconfio.status == PCI_GETCONF_ERROR ) { 6144f5e7dd7Smrg free( pci_sys ); 6154f5e7dd7Smrg close( pcidev ); 6164f5e7dd7Smrg return EINVAL; 6174f5e7dd7Smrg } 6184f5e7dd7Smrg 6194f5e7dd7Smrg /* Translate the list of devices into pciaccess's format. */ 6204f5e7dd7Smrg pci_sys->num_devices = pciconfio.num_matches; 6214f5e7dd7Smrg pci_sys->devices = calloc( pciconfio.num_matches, 6224f5e7dd7Smrg sizeof( struct pci_device_private ) ); 6234f5e7dd7Smrg 6244f5e7dd7Smrg for ( i = 0; i < pciconfio.num_matches; i++ ) { 6254f5e7dd7Smrg struct pci_conf *p = &pciconf[ i ]; 6264f5e7dd7Smrg 6274f5e7dd7Smrg#if HAVE_PCI_IO_PC_DOMAIN 6284f5e7dd7Smrg pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; 6294f5e7dd7Smrg#else 6304f5e7dd7Smrg pci_sys->devices[ i ].base.domain = 0; 6314f5e7dd7Smrg#endif 6324f5e7dd7Smrg pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; 6334f5e7dd7Smrg pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; 6344f5e7dd7Smrg pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; 6354f5e7dd7Smrg pci_sys->devices[ i ].base.vendor_id = p->pc_vendor; 6364f5e7dd7Smrg pci_sys->devices[ i ].base.device_id = p->pc_device; 6374f5e7dd7Smrg pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; 6384f5e7dd7Smrg pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; 6394f5e7dd7Smrg pci_sys->devices[ i ].base.revision = p->pc_revid; 6404f5e7dd7Smrg pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | 6414f5e7dd7Smrg (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; 6424f5e7dd7Smrg pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; 6434f5e7dd7Smrg } 6444f5e7dd7Smrg 6454f5e7dd7Smrg return 0; 6464f5e7dd7Smrg} 647