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