diff options
author | Mark Kettenis <mark.kettenis@xs4all.nl> | 2008-03-12 21:29:58 +0100 |
---|---|---|
committer | Matthieu Herrb <matthieu@reactor.herrb.net> | 2008-03-12 21:29:58 +0100 |
commit | d898072e28ac35f5b3569f48f2e90a9ef8eee2ca (patch) | |
tree | f4c193a27127a7485e5fed232b5829ea95eedb3a | |
parent | 4224ff23794500e1455f28fd5689bd0549b72367 (diff) |
OpenBSD support for libpciaccess.
xserver and libpciaccess both need to open /dev/xf86, which can only
be opened once. I implemented pci_system_init_dev_mem() like Ian
suggested. This requires some minor changes to the BSD-specific
os-support code. Since pci_system_init_dev_mem() is a no-op on
FreeBSD this should be no problem.
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | include/pciaccess.h | 2 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/common_init.c | 11 | ||||
-rw-r--r-- | src/common_interface.c | 10 | ||||
-rw-r--r-- | src/common_map.c | 1 | ||||
-rw-r--r-- | src/openbsd_pci.c | 406 | ||||
-rw-r--r-- | src/pciaccess_private.h | 2 |
8 files changed, 437 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index 8f4e55d..6163577 100644 --- a/configure.ac +++ b/configure.ac @@ -77,6 +77,9 @@ case $host_os in *linux*) linux=yes ;; + *openbsd*) + openbsd=yes + ;; *solaris*) solaris=yes PCIACCESS_LIBS="$PCIACCESS_LIBS -ldevinfo" @@ -85,6 +88,7 @@ esac AM_CONDITIONAL(LINUX, [test "x$linux" = xyes]) AM_CONDITIONAL(FREEBSD, [test "x$freebsd" = xyes]) +AM_CONDITIONAL(OPENBSD, [test "x$openbsd" = xyes]) AM_CONDITIONAL(SOLARIS, [test "x$solaris" = xyes]) AC_CHECK_FILE([/usr/include/asm/mtrr.h], diff --git a/include/pciaccess.h b/include/pciaccess.h index dcc0122..3138877 100644 --- a/include/pciaccess.h +++ b/include/pciaccess.h @@ -81,6 +81,8 @@ int pci_device_get_bridge_buses(struct pci_device *dev, int *primary_bus, int pci_system_init(void); +void pci_system_init_dev_mem(int fd); + void pci_system_cleanup(void); struct pci_device_iterator *pci_slot_match_iterator_create( diff --git a/src/Makefile.am b/src/Makefile.am index f370920..f60ccbd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,6 +33,10 @@ if FREEBSD OS_SUPPORT = freebsd_pci.c endif +if OPENBSD +OS_SUPPORT = openbsd_pci.c +endif + if SOLARIS OS_SUPPORT = solx_devfs.c pci_tools.h endif diff --git a/src/common_init.c b/src/common_init.c index 1092faf..7429518 100644 --- a/src/common_init.c +++ b/src/common_init.c @@ -56,13 +56,22 @@ pci_system_init( void ) err = pci_system_linux_sysfs_create(); #elif defined(__FreeBSD__) err = pci_system_freebsd_create(); +#elif defined(__OpenBSD__) + err = pci_system_openbsd_create(); #elif defined(__sun) - err = pci_system_solx_devfs_create(); + err = pci_system_solx_devfs_create(); #endif return err; } +void +pci_system_init_dev_mem(int fd) +{ +#ifdef __OpenBSD__ + pci_system_openbsd_init_dev_mem(fd); +#endif +} /** * Shutdown all access to the PCI subsystem. diff --git a/src/common_interface.c b/src/common_interface.c index 7fae277..d7e4b62 100644 --- a/src/common_interface.c +++ b/src/common_interface.c @@ -61,11 +61,17 @@ #include <sys/endian.h> -#define LETOH_16(x) le16toh(x) #define HTOLE_16(x) htole16(x) -#define LETOH_32(x) le32toh(x) #define HTOLE_32(x) htole32(x) +#ifdef __FreeBSD__ +#define LETOH_16(x) le16toh(x) +#define LETOH_32(x) le32toh(x) +#else +#define LETOH_16(x) letoh16(x) +#define LETOH_32(x) letoh32(x) +#endif + #endif /* others */ /** diff --git a/src/common_map.c b/src/common_map.c index ac1c668..8757151 100644 --- a/src/common_map.c +++ b/src/common_map.c @@ -22,6 +22,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include <sys/types.h> #include <sys/mman.h> #include <errno.h> diff --git a/src/openbsd_pci.c b/src/openbsd_pci.c new file mode 100644 index 0000000..5c06b47 --- /dev/null +++ b/src/openbsd_pci.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2008 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/memrange.h> +#include <sys/mman.h> +#include <sys/pciio.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pciaccess.h" +#include "pciaccess_private.h" + +static int pcifd; +static int aperturefd = -1; + +static int +pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val) +{ + struct pci_io io; + int err; + + bzero(&io, sizeof(io)); + io.pi_sel.pc_bus = bus; + io.pi_sel.pc_dev = dev; + io.pi_sel.pc_func = func; + io.pi_reg = reg; + io.pi_width = 4; + + err = ioctl(pcifd, PCIOCREAD, &io); + if (err) + return (err); + + *val = io.pi_data; + + return (0); +} + +static int +pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val) +{ + struct pci_io io; + + bzero(&io, sizeof(io)); + io.pi_sel.pc_bus = bus; + io.pi_sel.pc_dev = dev; + io.pi_sel.pc_func = func; + io.pi_reg = reg; + io.pi_width = 4; + io.pi_data = val; + + return ioctl(pcifd, PCIOCWRITE, &io); +} + +static int +pci_nfuncs(int bus, int dev) +{ + uint32_t hdr; + + if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) + return -1; + + return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); +} + +static int +pci_device_openbsd_map_range(struct pci_device *dev, + struct pci_device_mapping *map) +{ + struct mem_range_desc mr; + struct mem_range_op mo; + int prot = PROT_READ; + + if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) + prot |= PROT_WRITE; + + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, aperturefd, + map->base); + if (map->memory == MAP_FAILED) + return errno; + + /* No need to set an MTRR if it's the default mode. */ + if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || + (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { + mr.mr_base = map->base; + mr.mr_len = map->size; + mr.mr_flags = 0; + if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) + mr.mr_flags |= MDF_WRITEBACK; + if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) + mr.mr_flags |= MDF_WRITECOMBINE; + strlcpy(mr.mr_owner, "pciaccess", sizeof(mr.mr_owner)); + + mo.mo_desc = &mr; + mo.mo_arg[0] = MEMRANGE_SET_UPDATE; + + if (ioctl(aperturefd, MEMRANGE_SET, &mo)) + return errno; + } + + return 0; +} + +static int +pci_device_openbsd_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map) +{ + struct mem_range_desc mr; + struct mem_range_op mo; + + if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || + (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { + mr.mr_base = map->base; + mr.mr_len = map->size; + mr.mr_flags = MDF_UNCACHEABLE; + strlcpy(mr.mr_owner, "pciaccess", sizeof(mr.mr_owner)); + + mo.mo_desc = &mr; + mo.mo_arg[0] = MEMRANGE_SET_REMOVE; + + (void)ioctl(aperturefd, MEMRANGE_SET, &mo); + } + + return pci_device_generic_unmap_range(dev, map); +} + +static int +pci_device_openbsd_read(struct pci_device *dev, void *data, + pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) +{ + struct pci_io io; + + io.pi_sel.pc_bus = dev->bus; + io.pi_sel.pc_dev = dev->dev; + io.pi_sel.pc_func = dev->func; + + *bytes_read = 0; + while (size > 0) { + int toread = MIN(size, 4 - (offset & 0x3)); + + io.pi_reg = (offset & ~0x3); + io.pi_width = 4; + + if (ioctl(pcifd, PCIOCREAD, &io) == -1) + return errno; + + io.pi_data = htole32(io.pi_data); + io.pi_data >>= ((offset & 0x3) * 8); + + memcpy(data, &io.pi_data, toread); + + offset += toread; + data = (char *)data + toread; + size -= toread; + *bytes_read += toread; + } + + return 0; +} + +static int +pci_device_openbsd_write(struct pci_device *dev, const void *data, + pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) +{ + struct pci_io io; + + if ((offset % 4) == 0 || (size % 4) == 0) + return EINVAL; + + io.pi_sel.pc_bus = dev->bus; + io.pi_sel.pc_dev = dev->dev; + io.pi_sel.pc_func = dev->func; + + *bytes_written = 0; + while (size > 0) { + io.pi_reg = offset; + io.pi_width = 4; + memcpy(&io.pi_data, data, 4); + + if (ioctl(pcifd, PCIOCWRITE, &io) == -1) + return errno; + + offset += 4; + data = (char *)data + 4; + size -= 4; + *bytes_written += 4; + } + + return 0; +} + +static void +pci_system_openbsd_destroy(void) +{ + close(aperturefd); + close(pcifd); + free(pci_sys); + pci_sys = NULL; +} + +static int +pci_device_openbsd_probe(struct pci_device *device) +{ + struct pci_device_private *priv = (struct pci_device_private *)device; + struct pci_mem_region *region; + uint64_t reg64, size64; + uint32_t bar, reg, size; + int bus, dev, func, err; + + bus = device->bus; + dev = device->dev; + func = device->func; + + err = pci_read(bus, dev, func, PCI_BHLC_REG, ®); + if (err) + return err; + + priv->header_type = PCI_HDRTYPE_TYPE(reg); + if (priv->header_type != 0) + return 0; + + region = device->regions; + for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; + bar += sizeof(uint32_t), region++) { + err = pci_read(bus, dev, func, bar, ®); + if (err) + return err; + + /* Probe the size of the region. */ + err = pci_write(bus, dev, func, bar, ~0); + if (err) + return err; + pci_read(bus, dev, func, bar, &size); + pci_write(bus, dev, func, bar, reg); + + if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { + region->is_IO = 1; + region->base_addr = PCI_MAPREG_IO_ADDR(reg); + region->size = PCI_MAPREG_IO_SIZE(size); + } else { + if (PCI_MAPREG_MEM_PREFETCHABLE(reg)) + region->is_prefetchable = 1; + switch(PCI_MAPREG_MEM_TYPE(reg)) { + case PCI_MAPREG_MEM_TYPE_32BIT: + case PCI_MAPREG_MEM_TYPE_32BIT_1M: + region->base_addr = PCI_MAPREG_MEM_ADDR(reg); + region->size = PCI_MAPREG_MEM_SIZE(size); + break; + case PCI_MAPREG_MEM_TYPE_64BIT: + region->is_64 = 1; + + reg64 = reg; + size64 = size; + + bar += sizeof(uint32_t); + + err = pci_read(bus, dev, func, bar, ®); + if (err) + return err; + reg64 |= (uint64_t)reg << 32; + + err = pci_write(bus, dev, func, bar, ~0); + if (err) + return err; + pci_read(bus, dev, func, bar, &size); + pci_write(bus, dev, func, bar, reg64 >> 32); + size64 |= (uint64_t)size << 32; + + region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64); + region->size = PCI_MAPREG_MEM64_SIZE(size64); + region++; + break; + } + } + } + + return 0; +} + +static const struct pci_system_methods openbsd_pci_methods = { + pci_system_openbsd_destroy, + NULL, + NULL, + pci_device_openbsd_probe, + pci_device_openbsd_map_range, + pci_device_openbsd_unmap_range, + pci_device_openbsd_read, + pci_device_openbsd_write, + pci_fill_capabilities_generic +}; + +int +pci_system_openbsd_create(void) +{ + struct pci_device_private *device; + int bus, dev, func, ndevs, nfuncs; + uint32_t reg; + + pcifd = open("/dev/pci", O_RDWR); + if (pcifd == -1) + return ENXIO; + + pci_sys = calloc(1, sizeof(struct pci_system)); + if (pci_sys == NULL) { + close(aperturefd); + close(pcifd); + return ENOMEM; + } + + pci_sys->methods = &openbsd_pci_methods; + + ndevs = 0; + for (bus = 0; bus < 256; bus++) { + for (dev = 0; dev < 32; dev++) { + nfuncs = pci_nfuncs(bus, dev); + for (func = 0; func < nfuncs; func++) { + if (pci_read(bus, dev, func, PCI_ID_REG, + ®) != 0) + continue; + if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || + PCI_VENDOR(reg) == 0) + continue; + + ndevs++; + } + } + } + + pci_sys->num_devices = ndevs; + pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); + if (pci_sys->devices == NULL) { + free(pci_sys); + close(pcifd); + return ENOMEM; + } + + device = pci_sys->devices; + for (bus = 0; bus < 256; bus++) { + for (dev = 0; dev < 32; dev++) { + nfuncs = pci_nfuncs(bus, dev); + for (func = 0; func < nfuncs; func++) { + if (pci_read(bus, dev, func, PCI_ID_REG, + ®) != 0) + continue; + if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || + PCI_VENDOR(reg) == 0) + continue; + + device->base.domain = 0; + device->base.bus = bus; + device->base.dev = dev; + device->base.func = func; + device->base.vendor_id = PCI_VENDOR(reg); + device->base.device_id = PCI_PRODUCT(reg); + + if (pci_read(bus, dev, func, PCI_CLASS_REG, + ®) != 0) + continue; + + device->base.device_class = + PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 | + PCI_SUBCLASS(reg) << 8; + device->base.revision = PCI_REVISION(reg); + + if (pci_read(bus, dev, func, PCI_SUBVEND_0, + ®) != 0) + continue; + + device->base.subvendor_id = PCI_VENDOR(reg); + device->base.subdevice_id = PCI_PRODUCT(reg); + + device++; + } + } + } + + return 0; +} + +void +pci_system_openbsd_init_dev_mem(int fd) +{ + aperturefd = fd; +} diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index c8a483e..94b12bd 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -135,3 +135,5 @@ extern struct pci_system * pci_sys; extern int pci_system_linux_sysfs_create( void ); extern int pci_system_freebsd_create( void ); +extern int pci_system_openbsd_create( void ); +extern void pci_system_openbsd_init_dev_mem( int ); |