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, &reg, 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, &reg, 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