././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1711234816.7986476 libpciaccess-0.18.1/0000755014310600000120000000000014577657401013240 5ustar00alancstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/.gitignore0000664014310600000120000000175514577654164015245 0ustar00alancstaff# # X.Org module default exclusion patterns # The next section if for module specific patterns # # Do not edit the following section # GNU Build System (Autotools) aclocal.m4 autom4te.cache/ autoscan.log ChangeLog compile config.guess config.h config.h.in config.log config-ml.in config.py config.status config.status.lineno config.sub configure configure.scan depcomp .deps/ INSTALL install-sh .libs/ libtool libtool.m4 ltmain.sh lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 Makefile Makefile.in mdate-sh missing mkinstalldirs *.pc py-compile stamp-h? symlink-tree texinfo.tex ylwrap # Do not edit the following section # Edit Compile Debug Document Distribute *~ *.[0-9] *.[0-9]x *.bak *.bin core *.dll *.exe *-ISO*.bdf *-JIS*.bdf *-KOI8*.bdf *.kld *.ko *.ko.cmd *.lai *.l[oa] *.[oa] *.obj *.patch *.so *.pcf.gz *.pdb *.tar.bz2 *.tar.gz # # Add & Override patterns for libpciaccess # # Edit the following section as needed # For example, !report.pc overrides *.pc. See 'man gitignore' # ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/.gitlab-ci.yml0000664014310600000120000000503514577654164015704 0ustar00alancstaff# vim: set expandtab shiftwidth=2 tabstop=8 textwidth=0 filetype=yaml: # # This CI uses the freedesktop.org ci-templates. # Please see the ci-templates documentation for details: # https://freedesktop.pages.freedesktop.org/ci-templates/ .templates_sha: &template_sha 185ede0e9b9b1924b92306ab8b882a6294e92613 # see https://docs.gitlab.com/ee/ci/yaml/#includefile include: # Arch container builder template - project: 'freedesktop/ci-templates' ref: *template_sha file: '/templates/arch.yml' - project: 'freedesktop/ci-templates' ref: *template_sha file: '/templates/ci-fairy.yml' - template: Security/SAST.gitlab-ci.yml stages: - prep # prep work like rebuilding the container images if there is a change - build # for actually building and testing things in a container - test - deploy variables: FDO_UPSTREAM_REPO: 'xorg/lib/libpciaccess' # The tag should be updated each time the list of packages is updated. # Changing a tag forces the associated image to be rebuilt. # Note: the tag has no meaning, we use a date format purely for readability FDO_DISTRIBUTION_TAG: '2023-10-17.1' FDO_DISTRIBUTION_PACKAGES: 'git meson ninja gcc pkgconf zlib' # # Verify that commit messages are as expected, signed-off, etc. # check-commits: extends: - .fdo.ci-fairy stage: prep script: - ci-fairy check-commits --junit-xml=results.xml except: - master@xorg/lib/libpciaccess variables: GIT_DEPTH: 100 artifacts: reports: junit: results.xml # # Verify that the merge request has the allow-collaboration checkbox ticked # check-merge-request: extends: - .fdo.ci-fairy stage: deploy script: - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml artifacts: when: on_failure reports: junit: results.xml allow_failure: true # # Build a container with the given tag and the packages pre-installed. # This only happens if/when the tag changes, otherwise the existing image is # re-used. # container-prep: extends: - .fdo.container-build@arch stage: prep variables: GIT_STRATEGY: none # # Builds run on the image built above. # meson: extends: - .fdo.distribution-image@arch stage: build parallel: matrix: - MESON_OPTIONS: ['-Dzlib=disabled', '-Dzlib=enabled'] script: - mkdir -p ../_inst - meson setup builddir --prefix="$PWD/../_inst" -Dwarning_level=3 -Ddefault_library=both $MESON_OPTIONS - meson configure builddir - ninja -C builddir test - ninja -C builddir install ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/AUTHORS0000664014310600000120000000002414577654164014311 0ustar00alancstaffIan Romanick of IBM ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/COPYING0000664014310600000120000001135514577654164014305 0ustar00alancstaff(C) Copyright IBM Corporation 2006, 2007 (C) Copyright Eric Anholt 2006 (C) Copyright Mark Kettenis 2011 (C) Copyright Robert Millan 2012 Copyright (c) 2007, 2008, 2009, 2011, 2012, 2016 Oracle and/or its affiliates. Copyright 2009, 2012 Red Hat, Inc. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation on the rights to use, copy, modify, merge, publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ Copyright (c) 2008 Juan Romero Pardines Copyright (c) 2008, 2011 Mark Kettenis Copyright (c) 2009 Michael Lorenz Copyright (c) 2009, 2012 Samuel Thibault 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. ------------------------------------------------------------------------------ Copyright (C) 2000 The XFree86 Project, Inc. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the XFree86 Project shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the XFree86 Project. ------------------------------------------------------------------------------ Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti Copyright (c) 2009 Tiago Vignatti Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/README.cygwin0000664014310600000120000000126314577654164015426 0ustar00alancstaff= libpciaccess support under Windows through WinIo and Cygwin = == Platforms supported == The support should work on Windows NT/2000/XP/2003/Vista/7 and 2008 but has only been tested on Windows 7, 32 bits == Dependencies == This support depends of WinIo which allows direct I/O port and physical memory access under Windows NT/2000/XP/2003/Vista/7 and 2008. == How to install WinIo ? == First, you need to download WinIo from http://www.internals.com/. Then, you have to copy WinIo32.dll and WinIo32.sys to the same directory as the executables. == TODO == Check and fix 64 bits support. == Contact == If you have any problems, please send an email to samuel.pitoiset at gmail.com ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/README.md0000664014310600000120000000152114577654164014523 0ustar00alancstaffxorg/lib/libpciaccess - Generic PCI access library -------------------------------------------------- Documentation of the libpciaccess API's can be generated from the sources via the doxygen command. Information about porting Xorg drivers to libpciaccess is located at: https://www.x.org/wiki/PciReworkHowto For historical reference, the original proposal for this work is at: https://www.x.org/wiki/PciReworkProposal All questions regarding this software should be directed at the Xorg mailing list: https://lists.x.org/mailman/listinfo/xorg The primary development code repository can be found at: https://gitlab.freedesktop.org/xorg/lib/libpciaccess Please submit bug reports and requests to merge patches there. For patch submission instructions, see: https://www.x.org/wiki/Development/Documentation/SubmittingPatches ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/include/0000775014310600000120000000000014577654164014670 5ustar00alancstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/include/pciaccess.h0000664014310600000120000003622014577654164017001 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * Copyright 2009 Red Hat, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ /** * \file pciaccess.h * * \author Ian Romanick */ #ifndef PCIACCESS_H #define PCIACCESS_H #include #if (__GNUC__ >= 3) || (__SUNPRO_C >= 0x5130) #define __deprecated __attribute__((deprecated)) #else #define __deprecated #endif typedef uint64_t pciaddr_t; struct pci_device; struct pci_device_iterator; struct pci_id_match; struct pci_slot_match; #ifdef __cplusplus extern "C" { #endif int pci_device_has_kernel_driver(struct pci_device *dev); int pci_device_is_boot_vga(struct pci_device *dev); int pci_device_read_rom(struct pci_device *dev, void *buffer); int __deprecated pci_device_map_region(struct pci_device *dev, unsigned region, int write_enable); int __deprecated pci_device_unmap_region(struct pci_device *dev, unsigned region); int pci_device_map_range(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr); int pci_device_unmap_range(struct pci_device *dev, void *memory, pciaddr_t size); int __deprecated pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base, pciaddr_t size, int write_enable, void **addr); int __deprecated pci_device_unmap_memory_range(struct pci_device *dev, void *memory, pciaddr_t size); int pci_device_probe(struct pci_device *dev); const struct pci_agp_info *pci_device_get_agp_info(struct pci_device *dev); const struct pci_bridge_info *pci_device_get_bridge_info( struct pci_device *dev); const struct pci_pcmcia_bridge_info *pci_device_get_pcmcia_bridge_info( struct pci_device *dev); int pci_device_get_bridge_buses(struct pci_device *dev, int *primary_bus, int *secondary_bus, int *subordinate_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( const struct pci_slot_match *match); struct pci_device_iterator *pci_id_match_iterator_create( const struct pci_id_match *match); void pci_iterator_destroy(struct pci_device_iterator *iter); struct pci_device *pci_device_next(struct pci_device_iterator *iter); struct pci_device *pci_device_find_by_slot(uint32_t domain, uint32_t bus, uint32_t dev, uint32_t func); struct pci_device *pci_device_get_parent_bridge(struct pci_device *dev); void pci_get_strings(const struct pci_id_match *m, const char **device_name, const char **vendor_name, const char **subdevice_name, const char **subvendor_name); const char *pci_device_get_device_name(const struct pci_device *dev); const char *pci_device_get_subdevice_name(const struct pci_device *dev); const char *pci_device_get_vendor_name(const struct pci_device *dev); const char *pci_device_get_subvendor_name(const struct pci_device *dev); void pci_device_enable(struct pci_device *dev); void pci_device_disable(struct pci_device *dev); int pci_device_cfg_read (struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read); int pci_device_cfg_read_u8 (struct pci_device *dev, uint8_t *data, pciaddr_t offset); int pci_device_cfg_read_u16(struct pci_device *dev, uint16_t *data, pciaddr_t offset); int pci_device_cfg_read_u32(struct pci_device *dev, uint32_t *data, pciaddr_t offset); int pci_device_cfg_write (struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written); int pci_device_cfg_write_u8 (struct pci_device *dev, uint8_t data, pciaddr_t offset); int pci_device_cfg_write_u16(struct pci_device *dev, uint16_t data, pciaddr_t offset); int pci_device_cfg_write_u32(struct pci_device *dev, uint32_t data, pciaddr_t offset); int pci_device_cfg_write_bits(struct pci_device *dev, uint32_t mask, uint32_t data, pciaddr_t offset); #ifdef __cplusplus } #endif /** * \name Mapping flags passed to \c pci_device_map_range */ /*@{*/ #define PCI_DEV_MAP_FLAG_WRITABLE (1U<<0) #define PCI_DEV_MAP_FLAG_WRITE_COMBINE (1U<<1) #define PCI_DEV_MAP_FLAG_CACHABLE (1U<<2) /*@}*/ #define PCI_MATCH_ANY (~0U) /** * Compare two PCI ID values (either vendor or device). This is used * internally to compare the fields of \c pci_id_match to the fields of * \c pci_device. */ #define PCI_ID_COMPARE(a, b) \ (((a) == PCI_MATCH_ANY) || ((a) == (b))) /** */ struct pci_id_match { /** * \name Device / vendor matching controls * * Control the search based on the device, vendor, subdevice, or subvendor * IDs. Setting any of these fields to \c PCI_MATCH_ANY will cause the * field to not be used in the comparison. */ /*@{*/ uint32_t vendor_id; uint32_t device_id; uint32_t subvendor_id; uint32_t subdevice_id; /*@}*/ /** * \name Device class matching controls * */ /*@{*/ uint32_t device_class; uint32_t device_class_mask; /*@}*/ intptr_t match_data; }; /** */ struct pci_slot_match { /** * \name Device slot matching controls * * Control the search based on the domain, bus, slot, and function of * the device. Setting any of these fields to \c PCI_MATCH_ANY will cause * the field to not be used in the comparison. */ /*@{*/ uint32_t domain; uint32_t bus; uint32_t dev; uint32_t func; /*@}*/ intptr_t match_data; }; /** * BAR descriptor for a PCI device. */ struct pci_mem_region { /** * When the region is mapped, this is the pointer to the memory. * * This field is \b only set when the deprecated \c pci_device_map_region * interface is used. Use \c pci_device_map_range instead. * * \deprecated */ void *memory; /** * Base physical address of the region within its bus / domain. * * \warning * This address is really only useful to other devices in the same * domain. It's probably \b not the address applications will ever * use. * * \warning * Most (all?) platform back-ends leave this field unset. */ pciaddr_t bus_addr; /** * Base physical address of the region from the CPU's point of view. * * This address is typically passed to \c pci_device_map_range to create * a mapping of the region to the CPU's virtual address space. */ pciaddr_t base_addr; /** * Size, in bytes, of the region. */ pciaddr_t size; /** * Is the region I/O ports or memory? */ unsigned is_IO:1; /** * Is the memory region prefetchable? * * \note * This can only be set if \c is_IO is not set. */ unsigned is_prefetchable:1; /** * Is the memory at a 64-bit address? * * \note * This can only be set if \c is_IO is not set. */ unsigned is_64:1; }; /** * PCI device. * * Contains all of the information about a particular PCI device. * * This structure - like everything else in libpciaccess - is allocated * by the library itself. Do not embed this structure in other structs, * or otherwise allocate them yourself. */ struct pci_device { /** * \name Device bus identification. * * Complete bus identification, including domain, of the device. On * platforms that do not support PCI domains (e.g., 32-bit x86 hardware), * the domain will always be zero. * * The domain_16 field is provided for binary compatibility with older * libpciaccess. */ /*@{*/ uint16_t domain_16; uint8_t bus; uint8_t dev; uint8_t func; /*@}*/ /** * \name Vendor / device ID * * The vendor ID, device ID, and sub-IDs for the device. */ /*@{*/ uint16_t vendor_id; uint16_t device_id; uint16_t subvendor_id; uint16_t subdevice_id; /*@}*/ /** * Device's class, subclass, and programming interface packed into a * single 32-bit value. The class is at bits [23:16], subclass is at * bits [15:8], and programming interface is at [7:0]. */ uint32_t device_class; /** * Device revision number, as read from the configuration header. */ uint8_t revision; /** * BAR descriptors for the device. */ struct pci_mem_region regions[6]; /** * Size, in bytes, of the device's expansion ROM. */ pciaddr_t rom_size; /** * IRQ associated with the device. If there is no IRQ, this value will * be -1. */ int irq; /** * Storage for user data. Users of the library can store arbitrary * data in this pointer. The library will not use it for any purpose. * It is the user's responsibility to free this memory before destroying * the \c pci_device structure. */ intptr_t user_data; /** * Used by the VGA arbiter. Type of resource decoded by the device and * the file descriptor (/dev/vga_arbiter). */ int vgaarb_rsrc; /** * PCI domain value (full 32 bits) */ uint32_t domain; }; /** * Description of the AGP capability of the device. * * \sa pci_device_get_agp_info */ struct pci_agp_info { /** * Offset of the AGP registers in the devices configuration register * space. This is generally used so that the offset of the AGP command * register can be determined. */ unsigned config_offset; /** * \name AGP major / minor version. */ /*@{*/ uint8_t major_version; uint8_t minor_version; /*@}*/ /** * Logical OR of the supported AGP rates. For example, a value of 0x07 * means that the device can support 1x, 2x, and 4x. A value of 0x0c * means that the device can support 8x and 4x. */ uint8_t rates; unsigned int fast_writes:1; /**< Are fast-writes supported? */ unsigned int addr64:1; unsigned int htrans:1; unsigned int gart64:1; unsigned int coherent:1; unsigned int sideband:1; /**< Is side-band addressing supported? */ unsigned int isochronus:1; uint8_t async_req_size; uint8_t calibration_cycle_timing; uint8_t max_requests; }; /** * Description of a PCI-to-PCI bridge device. * * \sa pci_device_get_bridge_info */ struct pci_bridge_info { uint8_t primary_bus; uint8_t secondary_bus; uint8_t subordinate_bus; uint8_t secondary_latency_timer; uint8_t io_type; uint8_t mem_type; uint8_t prefetch_mem_type; uint16_t secondary_status; uint16_t bridge_control; uint32_t io_base; uint32_t io_limit; uint32_t mem_base; uint32_t mem_limit; uint64_t prefetch_mem_base; uint64_t prefetch_mem_limit; }; /** * Description of a PCI-to-PCMCIA bridge device. * * \sa pci_device_get_pcmcia_bridge_info */ struct pci_pcmcia_bridge_info { uint8_t primary_bus; uint8_t card_bus; uint8_t subordinate_bus; uint8_t cardbus_latency_timer; uint16_t secondary_status; uint16_t bridge_control; struct { uint32_t base; uint32_t limit; } io[2]; struct { uint32_t base; uint32_t limit; } mem[2]; }; /** * VGA Arbiter definitions, functions and related. */ /* Legacy VGA regions */ #define VGA_ARB_RSRC_NONE 0x00 #define VGA_ARB_RSRC_LEGACY_IO 0x01 #define VGA_ARB_RSRC_LEGACY_MEM 0x02 /* Non-legacy access */ #define VGA_ARB_RSRC_NORMAL_IO 0x04 #define VGA_ARB_RSRC_NORMAL_MEM 0x08 int pci_device_vgaarb_init (void); void pci_device_vgaarb_fini (void); int pci_device_vgaarb_set_target (struct pci_device *dev); /* use the targeted device */ int pci_device_vgaarb_decodes (int new_vga_rsrc); int pci_device_vgaarb_lock (void); int pci_device_vgaarb_trylock (void); int pci_device_vgaarb_unlock (void); /* return the current device count + resource decodes for the device */ int pci_device_vgaarb_get_info (struct pci_device *dev, int *vga_count, int *rsrc_decodes); /* * I/O space access. */ struct pci_io_handle; struct pci_io_handle *pci_device_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size); struct pci_io_handle *pci_legacy_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size); void pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle); uint32_t pci_io_read32(struct pci_io_handle *handle, uint32_t reg); uint16_t pci_io_read16(struct pci_io_handle *handle, uint32_t reg); uint8_t pci_io_read8(struct pci_io_handle *handle, uint32_t reg); void pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data); void pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data); void pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data); /* * Legacy memory access */ int pci_device_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr); int pci_device_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size); #endif /* PCIACCESS_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/man/0000775014310600000120000000000014577654164014020 5ustar00alancstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/man/meson.build0000664014310600000120000000255414577654164016170 0ustar00alancstaff# Copyright © 2020 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. custom_target( 'scanpci.1', input : 'scanpci.man', output : 'scanpci.1', command : [ find_program('sed'), '-e', 's/__xorgversion__/X Version 11/', '@INPUT@', ], capture : true, install : false, install_dir : join_paths(get_option('prefix'), get_option('mandir'), 'man1'), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/man/scanpci.man0000664014310600000120000000357414577654164016146 0ustar00alancstaff.\" Copyright (C) 2000 The XFree86 Project, Inc. All Rights Reserved. .\" .\" Permission is hereby granted, free of charge, to any person obtaining a copy .\" of this software and associated documentation files (the "Software"), to .\" deal in the Software without restriction, including without limitation the .\" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or .\" sell copies of the Software, and to permit persons to whom the Software is .\" furnished to do so, subject to the following conditions: .\" .\" The above copyright notice and this permission notice shall be included in .\" all copies or substantial portions of the Software. .\" .\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR .\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, .\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE .\" XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER .\" IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN .\" CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .\" .\" Except as contained in this notice, the name of the XFree86 Project shall .\" not be used in advertising or otherwise to promote the sale, use or other .\" dealings in this Software without prior written authorization from the .\" XFree86 Project. .\" .TH SCANPCI 1 __xorgversion__ .SH NAME scanpci - scan/probe PCI buses .SH SYNOPSIS .B scanpci .RB [ \-v ] .SH DESCRIPTION .I Scanpci is a utility that can be used to scan PCI buses and report information about the configuration space settings for each PCI device. On most platforms, .I scanpci can only be run by the root user. .SH OPTIONS .TP 8 .B \-v Print the configuration space information for each device in a verbose format. Without this option, only a brief description is printed for each device. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/meson.build0000664014310600000120000000735214577654164015416 0ustar00alancstaff# Copyright © 2018-2020 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. project( 'libpciaccess', ['c'], version : '0.18.1', license : 'MIT', meson_version : '>= 0.48.0', default_options : ['buildtype=debugoptimized'], ) c = meson.get_compiler('c') add_project_arguments( c.get_supported_arguments( '-Wpointer-arith', '-Wmissing-declarations', '-Wformat=2', '-Wstrict-prototypes', '-Wmissing-prototypes', '-Wnested-externs', '-Wbad-function-cast', '-Wold-style-definition', '-Wdeclaration-after-statement', '-Wunused', '-Wuninitialized', '-Wshadow', '-Wmissing-noreturn', '-Wmissing-format-attribute', '-Wredundant-decls', '-Wlogical-op', '-Werror=implicit', '-Werror=nonnull', '-Werror=init-self', '-Werror=main', '-Werror=missing-braces', '-Werror=sequence-point', '-Werror=return-type', '-Werror=trigraphs', '-Werror=array-bounds', '-Werror=write-strings', '-Werror=address', '-Werror=int-to-pointer-cast', ), language : 'c' ) config = configuration_data() config.set_quoted( 'PCIIDS_PATH', join_paths(get_option('prefix'), get_option('datadir'), get_option('pci-ids')) ) if get_option('linux-rom-fallback') config.set('LINUX_ROM', 1) endif dep_zlib = dependency('zlib', required : get_option('zlib')) if dep_zlib.found() config.set('HAVE_ZLIB', 1) endif extra_libs = [] if host_machine.system() == 'netbsd' extra_libs += c.find_library('pci') if host_machine.cpu_family() == 'x86' extra_libs += c.find_library('i386') elif host_machine.cpu_family() == 'x86_64' extra_libs += c.find_library('x86_64') elif host_machine.cpu_family() == 'alpha' # TODO extra_libs += c.find_library('alpha') endif elif host_machine.system() == 'sunos' extra_libs += c.find_library('devinfo') endif if host_machine.system() == 'netbsd' _prefix = '' if c.check_header('machine/sysarch.h') _prefix = ''' #include #include ''' endif if c.check_header('machine/mtrr.h', prefix : _prefix) config.set('HAVE_MTRR', 1) endif elif c.check_header('asm/mtrr.h') config.set('HAVE_MTRR', 1) endif foreach h : ['err.h', 'stdint.h', 'string.h', 'strings.h', 'inttypes.h'] if c.check_header(h) config.set('HAVE_' + h.to_upper().underscorify(), 1) endif endforeach config_h = configure_file( configuration : config, output : 'config.h', ) add_project_arguments('-DHAVE_CONFIG_H', language : ['c']) install_headers('include/pciaccess.h') inc_include = include_directories('.', 'include') subdir('src') subdir('scanpci') subdir('man') pkg = import('pkgconfig') pkg.generate( libpciaccess, description : 'Library providing generic access to the PCI bus and devices', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/meson_options.txt0000664014310600000120000000272314577654164016706 0ustar00alancstaff# Copyright © 2020 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. option( 'pci-ids', type : 'string', value : 'hwdata', description : 'path to pci ids. If relative is assumed relative to $datadir.', ) option( 'linux-rom-fallback', type : 'boolean', value : false, description : 'Enable support for falling back to /dev/mem for roms', ) option( 'zlib', type : 'feature', description : 'Enable zlib support to read gzip compressed pci.ids.', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/pciaccess.pc.in0000664014310600000120000000043514577654164016135 0ustar00alancstaffprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: pciaccess Description: Library providing generic access to the PCI bus and devices. Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lpciaccess Libs.Private: @PCIACCESS_LIBS@ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/scanpci/0000775014310600000120000000000014577654164014665 5ustar00alancstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/scanpci/.gitignore0000664014310600000120000000001014577654164016644 0ustar00alancstaffscanpci ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/scanpci/meson.build0000664014310600000120000000224014577654164017025 0ustar00alancstaff# Copyright © 2018-2020 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. scanpci = executable( 'scanpci', 'scanpci.c', dependencies : [dep_pciaccess], ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/scanpci/scanpci.c0000664014310600000120000001451414577654164016456 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_ERR_H #include #else # include # include # define err(exitcode, format, args...) \ errx(exitcode, format ": %s", ## args, strerror(errno)) # define errx(exitcode, format, args...) \ { warnx(format, ## args); exit(exitcode); } # define warn(format, args...) \ warnx(format ": %s", ## args, strerror(errno)) # define warnx(format, args...) \ fprintf(stderr, format "\n", ## args) #endif #include "pciaccess.h" #include "pciaccess_private.h" static void print_pci_bridge( const struct pci_bridge_info * info ) { printf( " Bus: primary=%02"PRIx8", secondary=%02"PRIx8", subordinate=%02"PRIx8", " "sec-latency=%"PRIu8"\n", info->primary_bus, info->secondary_bus, info->subordinate_bus, info->secondary_latency_timer ); printf( " I/O behind bridge: %08"PRIx32"-%08"PRIx32"\n", info->io_base, info->io_limit ); printf( " Memory behind bridge: %08"PRIx32"-%08"PRIx32"\n", info->mem_base, info->mem_limit ); printf( " Prefetchable memory behind bridge: %08"PRIx64"-%08"PRIx64"\n", info->prefetch_mem_base, info->prefetch_mem_limit ); } static void print_pci_device( struct pci_device * dev, int verbose ) { const char * dev_name; const char * vend_name; vend_name = pci_device_get_vendor_name( dev ); dev_name = pci_device_get_device_name( dev ); if ( dev_name == NULL ) { dev_name = "Device unknown"; } printf("\npci "); if (dev->domain != 0) printf("domain 0x%04x ", dev->domain); printf("bus 0x%04x cardnum 0x%02x function 0x%02x:" " vendor 0x%04x device 0x%04x\n", dev->bus, dev->dev, dev->func, dev->vendor_id, dev->device_id ); if ( vend_name != NULL ) { printf( " %s %s\n", vend_name, dev_name ); } else { printf( " %s\n", dev_name ); } if ( verbose ) { unsigned i; uint16_t command, status; uint8_t bist; uint8_t header_type; uint8_t latency_timer; uint8_t cache_line_size; uint8_t max_latency; uint8_t min_grant; uint8_t int_pin; vend_name = pci_device_get_subvendor_name( dev ); dev_name = pci_device_get_subdevice_name( dev ); if ( dev_name == NULL ) { dev_name = "Card unknown"; } printf( " CardVendor 0x%04x card 0x%04x (", dev->subvendor_id, dev->subdevice_id ); if ( vend_name != NULL ) { printf( "%s, %s)\n", vend_name, dev_name ); } else { printf( "%s)\n", dev_name ); } pci_device_cfg_read_u16( dev, & command, 4 ); pci_device_cfg_read_u16( dev, & status, 6 ); printf( " STATUS 0x%04x COMMAND 0x%04x\n", status, command ); printf( " CLASS 0x%02x 0x%02x 0x%02x REVISION 0x%02x\n", (dev->device_class >> 16) & 0x0ff, (dev->device_class >> 8) & 0x0ff, (dev->device_class >> 0) & 0x0ff, dev->revision ); pci_device_cfg_read_u8( dev, & cache_line_size, 12 ); pci_device_cfg_read_u8( dev, & latency_timer, 13 ); pci_device_cfg_read_u8( dev, & header_type, 14 ); pci_device_cfg_read_u8( dev, & bist, 15 ); printf( " BIST 0x%02x HEADER 0x%02x LATENCY 0x%02x CACHE 0x%02x\n", bist, header_type, latency_timer, cache_line_size ); pci_device_probe( dev ); for ( i = 0 ; i < 6 ; i++ ) { if ( dev->regions[i].base_addr != 0 ) { printf( " BASE%u 0x%0*"PRIxPTR" SIZE %zu %s", i, dev->regions[i].is_64 ? 16 : 8, (intptr_t) dev->regions[i].base_addr, (size_t) dev->regions[i].size, (dev->regions[i].is_IO) ? "I/O" : ((dev->regions[i].is_64) ? "MEM64" : "MEM")); if ( ! dev->regions[i].is_IO ) { if ( dev->regions[i].is_prefetchable ) { printf( " PREFETCHABLE" ); } } printf( "\n" ); } } if ( dev->rom_size ) { struct pci_device_private *priv = (struct pci_device_private *) dev; printf( " BASEROM 0x%08"PRIxPTR" SIZE %zu\n", (intptr_t) priv->rom_base, (size_t) dev->rom_size); } pci_device_cfg_read_u8( dev, & int_pin, 61 ); pci_device_cfg_read_u8( dev, & min_grant, 62 ); pci_device_cfg_read_u8( dev, & max_latency, 63 ); printf( " MAX_LAT 0x%02x MIN_GNT 0x%02x INT_PIN 0x%02x INT_LINE 0x%02x\n", max_latency, min_grant, int_pin, dev->irq ); if ( (dev->device_class >> 16) == 0x06 ) { const void * info; if ( (info = pci_device_get_bridge_info(dev)) != NULL ) { print_pci_bridge( (const struct pci_bridge_info *) info ); } else if ( (info = pci_device_get_pcmcia_bridge_info(dev)) != NULL ) { /* Nothing yet. */ } } } } int main( int argc, char ** argv ) { struct pci_device_iterator * iter; struct pci_device * dev; int ret; int verbose = 0; int c; int errors = 0; while ((c = getopt(argc, argv, "v")) != -1) { switch (c) { case 'v': verbose = 1; break; case '?': errors++; } } if (errors != 0) { fprintf(stderr, "usage: %s [-v]\n", argv[0]); exit(2); } ret = pci_system_init(); if (ret != 0) err(1, "Couldn't initialize PCI system"); iter = pci_slot_match_iterator_create( NULL ); while ( (dev = pci_device_next( iter )) != NULL ) { print_pci_device( dev, verbose ); } pci_system_cleanup(); return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/0000775014310600000120000000000014577654164014034 5ustar00alancstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/Doxyfile0000664014310600000120000014167714577654164015562 0ustar00alancstaff# Doxyfile 1.4.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libpciaccess # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is YES. SHOW_DIRECTORIES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_bridge.c0000664014310600000120000002405614577654164017013 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_bridge.c * Support routines used to process PCI header information for bridges. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #if defined(HAVE_STRING_H) # include #elif defined(HAVE_STRINGS_H) # include #endif #if defined(HAVE_INTTYPES_H) # include #elif defined(HAVE_STDINT_H) # include #endif #include "pciaccess.h" #include "pciaccess_private.h" static int read_bridge_info( struct pci_device_private * priv ) { uint8_t buf[0x40]; pciaddr_t bytes; int err; /* Make sure the device has been probed. If not, header_type won't be * set and the rest of this function will fail. */ err = pci_device_probe(& priv->base); if (err) { return err; } switch ( priv->header_type & 0x7f ) { case 0x00: break; case 0x01: { struct pci_bridge_info *info; info = malloc(sizeof(*info)); if (info != NULL) { pci_device_cfg_read( (struct pci_device *) priv, buf + 0x18, 0x18, 0x40 - 0x18, & bytes ); info->primary_bus = buf[0x18]; info->secondary_bus = buf[0x19]; info->subordinate_bus = buf[0x1a]; info->secondary_latency_timer = buf[0x1b]; info->io_type = buf[0x1c] & 0x0f; info->io_base = (((uint32_t) (buf[0x1c] & 0x0f0)) << 8) + (((uint32_t) buf[0x30]) << 16) + (((uint32_t) buf[0x31]) << 24); info->io_limit = 0x00000fff + (((uint32_t) (buf[0x1d] & 0x0f0)) << 8) + (((uint32_t) buf[0x32]) << 16) + (((uint32_t) buf[0x33]) << 24); info->mem_type = buf[0x20] & 0x0f; info->mem_base = (((uint32_t) (buf[0x20] & 0x0f0)) << 16) + (((uint32_t) buf[0x21]) << 24); info->mem_limit = 0x0000ffff + (((uint32_t) (buf[0x22] & 0x0f0)) << 16) + (((uint32_t) buf[0x23]) << 24); info->prefetch_mem_type = buf[0x24] & 0x0f; info->prefetch_mem_base = (((uint64_t) (buf[0x24] & 0x0f0)) << 16) + (((uint64_t) buf[0x25]) << 24) + (((uint64_t) buf[0x28]) << 32) + (((uint64_t) buf[0x29]) << 40) + (((uint64_t) buf[0x2a]) << 48) + (((uint64_t) buf[0x2b]) << 56); info->prefetch_mem_limit = 0x0000ffff + (((uint64_t) (buf[0x26] & 0x0f0)) << 16) + (((uint64_t) buf[0x27]) << 24) + (((uint64_t) buf[0x2c]) << 32) + (((uint64_t) buf[0x2d]) << 40) + (((uint64_t) buf[0x2e]) << 48) + (((uint64_t) buf[0x2f]) << 56); info->bridge_control = ((uint16_t) buf[0x3e]) + (((uint16_t) buf[0x3f]) << 8); info->secondary_status = ((uint16_t) buf[0x1e]) + (((uint16_t) buf[0x1f]) << 8); } priv->bridge.pci = info; break; } case 0x02: { struct pci_pcmcia_bridge_info *info; info = malloc(sizeof(*info)); if (info != NULL) { pci_device_cfg_read( (struct pci_device *) priv, buf + 0x16, 0x16, 0x40 - 0x16, & bytes ); info->primary_bus = buf[0x18]; info->card_bus = buf[0x19]; info->subordinate_bus = buf[0x1a]; info->cardbus_latency_timer = buf[0x1b]; info->mem[0].base = (((uint32_t) buf[0x1c])) + (((uint32_t) buf[0x1d]) << 8) + (((uint32_t) buf[0x1e]) << 16) + (((uint32_t) buf[0x1f]) << 24); info->mem[0].limit = (((uint32_t) buf[0x20])) + (((uint32_t) buf[0x21]) << 8) + (((uint32_t) buf[0x22]) << 16) + (((uint32_t) buf[0x23]) << 24); info->mem[1].base = (((uint32_t) buf[0x24])) + (((uint32_t) buf[0x25]) << 8) + (((uint32_t) buf[0x26]) << 16) + (((uint32_t) buf[0x27]) << 24); info->mem[1].limit = (((uint32_t) buf[0x28])) + (((uint32_t) buf[0x29]) << 8) + (((uint32_t) buf[0x2a]) << 16) + (((uint32_t) buf[0x2b]) << 24); info->io[0].base = (((uint32_t) buf[0x2c])) + (((uint32_t) buf[0x2d]) << 8) + (((uint32_t) buf[0x2e]) << 16) + (((uint32_t) buf[0x2f]) << 24); info->io[0].limit = (((uint32_t) buf[0x30])) + (((uint32_t) buf[0x31]) << 8) + (((uint32_t) buf[0x32]) << 16) + (((uint32_t) buf[0x33]) << 24); info->io[1].base = (((uint32_t) buf[0x34])) + (((uint32_t) buf[0x35]) << 8) + (((uint32_t) buf[0x36]) << 16) + (((uint32_t) buf[0x37]) << 24); info->io[1].limit = (((uint32_t) buf[0x38])) + (((uint32_t) buf[0x39]) << 8) + (((uint32_t) buf[0x3a]) << 16) + (((uint32_t) buf[0x3b]) << 24); info->secondary_status = ((uint16_t) buf[0x16]) + (((uint16_t) buf[0x17]) << 8); info->bridge_control = ((uint16_t) buf[0x3e]) + (((uint16_t) buf[0x3f]) << 8); } priv->bridge.pcmcia = info; break; } } return 0; } /** * Get the PCI bridge information for a device * * \returns * If \c dev is a PCI-to-PCI bridge, a pointer to a \c pci_bridge_info * structure. Otherwise, \c NULL is returned. */ const struct pci_bridge_info * pci_device_get_bridge_info( struct pci_device * dev ) { struct pci_device_private * priv = (struct pci_device_private *) dev; if (priv->bridge.pci == NULL) { read_bridge_info(priv); } return ((priv->header_type & 0x7f) == 1) ? priv->bridge.pci : NULL; } /** * Get the PCMCIA bridge information for a device * * \returns * If \c dev is a PCI-to-PCMCIA bridge, a pointer to a * \c pci_pcmcia_bridge_info structure. Otherwise, \c NULL is returned. */ const struct pci_pcmcia_bridge_info * pci_device_get_pcmcia_bridge_info( struct pci_device * dev ) { struct pci_device_private * priv = (struct pci_device_private *) dev; if (priv->bridge.pcmcia == NULL) { read_bridge_info(priv); } return (priv->header_type == 2) ? priv->bridge.pcmcia : NULL; } /** * Determine the primary, secondary, and subordinate buses for a bridge * * Determines the IDs of the primary, secondary, and subordinate buses for * a specified bridge. Not all bridges directly store this information * (e.g., PCI-to-ISA bridges). For those bridges, no error is returned, but * -1 is stored in the bus IDs that don't make sense. * * For example, for a PCI-to-ISA bridge, \c primary_bus will be set to the ID * of the bus containing the device and both \c secondary_bus and * \c subordinate_bus will be set to -1. * * \return * On success, zero is returned. If \c dev is not a bridge, \c ENODEV is * returned. * * \bug * Host bridges are handled the same way as PCI-to-ISA bridges. This is * almost certainly not correct. */ int pci_device_get_bridge_buses(struct pci_device * dev, int *primary_bus, int *secondary_bus, int *subordinate_bus) { struct pci_device_private * priv = (struct pci_device_private *) dev; /* If the device isn't a bridge, return an error. */ if (((dev->device_class >> 16) & 0x0ff) != 0x06) { return ENODEV; } switch ((dev->device_class >> 8) & 0x0ff) { case 0x00: /* What to do for host bridges? I'm pretty sure this isn't right. */ *primary_bus = dev->bus; *secondary_bus = -1; *subordinate_bus = -1; break; case 0x01: case 0x02: case 0x03: *primary_bus = dev->bus; *secondary_bus = -1; *subordinate_bus = -1; break; case 0x04: if (priv->bridge.pci == NULL) read_bridge_info(priv); if ((priv->header_type & 0x7f) == 0x01) { *primary_bus = priv->bridge.pci->primary_bus; *secondary_bus = priv->bridge.pci->secondary_bus; *subordinate_bus = priv->bridge.pci->subordinate_bus; } else { *primary_bus = dev->bus; *secondary_bus = -1; *subordinate_bus = -1; } break; case 0x07: if (priv->bridge.pcmcia == NULL) read_bridge_info(priv); if ((priv->header_type & 0x7f) == 0x02) { *primary_bus = priv->bridge.pcmcia->primary_bus; *secondary_bus = priv->bridge.pcmcia->card_bus; *subordinate_bus = priv->bridge.pcmcia->subordinate_bus; } else { *primary_bus = dev->bus; *secondary_bus = -1; *subordinate_bus = -1; } break; } return 0; } #define PCI_CLASS_BRIDGE 0x06 #define PCI_SUBCLASS_BRIDGE_PCI 0x04 #define PCI_CLASS_MASK 0xFF #define PCI_SUBCLASS_MASK 0xFF struct pci_device * pci_device_get_parent_bridge(struct pci_device *dev) { struct pci_id_match bridge_match = { PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, (PCI_CLASS_BRIDGE << 16) | (PCI_SUBCLASS_BRIDGE_PCI << 8), (PCI_CLASS_MASK << 16) | (PCI_SUBCLASS_MASK << 8) }; struct pci_device *bridge; struct pci_device_iterator *iter; if (dev == NULL) return NULL; iter = pci_id_match_iterator_create(& bridge_match); if (iter == NULL) return NULL; while ((bridge = pci_device_next(iter)) != NULL) { if (bridge->domain == dev->domain) { const struct pci_bridge_info *info = pci_device_get_bridge_info(bridge); if (info != NULL) { if (info->secondary_bus == dev->bus) { break; } } } } pci_iterator_destroy(iter); return bridge; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_capability.c0000664014310600000120000001275314577654164017701 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_capability.c * Platform independent PCI capability related routines. * * In addition to including the interface glue for \c pci_device_get_agp_info, * this file also contains a generic implementation of that function. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" /** * Generic implementation of \c pci_system_methods::fill_capabilities. * * \param dev Device whose capability information is to be processed. * * \return * Zero on success or an errno value on failure. * * \todo * Once more than just the AGP capability is supported, the body of each of * the cases in the capability processing loop should probably be broken out * into its own function. * * \todo * Once more than just the AGP capability is supported, some care will need * to be taken in partial failure cases. If, say, the first capability is * correctly processed but the second fails, the function would be re-called * later to try again for the second capability. This could lead to memory * leaks or other quirky behavior. */ _pci_hidden int pci_fill_capabilities_generic( struct pci_device * dev ) { struct pci_device_private * const dev_priv = (struct pci_device_private *) dev; int err; uint16_t status; uint8_t cap_offset; err = pci_device_cfg_read_u16( dev, & status, 6 ); if ( err ) { return err; } /* Are PCI capabilities supported by this device? */ if ( (status & 0x0010) == 0 ) { return ENOSYS; } err = pci_device_cfg_read_u8( dev, & cap_offset, 52 ); if ( err ) { return err; } /* Process each of the capabilities list in the PCI header. */ while ( cap_offset != 0 ) { uint8_t cap_id; uint8_t next_cap; err = pci_device_cfg_read_u8( dev, & cap_id, cap_offset ); if ( err ) { return err; } err = pci_device_cfg_read_u8( dev, & next_cap, cap_offset + 1 ); if ( err ) { return err; } switch ( cap_id ) { case 2: { struct pci_agp_info * agp_info; uint32_t agp_status; uint8_t agp_ver; err = pci_device_cfg_read_u8( dev, & agp_ver, cap_offset + 2 ); if ( err ) { return err; } err = pci_device_cfg_read_u32( dev, & agp_status, cap_offset + 4 ); if ( err ) { return err; } agp_info = calloc( 1, sizeof( struct pci_agp_info ) ); if ( agp_info == NULL ) { return ENOMEM; } agp_info->config_offset = cap_offset; agp_info->major_version = (agp_ver & 0x0f0) >> 4; agp_info->minor_version = (agp_ver & 0x00f); agp_info->rates = (agp_status & 0x07); /* If AGP3 is supported, then the meaning of the rates values * changes. */ if ( (agp_status & 0x08) != 0 ) { agp_info->rates <<= 2; } /* Some devices, notably motherboard chipsets, have the AGP3 * capability set and the 4x bit set. This results in an * impossible 16x mode being listed as available. I'm not 100% * sure this is the right solution. */ agp_info->rates &= 0x0f; agp_info->fast_writes = (agp_status & 0x0010) != 0; agp_info->addr64 = (agp_status & 0x0020) != 0; agp_info->htrans = (agp_status & 0x0040) == 0; agp_info->gart64 = (agp_status & 0x0080) != 0; agp_info->coherent = (agp_status & 0x0100) != 0; agp_info->sideband = (agp_status & 0x0200) != 0; agp_info->isochronus = (agp_status & 0x10000) != 0; agp_info->async_req_size = 4 + (1 << ((agp_status & 0xe000) >> 13)); agp_info->calibration_cycle_timing = ((agp_status & 0x1c00) >> 10); agp_info->max_requests = 1 + ((agp_status & 0xff000000) >> 24); dev_priv->agp = agp_info; break; } /* No other capabilities are currently handled. */ default: printf( "Unknown cap 0x%02x @ 0x%02x\n", cap_id, cap_offset ); break; } cap_offset = next_cap; } return 0; } /** * Get AGP capability data for a device. */ const struct pci_agp_info * pci_device_get_agp_info( struct pci_device * dev ) { struct pci_device_private * dev_priv = (struct pci_device_private *) dev; if ( dev == NULL ) { return NULL; } if ( dev_priv->agp == NULL ) { (void) (*pci_sys->methods->fill_capabilities)( dev ); } return dev_priv->agp; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_device_name.c0000664014310600000120000003047514577654164020020 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_device_name.c * Support routines used to determine the vendor or device names associated * with a particular device or vendor. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #if defined(HAVE_STRING_H) # include #elif defined(HAVE_STRINGS_H) # include #endif #if defined(HAVE_INTTYPES_H) # include #elif defined(HAVE_STDINT_H) # include #endif #include "pciaccess.h" #include "pciaccess_private.h" #define DO_MATCH(a,b) (((a) == PCI_MATCH_ANY) || ((a) == (b))) #ifdef HAVE_ZLIB #include typedef gzFile pci_id_file; static pci_id_file pci_id_file_open(void) { pci_id_file result; result = gzopen(PCIIDS_PATH "/pci.ids.gz", "rb"); if (result) return result; return gzopen(PCIIDS_PATH "/pci.ids", "rb"); } #define pci_id_file_gets(l, s, f) gzgets(f, l, s) #define pci_id_file_close(f) gzclose(f) #else /* not zlib */ typedef FILE * pci_id_file; static pci_id_file pci_id_file_open(void) { pci_id_file result; result = fopen(PCIIDS_PATH "/pci.ids", "re"); if (result) return result; #ifdef __FreeBSD__ return fopen("/usr/share/misc/pci_vendors", "re"); #endif return fopen(PCIIDS_PATH "/pci.ids", "r"); } #define pci_id_file_gets(l, s, f) fgets(l, s, f) #define pci_id_file_close(f) fclose(f) #endif /** * Node for sorting vendor IDs. * * Each structure forms an internal node of an n-way tree. Each node selects * \c pci_id_node::bits number of bits from the vendor ID. Starting from the * root of the tree, a slice of the low-order bits of the vendor ID are * selected and used as an index into the \c pci_id_node::children array. * * At the leaf nodes (i.e., the node entered when all 16 bits of the vendor ID * have been used), the \c pci_id_node::children is actually an array of * pointers to \c pci_id_leaf structures. * * \todo * Determine if there is a cleaner way (in the source code) to have the * \c children array change type based on whether the node is internal or * a leaf. * * \todo * Currently \c bits is always 4. Decide if this value can ever change * (i.e., to pull-up levels of the n-way tree when all the children's children * are full). If it can, rip it out and hard-code it to 4 everywhere. */ struct pci_id_node { unsigned bits; struct pci_id_node * children[16]; }; struct pci_id_leaf { uint16_t vendor; const char * vendor_name; size_t num_devices; struct pci_device_leaf * devices; }; struct pci_device_leaf { struct pci_id_match id; const char * device_name; }; /** * Root of the PCI vendor ID search tree. */ _pci_hidden struct pci_id_node * tree = NULL; /** * Get a pointer to the leaf node for a vendor ID. * * If the vendor ID does not exist in the tree, it is added. */ static struct pci_id_leaf * insert( uint16_t vendor ) { struct pci_id_node * n; unsigned bits = 0; if ( tree == NULL ) { tree = calloc( 1, sizeof( struct pci_id_node ) ); if ( tree == NULL ) return NULL; tree->bits = 4; } n = tree; while ( n != NULL ) { const unsigned used_bits = n->bits; const unsigned mask = (1 << used_bits) - 1; const unsigned idx = (vendor & (mask << bits)) >> bits; if ( bits >= 16 ) { break; } bits += used_bits; if ( n->children[ idx ] == NULL ) { if ( bits < 16 ) { struct pci_id_node * child = calloc( 1, sizeof( struct pci_id_node ) ); if ( tree == NULL ) return NULL; child->bits = 4; n->children[ idx ] = child; } else { struct pci_id_leaf * leaf = calloc( 1, sizeof( struct pci_id_leaf ) ); if ( tree == NULL ) return NULL; leaf->vendor = vendor; n->children[ idx ] = (struct pci_id_node *) leaf; } } n = n->children[ idx ]; } return (struct pci_id_leaf *) n; } /** * Populate a vendor node with all the devices associated with that vendor * * \param vend Vendor node that is to be filled from the pci.ids file. * * \todo * The parsing in this function should be more rhobust. There are some error * cases (i.e., a 0-tab line followed by a 2-tab line) that aren't handled * correctly. I don't think there are any security problems with the code, * but it's not impossible. */ static void populate_vendor( struct pci_id_leaf * vend, int fill_device_data ) { pci_id_file f; char buf[128]; unsigned vendor = PCI_MATCH_ANY; /* If the device tree for this vendor is already populated, don't do * anything. This avoids wasted processing and potential memory leaks. */ if (vend->num_devices != 0) { return; } f = pci_id_file_open(); /* If the pci.ids file could not be opened, there's nothing we can do. */ if (f == NULL) { return; } while( pci_id_file_gets( buf, sizeof( buf ), f ) != NULL ) { unsigned num_tabs; char * new_line; size_t length; /* Each line either starts with zero, one, or two tabs followed by * a series of 4 hex digits. Any lines not matching that are ignored. */ for ( num_tabs = 0 ; num_tabs < 3 ; num_tabs++ ) { if ( buf[ num_tabs ] != '\t' ) { break; } } if ( !isxdigit( buf[ num_tabs + 0 ] ) || !isxdigit( buf[ num_tabs + 1 ] ) || !isxdigit( buf[ num_tabs + 2 ] ) || !isxdigit( buf[ num_tabs + 3 ] ) ) { continue; } new_line = strchr( buf, '\n' ); if ( new_line != NULL ) { *new_line = '\0'; } length = strlen( buf ); (void) memset( buf + length, 0, sizeof( buf ) - length ); if ( num_tabs == 0 ) { vendor = (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 ); if ( vend->vendor == vendor ) { /* vendor_name may already be set from a previous invocation * of this function with fill_device_data = 0. */ if (vend->vendor_name == NULL) { vend->vendor_name = strdup( & buf[ num_tabs + 6 ] ); } /* If we're not going to fill in all of the device data as * well, then bail out now. We have all the information that * we need. */ if ( ! fill_device_data ) { break; } } } else if ( vendor == vend->vendor ) { struct pci_device_leaf * d; struct pci_device_leaf * dev; struct pci_device_leaf * last_dev; d = realloc( vend->devices, (vend->num_devices + 1) * sizeof( struct pci_device_leaf ) ); if ( d == NULL ) { goto cleanup; } last_dev = & d[ vend->num_devices - 1 ]; dev = & d[ vend->num_devices ]; vend->num_devices++; vend->devices = d; if ( num_tabs == 1 ) { dev->id.vendor_id = vend->vendor; dev->id.device_id = (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 ); dev->id.subvendor_id = PCI_MATCH_ANY; dev->id.subdevice_id = PCI_MATCH_ANY; dev->id.device_class = 0; dev->id.device_class_mask = 0; dev->id.match_data = 0; dev->device_name = strdup( & buf[ num_tabs + 6 ] ); } else { dev->id = last_dev->id; dev->id.subvendor_id= (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 ); dev->id.subdevice_id = (unsigned) strtoul( & buf[ num_tabs + 5 ], NULL, 16 ); dev->device_name = strdup( & buf[ num_tabs + 5 + 6 ] ); } } } cleanup: pci_id_file_close( f ); } /** * Find the name of the specified device. * * Finds the actual product name of the specified device. If a subvendor ID * and subdevice ID are specified in \c m, the returned name will be the name * of the subdevice. */ static const char * find_device_name( const struct pci_id_match * m ) { struct pci_id_leaf * vend; unsigned i; if ( m->vendor_id == PCI_MATCH_ANY ) { return NULL; } vend = insert( m->vendor_id ); if ( vend == NULL ) { return NULL; } if ( vend->num_devices == 0 ) { populate_vendor( vend, 1 ); } for ( i = 0 ; i < vend->num_devices ; i++ ) { struct pci_device_leaf * d = & vend->devices[ i ]; if ( DO_MATCH( m->vendor_id, d->id.vendor_id ) && DO_MATCH( m->device_id, d->id.device_id ) && DO_MATCH( m->subvendor_id, d->id.subvendor_id ) && DO_MATCH( m->subdevice_id, d->id.subdevice_id ) ) { return d->device_name; } } return NULL; } /** * Find the vendor name of the specified device. * * Finds the actual vendor name of the specified device. If a subvendor ID * and subdevice ID are specified in \c m, the returned name will be the name * associated with the subvendor. */ static const char * find_vendor_name( const struct pci_id_match * m ) { struct pci_id_leaf * vend; if ( m->vendor_id == PCI_MATCH_ANY ) { return NULL; } vend = insert( m->vendor_id ); if ( vend == NULL ) { return NULL; } if ( vend->vendor_name == NULL ) { populate_vendor( vend, 0 ); } return vend->vendor_name; } /** * Get a name based on an arbitrary PCI search structure. */ void pci_get_strings( const struct pci_id_match * m, const char ** device_name, const char ** vendor_name, const char ** subdevice_name, const char ** subvendor_name ) { struct pci_id_match temp; temp = *m; temp.subvendor_id = PCI_MATCH_ANY; temp.subdevice_id = PCI_MATCH_ANY; if ( device_name != NULL ) { *device_name = find_device_name( & temp ); } if ( vendor_name != NULL ) { *vendor_name = find_vendor_name( & temp ); } if ( subdevice_name != NULL ) { *subdevice_name = find_device_name( m ); } if ( subvendor_name != NULL ) { *subvendor_name = find_vendor_name( m ); } } /** * Get the name associated with the device's primary device ID. */ const char * pci_device_get_device_name( const struct pci_device * dev ) { struct pci_id_match m; m.vendor_id = dev->vendor_id; m.device_id = dev->device_id; m.subvendor_id = PCI_MATCH_ANY; m.subdevice_id = PCI_MATCH_ANY; m.device_class = 0; m.device_class_mask = 0; m.match_data = 0; return find_device_name( & m ); } /** * Get the name associated with the device's subdevice ID. */ const char * pci_device_get_subdevice_name( const struct pci_device * dev ) { struct pci_id_match m; if ( (dev->subvendor_id == 0) || (dev->subdevice_id == 0) ) { return NULL; } m.vendor_id = dev->vendor_id; m.device_id = dev->device_id; m.subvendor_id = dev->subvendor_id; m.subdevice_id = dev->subdevice_id; m.device_class = 0; m.device_class_mask = 0; m.match_data = 0; return find_device_name( & m ); } /** * Get the name associated with the device's primary vendor ID. */ const char * pci_device_get_vendor_name( const struct pci_device * dev ) { struct pci_id_match m; m.vendor_id = dev->vendor_id; m.device_id = PCI_MATCH_ANY; m.subvendor_id = PCI_MATCH_ANY; m.subdevice_id = PCI_MATCH_ANY; m.device_class = 0; m.device_class_mask = 0; m.match_data = 0; return find_vendor_name( & m ); } /** * Get the name associated with the device's subvendor ID. */ const char * pci_device_get_subvendor_name( const struct pci_device * dev ) { struct pci_id_match m; if ( dev->subvendor_id == 0 ) { return NULL; } m.vendor_id = dev->subvendor_id; m.device_id = PCI_MATCH_ANY; m.subvendor_id = PCI_MATCH_ANY; m.subdevice_id = PCI_MATCH_ANY; m.device_class = 0; m.device_class_mask = 0; m.match_data = 0; return find_vendor_name( & m ); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_init.c0000664014310600000120000000654514577654164016525 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_init.c * Platform independent routines for initializing access to the PCI system. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "pciaccess.h" #include "pciaccess_private.h" _pci_hidden struct pci_system * pci_sys; /** * Initialize the PCI subsystem for access. * * \return * Zero on success or an errno value on failure. In particular, if no * platform-specific initializers are available, \c ENOSYS will be returned. * * \sa pci_system_cleanup */ int pci_system_init( void ) { int err = ENOSYS; #ifdef __linux__ err = pci_system_linux_sysfs_create(); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) err = pci_system_freebsd_create(); #elif defined(__NetBSD__) err = pci_system_netbsd_create(); #elif defined(__OpenBSD__) err = pci_system_openbsd_create(); #elif defined(__sun) err = pci_system_solx_devfs_create(); #elif defined(__GNU__) err = pci_system_hurd_create(); #elif defined(__CYGWIN__) err = pci_system_x86_create(); #else # error "Unsupported OS" #endif return err; } void pci_system_init_dev_mem(int fd) { #if defined(__OpenBSD__) pci_system_openbsd_init_dev_mem(fd); #endif } /** * Shutdown all access to the PCI subsystem. * * \sa pci_system_init */ void pci_system_cleanup( void ) { unsigned i; unsigned j; if ( pci_sys == NULL ) { return; } pci_io_cleanup(); if ( pci_sys->devices ) { for ( i = 0 ; i < pci_sys->num_devices ; i++ ) { for ( j = 0 ; j < 6 ; j++ ) { (void) pci_device_unmap_region( & pci_sys->devices[i].base, j ); } free( (char *) pci_sys->devices[i].device_string ); free( (char *) pci_sys->devices[i].agp ); pci_sys->devices[i].device_string = NULL; pci_sys->devices[i].agp = NULL; if ( pci_sys->methods->destroy_device != NULL ) { (*pci_sys->methods->destroy_device)( & pci_sys->devices[i].base ); } } free( pci_sys->devices ); pci_sys->devices = NULL; pci_sys->num_devices = 0; } if ( pci_sys->methods->destroy != NULL ) { (*pci_sys->methods->destroy)(); } free( pci_sys ); pci_sys = NULL; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_interface.c0000664014310600000120000004565114577654164017523 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_interface.c * Platform independent interface glue. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #if defined(__linux__) || defined(__GLIBC__) || defined(__CYGWIN__) #include #if __BYTE_ORDER == __BIG_ENDIAN # define LETOH_16(x) bswap_16(x) # define HTOLE_16(x) bswap_16(x) # define LETOH_32(x) bswap_32(x) # define HTOLE_32(x) bswap_32(x) #else # define LETOH_16(x) (x) # define HTOLE_16(x) (x) # define LETOH_32(x) (x) # define HTOLE_32(x) (x) #endif /* linux */ #elif defined(__sun) #include #ifdef _BIG_ENDIAN # define LETOH_16(x) BSWAP_16(x) # define HTOLE_16(x) BSWAP_16(x) # define LETOH_32(x) BSWAP_32(x) # define HTOLE_32(x) BSWAP_32(x) #else # define LETOH_16(x) (x) # define HTOLE_16(x) (x) # define LETOH_32(x) (x) # define HTOLE_32(x) (x) #endif /* Solaris */ #elif defined(__APPLE__) #include #define htobe16(x) OSSwapHostToBigInt16(x) #define htole16(x) OSSwapHostToLittleInt16(x) #define be16toh(x) OSSwapBigToHostInt16(x) #define le16toh(x) OSSwapLittleToHostInt16(x) #define htobe32(x) OSSwapHostToBigInt32(x) #define htole32(x) OSSwapHostToLittleInt32(x) #define be32toh(x) OSSwapBigToHostInt32(x) #define le32toh(x) OSSwapLittleToHostInt32(x) #define htobe64(x) OSSwapHostToBigInt64(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) #else #include #define HTOLE_16(x) htole16(x) #define HTOLE_32(x) htole32(x) #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) #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 */ /** * Read a device's expansion ROM. * * Reads the device's expansion ROM and stores the data in the memory pointed * to by \c buffer. The buffer must be at least \c pci_device::rom_size * bytes. * * \param dev Device whose expansion ROM is to be read. * \param buffer Memory in which to store the ROM. * * \return * Zero on success or an \c errno value on failure. */ int pci_device_read_rom( struct pci_device * dev, void * buffer ) { if ( (dev == NULL) || (buffer == NULL) ) { return EFAULT; } return (pci_sys->methods->read_rom)( dev, buffer ); } /** * Probe a PCI (VGA) device to determine if its the boot VGA device * * \param dev Device whose VGA status to query * \return * Zero if not the boot VGA, 1 if the boot VGA. */ int pci_device_is_boot_vga( struct pci_device * dev ) { if (!pci_sys->methods->boot_vga) return 0; return pci_sys->methods->boot_vga( dev ); } /** * Probe a PCI device to determine if a kernel driver is attached. * * \param dev Device to query * \return * Zero if no driver attached, 1 if attached kernel drviver */ int pci_device_has_kernel_driver( struct pci_device * dev ) { if (!pci_sys->methods->has_kernel_driver) return 0; return pci_sys->methods->has_kernel_driver( dev ); } /** * Probe a PCI device to learn information about the device. * * Probes a PCI device to learn various information about the device. Before * calling this function, the only public fields in the \c pci_device * structure that have valid values are \c pci_device::domain, * \c pci_device::bus, \c pci_device::dev, and \c pci_device::func. * * \param dev Device to be probed. * * \return * Zero on success or an \c errno value on failure. */ int pci_device_probe( struct pci_device * dev ) { if ( dev == NULL ) { return EFAULT; } return (pci_sys->methods->probe)( dev ); } /** * Map the specified BAR so that it can be accessed by the CPU. * * Maps the specified BAR for access by the processor. The pointer to the * mapped region is stored in the \c pci_mem_region::memory pointer for the * BAR. * * \param dev Device whose memory region is to be mapped. * \param region Region, on the range [0, 5], that is to be mapped. * \param write_enable Map for writing (non-zero). * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_range, pci_device_unmap_range * \deprecated */ int pci_device_map_region(struct pci_device * dev, unsigned region, int write_enable) { const unsigned map_flags = (write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0; if ((region > 5) || (dev->regions[region].size == 0)) { return ENOENT; } if (dev->regions[region].memory != NULL) { return 0; } return pci_device_map_range(dev, dev->regions[region].base_addr, dev->regions[region].size, map_flags, &dev->regions[region].memory); } /** * Map the specified memory range so that it can be accessed by the CPU. * * Maps the specified memory range for access by the processor. The pointer * to the mapped region is stored in \c addr. In addition, the * \c pci_mem_region::memory pointer for the BAR will be updated. * * \param dev Device whose memory region is to be mapped. * \param base Base address of the range to be mapped. * \param size Size of the range to be mapped. * \param write_enable Map for writing (non-zero). * \param addr Location to store the mapped address. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_range */ int pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base, pciaddr_t size, int write_enable, void **addr) { return pci_device_map_range(dev, base, size, (write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0, addr); } /** * Map the specified memory range so that it can be accessed by the CPU. * * Maps the specified memory range for access by the processor. The pointer * to the mapped region is stored in \c addr. In addition, the * \c pci_mem_region::memory pointer for the BAR will be updated. * * \param dev Device whose memory region is to be mapped. * \param base Base address of the range to be mapped. * \param size Size of the range to be mapped. * \param map_flags Flag bits controlling how the mapping is accessed. * \param addr Location to store the mapped address. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_unmap_range */ int pci_device_map_range(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_private *const devp = (struct pci_device_private *) dev; struct pci_device_mapping *mappings; unsigned region; unsigned i; int err = 0; *addr = NULL; if (dev == NULL) { return EFAULT; } for (region = 0; region < 6; region++) { const struct pci_mem_region * const r = &dev->regions[region]; if (r->size != 0) { if ((r->base_addr <= base) && ((r->base_addr + r->size) > base)) { if ((base + size) > (r->base_addr + r->size)) { return E2BIG; } break; } } } if (region > 5) { return ENOENT; } /* Make sure that there isn't already a mapping with the same base and * size. */ for (i = 0; i < devp->num_mappings; i++) { if ((devp->mappings[i].base == base) && (devp->mappings[i].size == size)) { return EINVAL; } } mappings = realloc(devp->mappings, (sizeof(devp->mappings[0]) * (devp->num_mappings + 1))); if (mappings == NULL) { return ENOMEM; } mappings[devp->num_mappings].base = base; mappings[devp->num_mappings].size = size; mappings[devp->num_mappings].region = region; mappings[devp->num_mappings].flags = map_flags; mappings[devp->num_mappings].memory = NULL; if (dev->regions[region].memory == NULL) { err = (*pci_sys->methods->map_range)(dev, &mappings[devp->num_mappings]); } if (err == 0) { *addr = mappings[devp->num_mappings].memory; devp->num_mappings++; } else { mappings = realloc(mappings, (sizeof(mappings[0]) * devp->num_mappings)); } devp->mappings = mappings; return err; } /** * Unmap the specified BAR so that it can no longer be accessed by the CPU. * * Unmaps the specified BAR that was previously mapped via * \c pci_device_map_region. * * \param dev Device whose memory region is to be mapped. * \param region Region, on the range [0, 5], that is to be mapped. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_range, pci_device_unmap_range * \deprecated */ int pci_device_unmap_region( struct pci_device * dev, unsigned region ) { int err; if (dev == NULL) { return EFAULT; } if ((region > 5) || (dev->regions[region].size == 0)) { return ENOENT; } err = pci_device_unmap_range(dev, dev->regions[region].memory, dev->regions[region].size); if (!err) { dev->regions[region].memory = NULL; } return err; } /** * Unmap the specified memory range so that it can no longer be accessed by the CPU. * * Unmaps the specified memory range that was previously mapped via * \c pci_device_map_memory_range. * * \param dev Device whose memory is to be unmapped. * \param memory Pointer to the base of the mapped range. * \param size Size, in bytes, of the range to be unmapped. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_range, pci_device_unmap_range * \deprecated */ int pci_device_unmap_memory_range(struct pci_device *dev, void *memory, pciaddr_t size) { return pci_device_unmap_range(dev, memory, size); } /** * Unmap the specified memory range so that it can no longer be accessed by the CPU. * * Unmaps the specified memory range that was previously mapped via * \c pci_device_map_memory_range. * * \param dev Device whose memory is to be unmapped. * \param memory Pointer to the base of the mapped range. * \param size Size, in bytes, of the range to be unmapped. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_range */ int pci_device_unmap_range(struct pci_device *dev, void *memory, pciaddr_t size) { struct pci_device_private *const devp = (struct pci_device_private *) dev; unsigned i; int err; if (dev == NULL) { return EFAULT; } for (i = 0; i < devp->num_mappings; i++) { if ((devp->mappings[i].memory == memory) && (devp->mappings[i].size == size)) { break; } } if (i == devp->num_mappings) { return ENOENT; } err = (*pci_sys->methods->unmap_range)(dev, &devp->mappings[i]); if (!err) { const unsigned entries_to_move = (devp->num_mappings - i) - 1; if (entries_to_move > 0) { (void) memmove(&devp->mappings[i], &devp->mappings[i + 1], entries_to_move * sizeof(devp->mappings[0])); } devp->num_mappings--; devp->mappings = realloc(devp->mappings, (sizeof(devp->mappings[0]) * devp->num_mappings)); } return err; } /** * Read arbitrary bytes from device's PCI config space * * Reads data from the device's PCI configuration space. As with the system * read command, less data may be returned, without an error, than was * requested. This is particularly the case if a non-root user tries to read * beyond the first 64-bytes of configuration space. * * \param dev Device whose PCI configuration data is to be read. * \param data Location to store the data * \param offset Initial byte offset to read * \param size Total number of bytes to read * \param bytes_read Location to store the actual number of bytes read. This * pointer may be \c NULL. * * \returns * Zero on success or an errno value on failure. * * \note * Data read from PCI configuration space using this routine is \b not * byte-swapped to the host's byte order. PCI configuration data is always * stored in little-endian order, and that is what this routine returns. */ int pci_device_cfg_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ) { pciaddr_t scratch; if ( (dev == NULL) || (data == NULL) ) { return EFAULT; } return pci_sys->methods->read( dev, data, offset, size, (bytes_read == NULL) ? & scratch : bytes_read ); } int pci_device_cfg_read_u8( struct pci_device * dev, uint8_t * data, pciaddr_t offset ) { pciaddr_t bytes; int err = pci_device_cfg_read( dev, data, offset, 1, & bytes ); if ( (err == 0) && (bytes != 1) ) { err = ENXIO; } return err; } int pci_device_cfg_read_u16( struct pci_device * dev, uint16_t * data, pciaddr_t offset ) { pciaddr_t bytes; int err = pci_device_cfg_read( dev, data, offset, 2, & bytes ); if ( (err == 0) && (bytes != 2) ) { err = ENXIO; } *data = LETOH_16( *data ); return err; } int pci_device_cfg_read_u32( struct pci_device * dev, uint32_t * data, pciaddr_t offset ) { pciaddr_t bytes; int err = pci_device_cfg_read( dev, data, offset, 4, & bytes ); if ( (err == 0) && (bytes != 4) ) { err = ENXIO; } *data = LETOH_32( *data ); return err; } /** * Write arbitrary bytes to device's PCI config space * * Writes data to the device's PCI configuration space. As with the system * write command, less data may be written, without an error, than was * requested. * * \param dev Device whose PCI configuration data is to be written. * \param data Location of the source data * \param offset Initial byte offset to write * \param size Total number of bytes to write * \param bytes_read Location to store the actual number of bytes written. * This pointer may be \c NULL. * * \returns * Zero on success or an errno value on failure. * * \note * Data written to PCI configuration space using this routine is \b not * byte-swapped from the host's byte order. PCI configuration data is always * stored in little-endian order, so data written with this routine should be * put in that order in advance. */ int pci_device_cfg_write( struct pci_device * dev, const void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_written ) { pciaddr_t scratch; if ( (dev == NULL) || (data == NULL) ) { return EFAULT; } return pci_sys->methods->write( dev, data, offset, size, (bytes_written == NULL) ? & scratch : bytes_written ); } int pci_device_cfg_write_u8(struct pci_device *dev, uint8_t data, pciaddr_t offset) { pciaddr_t bytes; int err = pci_device_cfg_write(dev, & data, offset, 1, & bytes); if ( (err == 0) && (bytes != 1) ) { err = ENOSPC; } return err; } int pci_device_cfg_write_u16(struct pci_device *dev, uint16_t data, pciaddr_t offset) { pciaddr_t bytes; const uint16_t temp = HTOLE_16(data); int err = pci_device_cfg_write( dev, & temp, offset, 2, & bytes ); if ( (err == 0) && (bytes != 2) ) { err = ENOSPC; } return err; } int pci_device_cfg_write_u32(struct pci_device *dev, uint32_t data, pciaddr_t offset) { pciaddr_t bytes; const uint32_t temp = HTOLE_32(data); int err = pci_device_cfg_write( dev, & temp, offset, 4, & bytes ); if ( (err == 0) && (bytes != 4) ) { err = ENOSPC; } return err; } int pci_device_cfg_write_bits( struct pci_device * dev, uint32_t mask, uint32_t data, pciaddr_t offset ) { uint32_t temp; int err; err = pci_device_cfg_read_u32( dev, & temp, offset ); if ( ! err ) { temp &= ~mask; temp |= data; err = pci_device_cfg_write_u32(dev, temp, offset); } return err; } void pci_device_enable(struct pci_device *dev) { if (dev == NULL) { return; } if (pci_sys->methods->enable) pci_sys->methods->enable(dev); } void pci_device_disable(struct pci_device *dev) { if (dev == NULL) return; if (pci_sys->methods->disable) pci_sys->methods->disable(dev); } /** * Map the legacy memory space for the PCI domain containing \c dev. * * \param dev Device whose memory region is to be mapped. * \param base Base address of the range to be mapped. * \param size Size of the range to be mapped. * \param map_flags Flag bits controlling how the mapping is accessed. * \param addr Location to store the mapped address. * * \returns * Zero on success or an \c errno value on failure. */ int pci_device_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { if (base > 0x100000 || base + size > 0x100000) return EINVAL; if (!pci_sys->methods->map_legacy) return ENOSYS; return pci_sys->methods->map_legacy(dev, base, size, map_flags, addr); } /** * Unmap the legacy memory space for the PCI domain containing \c dev. * * \param dev Device whose memory region is to be unmapped. * \param addr Location of the mapped address. * \param size Size of the range to be unmapped. * * \returns * Zero on success or an \c errno value on failure. */ int pci_device_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { if (!pci_sys->methods->unmap_legacy) return ENOSYS; return pci_sys->methods->unmap_legacy(dev, addr, size); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_io.c0000664014310600000120000001307714577654164016167 0ustar00alancstaff/* * Copyright 2009 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software") * to deal in the software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * them Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Author: * Adam Jackson */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "pciaccess.h" #include "pciaccess_private.h" static struct pci_io_handle * new_io_handle(void) { struct pci_io_handle *new; new = malloc(sizeof(struct pci_io_handle)); if (!new) return NULL; return new; } static void delete_io_handle(struct pci_io_handle *handle) { free(handle); return; } _pci_hidden void pci_io_cleanup(void) { } /** * Open a handle to a PCI device I/O range. The \c base and \c size * requested must fit entirely within a single I/O BAR on the device. * \c size is in bytes. * * \returns * An opaque handle to the I/O BAR, or \c NULL on error. */ struct pci_io_handle * pci_device_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size) { struct pci_io_handle *ret; int bar; if (!pci_sys->methods->open_device_io) return NULL; for (bar = 0; bar < 6; bar++) { struct pci_mem_region *region = &(dev->regions[bar]); if (!region->is_IO) continue; if (base < region->base_addr || base > (region->base_addr+region->size)) continue; if ((base + size) > (region->base_addr + region->size)) continue; ret = new_io_handle(); if (!ret) return NULL; if (!pci_sys->methods->open_device_io(ret, dev, bar, base, size)) { delete_io_handle(ret); return NULL; } return ret; } return NULL; } /** * Open a handle to the legacy I/O space for the PCI domain containing * \c dev. \c size is in bytes. * * \returns * An opaque handle to the requested range, or \c NULL on error. */ struct pci_io_handle * pci_legacy_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size) { struct pci_io_handle *ret; if (!pci_sys->methods->open_legacy_io) return NULL; ret = new_io_handle(); if (!ret) return NULL; if (!pci_sys->methods->open_legacy_io(ret, dev, base, size)) { delete_io_handle(ret); return NULL; } return ret; } /** * Close an I/O handle. */ void pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle) { if (dev && handle && pci_sys->methods->close_io) pci_sys->methods->close_io(dev, handle); delete_io_handle(handle); } /** * Read a 32-bit value from the I/O space. \c reg is relative to the * \c base specified when the handle was opened. Some platforms may * require that \c reg be 32-bit-aligned. * * \returns * The value read from the I/O port, or undefined on any error. */ uint32_t pci_io_read32(struct pci_io_handle *handle, uint32_t reg) { if (reg + 4 > handle->size) return UINT32_MAX; return pci_sys->methods->read32(handle, reg); } /** * Read a 16-bit value from the I/O space. \c reg is relative to the * \c base specified when the handle was opened. Some platforms may * require that \c reg be 16-bit-aligned. * * \returns * The value read from the I/O port, or undefined on any error. */ uint16_t pci_io_read16(struct pci_io_handle *handle, uint32_t reg) { if (reg + 2 > handle->size) return UINT16_MAX; return pci_sys->methods->read16(handle, reg); } /** * Read a 8-bit value from the I/O space. \c reg is relative to the * \c base specified when the handle was opened. * * \returns * The value read from the I/O port, or undefined on any error. */ uint8_t pci_io_read8(struct pci_io_handle *handle, uint32_t reg) { if (reg + 1 > handle->size) return UINT8_MAX; return pci_sys->methods->read8(handle, reg); } /** * Write a 32-bit value to the I/O space. \c reg is relative to the * \c base specified when the handle was opened. Some platforms may * require that \c reg be 32-bit-aligned. */ void pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { if (reg + 4 > handle->size) return; pci_sys->methods->write32(handle, reg, data); } /** * Write a 16-bit value to the I/O space. \c reg is relative to the * \c base specified when the handle was opened. Some platforms may * require that \c reg be 16-bit-aligned. */ void pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { if (reg + 2 > handle->size) return; pci_sys->methods->write16(handle, reg, data); } /** * Write a 8-bit value to the I/O space. \c reg is relative to the * \c base specified when the handle was opened. */ void pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { if (reg + 1 > handle->size) return; pci_sys->methods->write8(handle, reg, data); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_iterator.c0000664014310600000120000001271214577654164017404 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file common_iterator.c * Platform independent iterator support routines. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "pciaccess.h" #include "pciaccess_private.h" /** * Track device iteration state * * \private */ struct pci_device_iterator { unsigned next_index; enum { match_any, match_slot, match_id } mode; union { struct pci_slot_match slot; struct pci_id_match id; } match; }; /** * Create an iterator based on a regular expression. * * \return * A pointer to a fully initialized \c pci_device_iterator structure on * success, or \c NULL on failure. * * \sa pci_id_match_iterator_create, pci_device_next, pci_iterator_destroy */ struct pci_device_iterator * pci_slot_match_iterator_create( const struct pci_slot_match * match ) { struct pci_device_iterator * iter; if ( pci_sys == NULL ) { return NULL; } iter = malloc( sizeof( *iter ) ); if ( iter != NULL ) { iter->next_index = 0; if ( match != NULL ) { iter->mode = match_slot; (void) memcpy( & iter->match.slot, match, sizeof( *match ) ); } else { iter->mode = match_any; } } return iter; } /** * Create an iterator based on a regular expression. * * \return * A pointer to a fully initialized \c pci_device_iterator structure on * success, or \c NULL on failure. * * \sa pci_slot_match_iterator_create, pci_device_next, pci_iterator_destroy */ struct pci_device_iterator * pci_id_match_iterator_create( const struct pci_id_match * match ) { struct pci_device_iterator * iter; if ( pci_sys == NULL ) { return NULL; } iter = malloc( sizeof( *iter ) ); if ( iter != NULL ) { iter->next_index = 0; if ( match != NULL ) { iter->mode = match_id; (void) memcpy( & iter->match.id, match, sizeof( *match ) ); } else { iter->mode = match_any; } } return iter; } /** * Destroy an iterator previously created with \c pci_iterator_create. * * \param iter Iterator to be destroyed. * * \sa pci_device_next, pci_iterator_create */ void pci_iterator_destroy( struct pci_device_iterator * iter ) { if ( iter != NULL ) { free( iter ); } } /** * Iterate to the next PCI device. * * \param iter Device iterator returned by \c pci_device_iterate. * * \return * A pointer to a \c pci_device, or \c NULL when all devices have been * iterated. */ struct pci_device * pci_device_next( struct pci_device_iterator * iter ) { struct pci_device_private * d = NULL; if (!iter) return NULL; switch( iter->mode ) { case match_any: if ( iter->next_index < pci_sys->num_devices ) { d = & pci_sys->devices[ iter->next_index ]; iter->next_index++; } break; case match_slot: { while ( iter->next_index < pci_sys->num_devices ) { struct pci_device_private * const temp = & pci_sys->devices[ iter->next_index ]; iter->next_index++; if ( PCI_ID_COMPARE( iter->match.slot.domain, temp->base.domain ) && PCI_ID_COMPARE( iter->match.slot.bus, temp->base.bus ) && PCI_ID_COMPARE( iter->match.slot.dev, temp->base.dev ) && PCI_ID_COMPARE( iter->match.slot.func, temp->base.func ) ) { d = temp; break; } } break; } case match_id: { while ( iter->next_index < pci_sys->num_devices ) { struct pci_device_private * const temp = & pci_sys->devices[ iter->next_index ]; iter->next_index++; if ( PCI_ID_COMPARE( iter->match.id.vendor_id, temp->base.vendor_id ) && PCI_ID_COMPARE( iter->match.id.device_id, temp->base.device_id ) && PCI_ID_COMPARE( iter->match.id.subvendor_id, temp->base.subvendor_id ) && PCI_ID_COMPARE( iter->match.id.subdevice_id, temp->base.subdevice_id ) && ((temp->base.device_class & iter->match.id.device_class_mask) == iter->match.id.device_class) ) { d = temp; break; } } break; } } return (struct pci_device *) d; } struct pci_device * pci_device_find_by_slot( uint32_t domain, uint32_t bus, uint32_t dev, uint32_t func ) { struct pci_device_iterator iter; iter.next_index = 0; iter.mode = match_slot; iter.match.slot.domain = domain; iter.match.slot.bus = bus; iter.match.slot.dev = dev; iter.match.slot.func = func; return pci_device_next( & iter ); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_map.c0000664014310600000120000000360014577654164016324 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2007 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" /** * \file common_map.c * Platform independent memory map routines. * * \author Ian Romanick */ /** * Unmap the specified region using the munmap. * * \param dev Device whose memory region is to be mapped. * \param map Memory mapping that is to be undone. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_unmap_range */ _pci_hidden int pci_device_generic_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { return (munmap(map->memory, map->size) == -1) ? errno : 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_vgaarb.c0000664014310600000120000002030614577654164017013 0ustar00alancstaff/* * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti * 2009 Tiago Vignatti * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #define BUFSIZE 64 static int parse_string_to_decodes_rsrc(char *input, int *vga_count, struct pci_slot_match *match) { char *tok; char *input_sp = NULL, *count_sp, *pci_sp; char tmp[32]; tok = strtok_r(input,",",&input_sp); if (!tok) goto fail; strncpy(tmp, input, 15); tmp[15] = 0; tok = strtok_r(tmp,":",&count_sp); if (!tok) goto fail; tok = strtok_r(NULL, ":",&count_sp); if (!tok) goto fail; *vga_count = strtoul(tok, NULL, 10); if (*vga_count == LONG_MAX) goto fail; #ifdef DEBUG fprintf(stderr,"vga count is %d\n", *vga_count); #endif tok = strtok_r(NULL, ",",&input_sp); if (!tok) goto fail; if (match) { strncpy(tmp, tok, 32); tmp[31] = 0; tok = strtok_r(tmp, ":", &pci_sp); if (!tok) goto fail; tok = strtok_r(NULL, ":", &pci_sp); if (!tok) goto fail; match->domain = strtoul(tok, NULL, 16); tok = strtok_r(NULL, ":", &pci_sp); if (!tok) goto fail; match->bus = strtoul(tok, NULL, 16); tok = strtok_r(NULL, ".", &pci_sp); if (!tok) goto fail; match->dev = strtoul(tok, NULL, 16); tok = strtok_r(NULL, ".", &pci_sp); if (!tok) goto fail; match->func = strtoul(tok, NULL, 16); } tok = strtok_r(NULL, ",",&input_sp); if (!tok) goto fail; tok = strtok_r(tok, "=", &input_sp); if (!tok) goto fail; tok = strtok_r(NULL, "=", &input_sp); if (!tok) goto fail; if (!strncmp(tok, "io+mem", 6)) return VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM; if (!strncmp(tok, "io", 2)) return VGA_ARB_RSRC_LEGACY_IO; if (!strncmp(tok, "mem", 3)) return VGA_ARB_RSRC_LEGACY_MEM; fail: return VGA_ARB_RSRC_NONE; } int pci_device_vgaarb_init(void) { struct pci_slot_match match; char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */ int ret, rsrc; if (!pci_sys) return -1; if ((pci_sys->vgaarb_fd = open ("/dev/vga_arbiter", O_RDWR | O_CLOEXEC)) < 0) { return errno; } ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE); if (ret <= 0) return -1; buf[ret] = 0; /* ret will never be greater than BUFSIZE */ memset(&match, 0xff, sizeof(match)); /* need to find the device to go back to and what it was decoding */ rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, &match); pci_sys->vga_default_dev = pci_device_find_by_slot(match.domain, match.bus, match.dev, match.func); if (pci_sys->vga_default_dev) pci_sys->vga_default_dev->vgaarb_rsrc = rsrc; return 0; } void pci_device_vgaarb_fini(void) { if (!pci_sys) return; close(pci_sys->vgaarb_fd); } /** * Writes message on vga device. The messages are defined by the kernel * implementation. * * \param fd vga arbiter device. * \param buf message itself. * \param len message length. * * \return * Zero on success, 1 if something gets wrong and 2 if fd is busy (only for * 'trylock') */ static int vgaarb_write(int fd, char *buf, int len) { int ret; buf[len] = '\0'; ret = write(fd, buf, len); if (ret == -1) { /* the user may have called "trylock" and didn't get the lock */ if (errno == EBUSY) return 2; #ifdef DEBUG fprintf(stderr, "write error"); #endif return 1; } else if (ret != len) { /* it's need to receive the exactly amount required. */ #ifdef DEBUG fprintf(stderr, "write error: wrote different than expected\n"); #endif return 1; } #ifdef DEBUG fprintf(stderr, "%s: successfully wrote: '%s'\n", __FUNCTION__, buf); #endif return 0; } static const char * rsrc_to_str(int iostate) { switch (iostate) { case VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM: return "io+mem"; case VGA_ARB_RSRC_LEGACY_IO: return "io"; case VGA_ARB_RSRC_LEGACY_MEM: return "mem"; } return "none"; } int pci_device_vgaarb_set_target(struct pci_device *dev) { int len; char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */ int ret; if (!dev) dev = pci_sys->vga_default_dev; if (!dev) return -1; len = snprintf(buf, BUFSIZE, "target PCI:%04x:%02x:%02x.%x", dev->domain, dev->bus, dev->dev, dev->func); ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len); if (ret) return ret; ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE); if (ret <= 0) return -1; buf[ret] = 0; /* ret will never be greater than BUFSIZE */ dev->vgaarb_rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL); pci_sys->vga_target = dev; return 0; } int pci_device_vgaarb_decodes(int new_vgaarb_rsrc) { int len; char buf[BUFSIZE + 1]; /* reading BUFSIZE characters, + 1 for NULL */ int ret; struct pci_device *dev = pci_sys->vga_target; if (!dev) return -1; if (dev->vgaarb_rsrc == new_vgaarb_rsrc) return 0; len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(new_vgaarb_rsrc)); ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len); if (ret == 0) dev->vgaarb_rsrc = new_vgaarb_rsrc; ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE); if (ret <= 0) return -1; buf[ret] = 0; /* ret will never be greater than BUFSIZE */ parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL); return ret; } int pci_device_vgaarb_lock(void) { int len; char buf[BUFSIZE]; struct pci_device *dev = pci_sys->vga_target; if (!dev) return -1; if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1) return 0; len = snprintf(buf, BUFSIZE, "lock %s", rsrc_to_str(dev->vgaarb_rsrc)); return vgaarb_write(pci_sys->vgaarb_fd, buf, len); } int pci_device_vgaarb_trylock(void) { int len; char buf[BUFSIZE]; struct pci_device *dev = pci_sys->vga_target; if (!dev) return -1; if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1) return 0; len = snprintf(buf, BUFSIZE, "trylock %s", rsrc_to_str(dev->vgaarb_rsrc)); return vgaarb_write(pci_sys->vgaarb_fd, buf, len); } int pci_device_vgaarb_unlock(void) { int len; char buf[BUFSIZE]; struct pci_device *dev = pci_sys->vga_target; if (!dev) return -1; if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1) return 0; len = snprintf(buf, BUFSIZE, "unlock %s", rsrc_to_str(dev->vgaarb_rsrc)); return vgaarb_write(pci_sys->vgaarb_fd, buf, len); } int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes) { *vga_count = pci_sys->vga_count; if (!dev) return 0; *rsrc_decodes = dev->vgaarb_rsrc; return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/common_vgaarb_stub.c0000664014310600000120000000356314577654164020056 0ustar00alancstaff/* * Copyright (c) 2009 Tiago Vignatti * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "pciaccess.h" int pci_device_vgaarb_init(void) { #ifdef DEBUG fprintf(stderr, "%s: You're using VGA arbiter stub functions!\n", __FUNCTION__); #endif return -1; } void pci_device_vgaarb_fini(void) { } int pci_device_vgaarb_set_target(struct pci_device *dev) { return -1; } int pci_device_vgaarb_decodes(int new_vga_rsrc) { return -1; } int pci_device_vgaarb_lock(void) { return -1; } int pci_device_vgaarb_trylock(void) { return -1; } int pci_device_vgaarb_unlock(void) { return 0; } int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes) { return -1; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/freebsd_pci.c0000664014310600000120000004205414577654164016452 0ustar00alancstaff/* * (C) Copyright Eric Anholt 2006 * (C) Copyright IBM Corporation 2006 * (C) Copyright Mark Kettenis 2011 * (C) Copyright Robert Millan 2012 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file freebsd_pci.c * * Access the kernel PCI support using /dev/pci's ioctl and mmap interface. * * \author Eric Anholt */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #else #include #endif #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #define PCIC_DISPLAY 0x03 #define PCIS_DISPLAY_VGA 0x00 #define PCIS_DISPLAY_XGA 0x01 #define PCIS_DISPLAY_3D 0x02 #define PCIS_DISPLAY_OTHER 0x80 /* Registers taken from pcireg.h */ #define PCIR_COMMAND 0x04 #define PCIM_CMD_PORTEN 0x0001 #define PCIM_CMD_MEMEN 0x0002 #define PCIR_BIOS 0x30 #define PCIM_BIOS_ENABLE 0x01 #define PCIM_BIOS_ADDR_MASK 0xfffff800 #define PCIR_BARS 0x10 #define PCIR_BAR(x) (PCIR_BARS + (x) * 4) #define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) #define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) #define PCIM_BAR_MEM_TYPE 0x00000006 #define PCIM_BAR_MEM_64 4 #define PCIM_BAR_MEM_PREFETCH 0x00000008 #define PCIM_BAR_SPACE 0x00000001 #define PCIM_BAR_MEM_SPACE 0 #define PCIM_BAR_IO_SPACE 1 /** * FreeBSD private pci_system structure that extends the base pci_system * structure. * * It is initialized once and used as a global, just as pci_system is used. */ _pci_hidden struct freebsd_pci_system { /* This must be the first entry in the structure, as pci_system_cleanup() * frees pci_sys. */ struct pci_system pci_sys; int pcidev; /**< fd for /dev/pci */ } *freebsd_pci_sys; /** * Map a memory region for a device using /dev/mem. * * \param dev Device whose memory region is to be mapped. * \param map Parameters of the mapping that is to be created. * * \return * Zero on success or an \c errno value on failure. */ static int pci_device_freebsd_map_range( struct pci_device *dev, struct pci_device_mapping *map ) { const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? (PROT_READ | PROT_WRITE) : PROT_READ; struct mem_range_desc mrd; struct mem_range_op mro; int fd, err = 0; fd = open("/dev/mem", O_RDWR | O_CLOEXEC); if (fd == -1) return errno; map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); if (map->memory == MAP_FAILED) { err = errno; } mrd.mr_base = map->base; mrd.mr_len = map->size; strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) mrd.mr_flags = MDF_WRITEBACK; else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) mrd.mr_flags = MDF_WRITECOMBINE; else mrd.mr_flags = MDF_UNCACHEABLE; mro.mo_desc = &mrd; mro.mo_arg[0] = MEMRANGE_SET_UPDATE; /* No need to set an MTRR if it's the default mode. */ if (mrd.mr_flags != MDF_UNCACHEABLE) { if (ioctl(fd, MEMRANGE_SET, &mro)) { fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno)); } } close(fd); return err; } static int pci_device_freebsd_unmap_range( struct pci_device *dev, struct pci_device_mapping *map ) { struct mem_range_desc mrd; struct mem_range_op mro; int fd; if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { fd = open("/dev/mem", O_RDWR | O_CLOEXEC); if (fd != -1) { mrd.mr_base = map->base; mrd.mr_len = map->size; strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); mrd.mr_flags = MDF_UNCACHEABLE; mro.mo_desc = &mrd; mro.mo_arg[0] = MEMRANGE_SET_REMOVE; if (ioctl(fd, MEMRANGE_SET, &mro)) { fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); } close(fd); } else { fprintf(stderr, "Failed to open /dev/mem\n"); } } return pci_device_generic_unmap_range(dev, map); } static int pci_device_freebsd_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_domain = dev->domain; 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 = (size < 4) ? size : 4; /* Only power of two allowed. */ if (toread == 3) toread = 2; io.pi_reg = offset; io.pi_width = toread; if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) return errno; memcpy(data, &io.pi_data, toread ); offset += toread; data = (char *)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_freebsd_write( struct pci_device * dev, const void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_written ) { struct pci_io io; io.pi_sel.pc_domain = dev->domain; 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 ) { int towrite = (size < 4 ? size : 4); /* Only power of two allowed. */ if (towrite == 3) towrite = 2; io.pi_reg = offset; io.pi_width = towrite; memcpy( &io.pi_data, data, towrite ); if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) return errno; offset += towrite; data = (char *)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } /** * Read a VGA rom using the 0xc0000 mapping. * * This function should be extended to handle access through PCI resources, * which should be more reliable when available. */ static int pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) { struct pci_device_private *priv = (struct pci_device_private *) dev; void *bios; pciaddr_t rom_base; uint32_t rom; uint16_t reg; int pci_rom, memfd; if ( ( dev->device_class & 0x00ffff00 ) != ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) { return ENOSYS; } if (priv->rom_base == 0) { #if defined(__amd64__) || defined(__i386__) rom_base = 0xc0000; pci_rom = 0; #else return ENOSYS; #endif } else { rom_base = priv->rom_base; pci_rom = 1; pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); } printf("Using rom_base = 0x%lx\n", (long)rom_base); memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); if ( memfd == -1 ) return errno; bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); if ( bios == MAP_FAILED ) { close( memfd ); return errno; } memcpy( buffer, bios, dev->rom_size ); munmap( bios, dev->rom_size ); close( memfd ); if (pci_rom) { pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); } return 0; } /** Returns the number of regions (base address registers) the device has */ static int pci_device_freebsd_get_num_regions( struct pci_device * dev ) { struct pci_device_private *priv = (struct pci_device_private *) dev; switch (priv->header_type) { case 0: return 6; case 1: return 2; case 2: return 1; default: printf("unknown header type %02x\n", priv->header_type); return 0; } } static int pci_device_freebsd_probe( struct pci_device * dev ) { struct pci_bar_io bar; uint8_t irq; int err, i; bar.pbi_sel.pc_domain = dev->domain; bar.pbi_sel.pc_bus = dev->bus; bar.pbi_sel.pc_dev = dev->dev; bar.pbi_sel.pc_func = dev->func; /* Many of the fields were filled in during initial device enumeration. * At this point, we need to fill in regions, rom_size, and irq. */ err = pci_device_cfg_read_u8( dev, &irq, 60 ); if (err) return errno; dev->irq = irq; for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { bar.pbi_reg = PCIR_BAR(i); if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) continue; if (PCI_BAR_IO(bar.pbi_base)) dev->regions[i].is_IO = 1; if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) dev->regions[i].is_64 = 1; if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) dev->regions[i].is_prefetchable = 1; dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); dev->regions[i].size = bar.pbi_length; } /* If it's a VGA device, set up the rom size for read_rom using the * 0xc0000 mapping. */ if ((dev->device_class & 0x00ffff00) == ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { dev->rom_size = 64 * 1024; } return 0; } static void pci_system_freebsd_destroy( void ) { close(freebsd_pci_sys->pcidev); free(freebsd_pci_sys->pci_sys.devices); freebsd_pci_sys = NULL; } static int pci_device_freebsd_has_kernel_driver( struct pci_device *dev ) { struct pci_io io; io.pi_sel.pc_domain = dev->domain; io.pi_sel.pc_bus = dev->bus; io.pi_sel.pc_dev = dev->dev; io.pi_sel.pc_func = dev->func; if ( ioctl( freebsd_pci_sys->pcidev, PCIOCATTACHED, &io ) < 0 ) { return 0; } /* if io.pi_data is 0, no driver is attached */ return io.pi_data == 0 ? 0 : 1; } static struct pci_io_handle * pci_device_freebsd_open_legacy_io( struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size ) { ret->fd = open( "/dev/io", O_RDWR | O_CLOEXEC ); if ( ret->fd < 0 ) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; } static struct pci_io_handle * pci_device_freebsd_open_io( struct pci_io_handle *ret, struct pci_device *dev, int bar, pciaddr_t base, pciaddr_t size ) { ret = pci_device_freebsd_open_legacy_io( ret, dev, base, size ); if ( ret != NULL ) ret->is_legacy = 0; return ret; } static void pci_device_freebsd_close_io( struct pci_device *dev, struct pci_io_handle *handle ) { if ( handle->fd > -1 ) close( handle->fd ); } static uint32_t pci_device_freebsd_read32( struct pci_io_handle *handle, uint32_t reg ) { #if defined(__i386__) || defined(__amd64__) return inl( handle->base + reg ); #else struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 4, 0 }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); return req.val; #endif } static uint16_t pci_device_freebsd_read16( struct pci_io_handle *handle, uint32_t reg ) { #if defined(__i386__) || defined(__amd64__) return inw( handle->base + reg ); #else struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 2, 0 }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); return req.val; #endif } static uint8_t pci_device_freebsd_read8( struct pci_io_handle *handle, uint32_t reg ) { #if defined(__i386__) || defined(__amd64__) return inb( handle->base + reg ); #else struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 1, 0 }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); return req.val; #endif } static void pci_device_freebsd_write32( struct pci_io_handle *handle, uint32_t reg, uint32_t data ) { #if defined(__i386__) || defined(__amd64__) outl( handle->base + reg, data ); #else struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 4, data }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); #endif } static void pci_device_freebsd_write16( struct pci_io_handle *handle, uint32_t reg, uint16_t data ) { #if defined(__i386__) || defined(__amd64__) outw( handle->base + reg, data ); #else struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 2, data }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); #endif } static void pci_device_freebsd_write8( struct pci_io_handle *handle, uint32_t reg, uint8_t data ) { #if defined(__i386__) || defined(__amd64__) outb( handle->base + reg, data ); #else struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 1, data }; if ( handle->fd > -1 ) ioctl( handle->fd, IODEV_PIO, &req ); #endif } static int pci_device_freebsd_map_legacy( struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr ) { struct pci_device_mapping map; int err; map.base = base; map.size = size; map.flags = map_flags; map.memory = NULL; err = pci_device_freebsd_map_range( dev, &map ); *addr = map.memory; return err; } static int pci_device_freebsd_unmap_legacy( struct pci_device *dev, void *addr, pciaddr_t size ) { struct pci_device_mapping map; map.memory = addr; map.size = size; map.flags = 0; return pci_device_freebsd_unmap_range( dev, &map ); } static const struct pci_system_methods freebsd_pci_methods = { .destroy = pci_system_freebsd_destroy, .destroy_device = NULL, /* nothing to do for this */ .read_rom = pci_device_freebsd_read_rom, .probe = pci_device_freebsd_probe, .map_range = pci_device_freebsd_map_range, .unmap_range = pci_device_freebsd_unmap_range, .read = pci_device_freebsd_read, .write = pci_device_freebsd_write, .fill_capabilities = pci_fill_capabilities_generic, .enable = NULL, .boot_vga = NULL, .has_kernel_driver = pci_device_freebsd_has_kernel_driver, .open_device_io = pci_device_freebsd_open_io, .open_legacy_io = pci_device_freebsd_open_legacy_io, .close_io = pci_device_freebsd_close_io, .read32 = pci_device_freebsd_read32, .read16 = pci_device_freebsd_read16, .read8 = pci_device_freebsd_read8, .write32 = pci_device_freebsd_write32, .write16 = pci_device_freebsd_write16, .write8 = pci_device_freebsd_write8, .map_legacy = pci_device_freebsd_map_legacy, .unmap_legacy = pci_device_freebsd_unmap_legacy, }; /** * Attempt to access the FreeBSD PCI interface. */ _pci_hidden int pci_system_freebsd_create( void ) { struct pci_conf_io pciconfio; struct pci_conf pciconf[255]; int pcidev; int i; /* Try to open the PCI device */ pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); if ( pcidev == -1 ) return ENXIO; freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) ); if ( freebsd_pci_sys == NULL ) { close( pcidev ); return ENOMEM; } pci_sys = &freebsd_pci_sys->pci_sys; pci_sys->methods = & freebsd_pci_methods; freebsd_pci_sys->pcidev = pcidev; /* Probe the list of devices known by the system */ bzero( &pciconfio, sizeof( struct pci_conf_io ) ); pciconfio.match_buf_len = sizeof(pciconf); pciconfio.matches = pciconf; if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) { free( pci_sys ); pci_sys = NULL; close( pcidev ); return errno; } if (pciconfio.status == PCI_GETCONF_ERROR ) { free( pci_sys ); pci_sys = NULL; close( pcidev ); return EINVAL; } /* Translate the list of devices into pciaccess's format. */ pci_sys->num_devices = pciconfio.num_matches; pci_sys->devices = calloc( pciconfio.num_matches, sizeof( struct pci_device_private ) ); for ( i = 0; i < pciconfio.num_matches; i++ ) { struct pci_conf *p = &pciconf[ i ]; pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; pci_sys->devices[ i ].base.vendor_id = p->pc_vendor; pci_sys->devices[ i ].base.device_id = p->pc_device; pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; pci_sys->devices[ i ].base.revision = p->pc_revid; pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; } return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/hurd_pci.c0000664014310600000120000004770014577654164016005 0ustar00alancstaff/* * Copyright (c) 2018, Damien Zammit * Copyright (c) 2017, Joan Lledó * Copyright (c) 2009, 2012 Samuel Thibault * Heavily inspired from the freebsd, netbsd, and openbsd backends * (C) Copyright Eric Anholt 2006 * (C) Copyright IBM Corporation 2006 * Copyright (c) 2008 Juan Romero Pardines * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "x86_pci.h" #include "pciaccess.h" #include "pciaccess_private.h" /* * Hurd PCI access using RPCs. * * Some functions are shared with the x86 module to avoid repeating code. */ /* Server path */ #define _SERVERS_BUS_PCI _SERVERS_BUS "/pci" /* File names */ #define FILE_CONFIG_NAME "config" #define FILE_REGION_NAME "region" #define FILE_ROM_NAME "rom" #define LEGACY_REGION -1 /* Level in the fs tree */ typedef enum { LEVEL_NONE, LEVEL_DOMAIN, LEVEL_BUS, LEVEL_DEV, LEVEL_FUNC } tree_level; struct pci_system_hurd { struct pci_system system; mach_port_t root; }; static int pci_device_hurd_probe(struct pci_device *dev) { uint8_t irq; int err, i; struct pci_bar regions[6]; struct pci_xrom_bar rom; struct pci_device_private *d; mach_msg_type_number_t size; char *buf; /* Many of the fields were filled in during initial device enumeration. * At this point, we need to fill in regions, rom_size, and irq. */ err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ); if (err) return err; dev->irq = irq; /* Get regions */ buf = (char *)®ions; size = sizeof(regions); d = (struct pci_device_private *)dev; err = pci_get_dev_regions(d->device_port, &buf, &size); if(err) return err; if((char*)®ions != buf) { /* Sanity check for bogus server. */ if(size > sizeof(regions)) { vm_deallocate(mach_task_self(), (vm_address_t) buf, size); return EGRATUITOUS; } memcpy(®ions, buf, size); vm_deallocate(mach_task_self(), (vm_address_t) buf, size); } for(i=0; i<6; i++) { if(regions[i].size == 0) continue; dev->regions[i].base_addr = regions[i].base_addr; dev->regions[i].size = regions[i].size; dev->regions[i].is_IO = regions[i].is_IO; dev->regions[i].is_prefetchable = regions[i].is_prefetchable; dev->regions[i].is_64 = regions[i].is_64; } /* Get rom info */ buf = (char *)&rom; size = sizeof(rom); err = pci_get_dev_rom(d->device_port, &buf, &size); if(err) return err; if((char*)&rom != buf) { /* Sanity check for bogus server. */ if(size > sizeof(rom)) { vm_deallocate(mach_task_self(), (vm_address_t) buf, size); return EGRATUITOUS; } memcpy(&rom, buf, size); vm_deallocate(mach_task_self(), (vm_address_t) buf, size); } d->rom_base = rom.base_addr; dev->rom_size = rom.size; return 0; } static void pci_system_hurd_destroy(void) { struct pci_system_hurd *pci_sys_hurd = (struct pci_system_hurd *)pci_sys; x86_disable_io(); mach_port_deallocate(mach_task_self(), pci_sys_hurd->root); } static int pci_device_hurd_map_range(struct pci_device *dev, struct pci_device_mapping *map) { #if 0 struct pci_device_private *d = (struct pci_device_private *)dev; #endif struct pci_system_hurd *pci_sys_hurd = (struct pci_system_hurd *)pci_sys; int err = 0; file_t file = MACH_PORT_NULL; memory_object_t robj, wobj, pager; vm_offset_t offset; vm_prot_t prot = VM_PROT_READ; const struct pci_mem_region *region; const struct pci_mem_region rom_region = { #if 0 /* FIXME: We should be doing this: */ .base_addr = d->rom_base, /* But currently pci-arbiter cannot get rom_base from libpciaccess, so we are here assuming the caller is mapping from the rom base */ #else .base_addr = map->base, #endif .size = dev->rom_size, }; int flags = O_RDONLY; char server[NAME_MAX]; if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) { prot |= VM_PROT_WRITE; flags = O_RDWR; } if (map->region != LEGACY_REGION) { region = &dev->regions[map->region]; snprintf(server, NAME_MAX, "%04x/%02x/%02x/%01u/%s%01u", dev->domain, dev->bus, dev->dev, dev->func, FILE_REGION_NAME, map->region); } else { region = &rom_region; snprintf(server, NAME_MAX, "%04x/%02x/%02x/%01u/%s", dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME); if (map->base < region->base_addr || map->base + map->size > region->base_addr + region->size) return EINVAL; } file = file_name_lookup_under (pci_sys_hurd->root, server, flags, 0); if (! MACH_PORT_VALID (file)) { return errno; } err = io_map (file, &robj, &wobj); mach_port_deallocate (mach_task_self(), file); if (err) return err; switch (prot & (VM_PROT_READ|VM_PROT_WRITE)) { case VM_PROT_READ: pager = robj; if (wobj != MACH_PORT_NULL) mach_port_deallocate (mach_task_self(), wobj); break; case VM_PROT_READ|VM_PROT_WRITE: if (robj == wobj) { if (robj == MACH_PORT_NULL) return EPERM; pager = wobj; /* Remove extra reference. */ mach_port_deallocate (mach_task_self (), pager); } else { if (robj != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), robj); if (wobj != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), wobj); return EPERM; } break; default: return EINVAL; } offset = map->base - region->base_addr; err = vm_map (mach_task_self (), (vm_address_t *)&map->memory, map->size, 0, 1, pager, /* a memory object proxy containing only the region */ offset, /* offset from region start */ 0, prot, VM_PROT_ALL, VM_INHERIT_SHARE); mach_port_deallocate (mach_task_self(), pager); return err; } static int pci_device_hurd_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { int err; err = pci_device_generic_unmap_range(dev, map); map->memory = NULL; return err; } static int pci_device_hurd_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_mapping map; int err; if (base >= 0xC0000 && base < 0x100000) { /* FIXME: We would rather know for sure from d->rom_base whether this is the ROM or not but currently pci-arbiter cannot get rom_base from libpciaccess, so we are here assuming the caller is mapping from the rom base */ map.base = base; map.size = size; map.flags = map_flags; map.region = LEGACY_REGION; err = pci_device_hurd_map_range(dev, &map); *addr = map.memory; if (!err) return 0; } /* This is probably not the ROM, this is probably something like VRam or the interrupt table, just map it by hand. */ return pci_system_x86_map_dev_mem(addr, base, size, !!(map_flags & PCI_DEV_MAP_FLAG_WRITABLE)); } static int pci_device_hurd_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map; map.size = size; map.flags = 0; map.region = LEGACY_REGION; map.memory = addr; return pci_device_hurd_unmap_range(dev, &map); } /* * Read `nbytes' bytes from `reg' in device's configuration space * and store them in `buf'. * * It's assumed that `nbytes' bytes are allocated in `buf' */ static int pciclient_cfg_read(mach_port_t device_port, int reg, char *buf, size_t * nbytes) { int err; mach_msg_type_number_t nread; char *data; data = buf; nread = *nbytes; err = __pci_conf_read(device_port, reg, &data, &nread, *nbytes); if (err) return err; if (data != buf) { if (nread > *nbytes) /* Sanity check for bogus server. */ { vm_deallocate(mach_task_self(), (vm_address_t) data, nread); return EGRATUITOUS; } memcpy(buf, data, nread); vm_deallocate(mach_task_self(), (vm_address_t)data, nread); } *nbytes = nread; return 0; } /* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */ static int pciclient_cfg_write(mach_port_t device_port, int reg, char *buf, size_t * nbytes) { int err; size_t nwrote; err = __pci_conf_write(device_port, reg, buf, *nbytes, &nwrote); if (!err) *nbytes = nwrote; return err; } /* * Read up to `size' bytes from `dev' configuration space to `data' starting * at `offset'. Write the amount on read bytes in `bytes_read'. */ static int pci_device_hurd_read(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { int err; struct pci_device_private *d; *bytes_read = 0; d = (struct pci_device_private *)dev; while (size > 0) { size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); if (toread > size) toread = size; err = pciclient_cfg_read(d->device_port, offset, (char*)data, &toread); if (err) return err; offset += toread; data = (char*)data + toread; size -= toread; *bytes_read += toread; } return 0; } /* * Write up to `size' bytes from `data' to `dev' configuration space starting * at `offset'. Write the amount on written bytes in `bytes_written'. */ static int pci_device_hurd_write(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { int err; struct pci_device_private *d; *bytes_written = 0; d = (struct pci_device_private *)dev; while (size > 0) { size_t towrite = 4; if (towrite > size) towrite = size; if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); err = pciclient_cfg_write(d->device_port, offset, (char*)data, &towrite); if (err) return err; offset += towrite; data = (const char*)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } /* * Copy the device's firmware in `buffer' */ static int pci_device_hurd_read_rom(struct pci_device * dev, void * buffer) { ssize_t rd; int romfd; char server[NAME_MAX]; snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME); romfd = open(server, O_RDONLY | O_CLOEXEC); if (romfd == -1) return errno; rd = read(romfd, buffer, dev->rom_size); if (rd != dev->rom_size) { close(romfd); return errno; } close(romfd); return 0; } /* * Each device has its own server where send RPC's to. * * Deallocate the port before destroying the device. */ static void pci_device_hurd_destroy_device(struct pci_device *dev) { struct pci_device_private *d = (struct pci_device_private*) dev; mach_port_deallocate (mach_task_self (), d->device_port); } static struct dirent64 * simple_readdir(mach_port_t port, uint32_t *first_entry) { char *data; int nentries = 0; mach_msg_type_number_t size; dir_readdir (port, &data, &size, *first_entry, 1, 0, &nentries); if (nentries == 0) { return NULL; } *first_entry = *first_entry + 1; return (struct dirent64 *)data; } /* Walk through the FS tree to see what is allowed for us */ static int enum_devices(mach_port_t pci_port, const char *parent, int domain, int bus, int dev, int func, tree_level lev) { int err, ret; struct dirent64 *entry = NULL; char path[NAME_MAX]; char server[NAME_MAX]; uint32_t reg, count = 0; size_t toread; mach_port_t cwd_port, device_port; struct pci_device_private *d, *devices; if (lev > LEVEL_FUNC + 1) { return 0; } cwd_port = file_name_lookup_under (pci_port, parent, O_DIRECTORY | O_RDONLY | O_EXEC, 0); if (cwd_port == MACH_PORT_NULL) { return 0; } while ((entry = simple_readdir(cwd_port, &count)) != NULL) { snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name); if (entry->d_type == DT_DIR) { if (!strncmp(entry->d_name, ".", NAME_MAX) || !strncmp(entry->d_name, "..", NAME_MAX)) continue; errno = 0; ret = strtol(entry->d_name, 0, 16); if (errno) { return errno; } /* * We found a valid directory. * Update the address and switch to the next level. */ switch (lev) { case LEVEL_DOMAIN: domain = ret; break; case LEVEL_BUS: bus = ret; break; case LEVEL_DEV: dev = ret; break; case LEVEL_FUNC: func = ret; break; default: return 0; } err = enum_devices(pci_port, path, domain, bus, dev, func, lev+1); if (err && err != EPERM && err != EACCES) { return 0; } } else { if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX)) /* We are looking for the config file */ continue; /* We found an available virtual device, add it to our list */ snprintf(server, NAME_MAX, "./%04x/%02x/%02x/%01u/%s", domain, bus, dev, func, entry->d_name); device_port = file_name_lookup_under(pci_port, server, O_RDONLY, 0); if (device_port == MACH_PORT_NULL) { return 0; } toread = sizeof(reg); err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)®, &toread); if (err) { mach_port_deallocate (mach_task_self (), device_port); return err; } if (toread != sizeof(reg)) { mach_port_deallocate (mach_task_self (), device_port); return -1; } devices = realloc(pci_sys->devices, (pci_sys->num_devices + 1) * sizeof(struct pci_device_private)); if (!devices) { mach_port_deallocate (mach_task_self (), device_port); return ENOMEM; } d = devices + pci_sys->num_devices; memset(d, 0, sizeof(struct pci_device_private)); d->base.domain = domain; d->base.bus = bus; d->base.dev = dev; d->base.func = func; d->base.vendor_id = PCI_VENDOR(reg); d->base.device_id = PCI_DEVICE(reg); toread = sizeof(reg); err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)®, &toread); if (err) { mach_port_deallocate (mach_task_self (), device_port); return err; } if (toread != sizeof(reg)) { mach_port_deallocate (mach_task_self (), device_port); return -1; } d->base.device_class = reg >> 8; d->base.revision = reg & 0xFF; toread = sizeof(reg); err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID, (char*)®, &toread); if (err) { mach_port_deallocate (mach_task_self (), device_port); return err; } if (toread != sizeof(reg)) { mach_port_deallocate (mach_task_self (), device_port); return -1; } d->base.subvendor_id = PCI_VENDOR(reg); d->base.subdevice_id = PCI_DEVICE(reg); d->device_port = device_port; pci_sys->devices = devices; pci_sys->num_devices++; } } mach_port_deallocate (mach_task_self (), cwd_port); return 0; } static const struct pci_system_methods hurd_pci_methods = { .destroy = pci_system_hurd_destroy, .destroy_device = pci_device_hurd_destroy_device, .read_rom = pci_device_hurd_read_rom, .probe = pci_device_hurd_probe, .map_range = pci_device_hurd_map_range, .unmap_range = pci_device_hurd_unmap_range, .read = pci_device_hurd_read, .write = pci_device_hurd_write, .fill_capabilities = pci_fill_capabilities_generic, .open_legacy_io = pci_device_x86_open_legacy_io, .close_io = pci_device_x86_close_io, .read32 = pci_device_x86_read32, .read16 = pci_device_x86_read16, .read8 = pci_device_x86_read8, .write32 = pci_device_x86_write32, .write16 = pci_device_x86_write16, .write8 = pci_device_x86_write8, .map_legacy = pci_device_hurd_map_legacy, .unmap_legacy = pci_device_hurd_unmap_legacy, }; /* Get the name of the server using libpciaccess if any */ extern char *netfs_server_name; #pragma weak netfs_server_name _pci_hidden int pci_system_hurd_create(void) { int err; struct pci_system_hurd *pci_sys_hurd; mach_port_t device_master; mach_port_t pci_port = MACH_PORT_NULL; mach_port_t root = MACH_PORT_NULL; if (&netfs_server_name && netfs_server_name && !strcmp(netfs_server_name, "pci-arbiter")) { /* We are a PCI arbiter, try the x86 way */ err = pci_system_x86_create(); if (!err) return 0; } /* * From this point on, we are either a client or a nested arbiter. * Both will connect to a master arbiter. */ pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd)); if (pci_sys_hurd == NULL) { x86_disable_io(); return ENOMEM; } pci_sys = &pci_sys_hurd->system; pci_sys->methods = &hurd_pci_methods; pci_sys->num_devices = 0; err = get_privileged_ports (NULL, &device_master); if(!err && device_master != MACH_PORT_NULL) { err = device_open (device_master, D_READ, "pci", &pci_port); mach_port_deallocate (mach_task_self (), device_master); } if (!err && pci_port != MACH_PORT_NULL) { root = file_name_lookup_under (pci_port, ".", O_DIRECTORY | O_RDONLY | O_EXEC, 0); device_close (pci_port); mach_port_deallocate (mach_task_self (), pci_port); } if (root == MACH_PORT_NULL) { root = file_name_lookup (_SERVERS_BUS_PCI, O_RDONLY, 0); } if (root == MACH_PORT_NULL) { pci_system_cleanup(); return errno; } pci_sys_hurd->root = root; err = enum_devices (root, ".", -1, -1, -1, -1, LEVEL_DOMAIN); if (err) { pci_system_cleanup(); return err; } return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/linux_devmem.c0000664014310600000120000000747414577654164016710 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2007 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file linux_devmem.c * Access PCI subsystem using Linux's the old /dev/mem interface. * * \note * This is currently just a skeleton. It only includes the /dev/mem based * function for reading the device ROM. * * \author Ian Romanick */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #include "linux_devmem.h" /** * Read a device's expansion ROM using /dev/mem. * * \note * This function could probably be used, as-is, on other platforms that have * a /dev/mem interface. * * \bugs * Before using the VGA special case code, this function should check that * VGA access are routed to the device. Right? */ _pci_hidden int pci_device_linux_devmem_read_rom(struct pci_device *dev, void *buffer) { struct pci_device_private *priv = (struct pci_device_private *) dev; int fd; int err = 0; uint32_t rom_base_tmp; pciaddr_t rom_base; pciaddr_t rom_size; int PCI_ROM; /* Handle some special cases of legacy devices. */ if (priv->base.rom_size == 0) { /* VGA ROMs are supposed to be at 0xC0000. */ if ((priv->base.device_class & 0x00ffff00) == 0x000030000) { rom_base = 0x000C0000; rom_size = 0x00010000; PCI_ROM = 0; } else { /* "Function not implemented." */ return ENOSYS; } } else { rom_base = priv->rom_base; rom_size = priv->base.rom_size; PCI_ROM = 1; } /* Enable the device's ROM. */ if (PCI_ROM) { err = pci_device_cfg_read_u32(& priv->base, & rom_base_tmp, 48); if (err) { return err; } if ((rom_base_tmp & 0x000000001) == 0) { err = pci_device_cfg_write_u32(& priv->base, rom_base_tmp | 1, 48); if (err) { return err; } } } /* Read the portion of /dev/mem that corresponds to the device's ROM. */ fd = open("/dev/mem", O_RDONLY, 0); if (fd < 0) { err = errno; } else { size_t bytes; for (bytes = 0; bytes < rom_size; /* empty */) { const ssize_t got = pread(fd, buffer, rom_size - bytes, rom_base + bytes); if (got == -1) { err = errno; break; } bytes += got; } close(fd); } /* Disable the device's ROM. */ if (PCI_ROM && ((rom_base_tmp & 0x000000001) == 0)) { const int tmp_err = pci_device_cfg_write_u32(& priv->base, rom_base_tmp, 48); /* Prefer to return the first error that occurred. */ if (err == 0) { err = tmp_err; } } return err; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/linux_devmem.h0000664014310600000120000000273514577654164016710 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2007 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file linux_devmem.h * Functions and datastructures that are private to the /dev/mem based * back-end for pciaccess. * * \author Ian Romanick */ #ifndef LINUX_DEVMEM_H #define LINUX_DEVMEM_H extern int pci_device_linux_devmem_read_rom(struct pci_device *dev, void *buffer); #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/linux_sysfs.c0000664014310600000120000006303414577654164016574 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * Copyright 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file linux_sysfs.c * Access PCI subsystem using Linux's sysfs interface. This interface is * available starting somewhere in the late 2.5.x kernel phase, and is the * preferred method on all 2.6.x kernels. * * \author Ian Romanick */ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__x86_64__) #include #else #define inb(x) -1 #define inw(x) -1 #define inl(x) -1 #define outb(x,y) do {} while (0) #define outw(x,y) do {} while (0) #define outl(x,y) do {} while (0) #define iopl(x) -1 #endif #ifdef HAVE_MTRR #include #include #endif #include "pciaccess.h" #include "pciaccess_private.h" #include "linux_devmem.h" static const struct pci_system_methods linux_sysfs_methods; #define SYS_BUS_PCI "/sys/bus/pci/devices" static int pci_device_linux_sysfs_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ); static int populate_entries(struct pci_system * pci_sys); /** * Attempt to access PCI subsystem using Linux's sysfs interface. */ _pci_hidden int pci_system_linux_sysfs_create( void ) { int err = 0; struct stat st; /* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem * can be accessed using this interface. */ if ( stat( SYS_BUS_PCI, & st ) == 0 ) { pci_sys = calloc( 1, sizeof( struct pci_system ) ); if ( pci_sys != NULL ) { pci_sys->methods = & linux_sysfs_methods; #ifdef HAVE_MTRR pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY | O_CLOEXEC); #endif err = populate_entries(pci_sys); } else { err = ENOMEM; } } else { err = errno; } return err; } /** * Filter out the names "." and ".." from the scanned sysfs entries. * * \param d Directory entry being processed by \c scandir. * * \return * Zero if the entry name matches either "." or ".." * * \sa scandir, populate_entries */ static int scan_sys_pci_filter( const struct dirent * d ) { return !((strcmp( d->d_name, "." ) == 0) || (strcmp( d->d_name, ".." ) == 0)); } static int parse_separate_sysfs_files(struct pci_device * dev) { static const char *attrs[] = { "vendor", "device", "class", "revision", "subsystem_vendor", "subsystem_device", }; char name[256]; char resource[512]; uint64_t data[6]; int fd; int i; for (i = 0; i < 6; i++) { snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/%s", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, attrs[i]); fd = open(name, O_RDONLY | O_CLOEXEC); if (fd == -1) { return errno; } read(fd, resource, 512); resource[511] = '\0'; close(fd); data[i] = strtoull(resource, NULL, 16); } dev->vendor_id = data[0] & 0xffff; dev->device_id = data[1] & 0xffff; dev->device_class = data[2] & 0xffffff; dev->revision = data[3] & 0xff; dev->subvendor_id = data[4] & 0xffff; dev->subdevice_id = data[5] & 0xffff; return 0; } int populate_entries( struct pci_system * p ) { struct dirent ** devices = NULL; int n; int i; int err = 0; n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort ); if ( n > 0 ) { p->num_devices = n; p->devices = calloc( n, sizeof( struct pci_device_private ) ); if (p->devices != NULL) { for (i = 0 ; i < n ; i++) { uint8_t config[48]; pciaddr_t bytes; unsigned dom, bus, dev, func; struct pci_device_private *device = (struct pci_device_private *) &p->devices[i]; sscanf(devices[i]->d_name, "%x:%02x:%02x.%1u", & dom, & bus, & dev, & func); device->base.domain = dom; /* * Applications compiled with older versions do not expect * 32-bit domain numbers. To keep them working, we keep a 16-bit * version of the domain number at the previous location. */ if (dom > 0xffff) device->base.domain_16 = 0xffff; else device->base.domain_16 = dom; device->base.bus = bus; device->base.dev = dev; device->base.func = func; err = parse_separate_sysfs_files(& device->base); if (!err) continue; err = pci_device_linux_sysfs_read(& device->base, config, 0, 48, & bytes); if ((bytes == 48) && !err) { device->base.vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8); device->base.device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8); device->base.device_class = (uint32_t)config[9] + ((uint32_t)config[10] << 8) + ((uint32_t)config[11] << 16); device->base.revision = config[8]; device->base.subvendor_id = (uint16_t)config[44] + ((uint16_t)config[45] << 8); device->base.subdevice_id = (uint16_t)config[46] + ((uint16_t)config[47] << 8); } if (err) { break; } } } else { err = ENOMEM; } } for (i = 0; i < n; i++) free(devices[i]); free(devices); if (err) { free(p->devices); p->devices = NULL; p->num_devices = 0; } return err; } static int pci_device_linux_sysfs_probe( struct pci_device * dev ) { char name[256]; uint8_t config[256]; char resource[512]; int fd; pciaddr_t bytes; unsigned i; int err; err = pci_device_linux_sysfs_read( dev, config, 0, 256, & bytes ); if ( bytes >= 64 ) { struct pci_device_private *priv = (struct pci_device_private *) dev; dev->irq = config[60]; priv->header_type = config[14]; /* The PCI config registers can be used to obtain information * about the memory and I/O regions for the device. However, * doing so requires some tricky parsing (to correctly handle * 64-bit memory regions) and requires writing to the config * registers. Since we'd like to avoid having to deal with the * parsing issues and non-root users can write to PCI config * registers, we use a different file in the device's sysfs * directory called "resource". * * The resource file contains all of the needed information in * a format that is consistent across all platforms. Each BAR * and the expansion ROM have a single line of data containing * 3, 64-bit hex values: the first address in the region, * the last address in the region, and the region's flags. */ snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_RDONLY | O_CLOEXEC); if ( fd != -1 ) { char * next; pciaddr_t low_addr; pciaddr_t high_addr; pciaddr_t flags; bytes = read( fd, resource, 512 ); resource[511] = '\0'; close( fd ); next = resource; for ( i = 0 ; i < 6 ; i++ ) { dev->regions[i].base_addr = strtoull( next, & next, 16 ); high_addr = strtoull( next, & next, 16 ); flags = strtoull( next, & next, 16 ); if ( dev->regions[i].base_addr != 0 ) { dev->regions[i].size = (high_addr - dev->regions[i].base_addr) + 1; dev->regions[i].is_IO = (flags & 0x01) != 0; dev->regions[i].is_64 = (flags & 0x04) != 0; dev->regions[i].is_prefetchable = (flags & 0x08) != 0; } } low_addr = strtoull( next, & next, 16 ); high_addr = strtoull( next, & next, 16 ); flags = strtoull( next, & next, 16 ); if ( low_addr != 0 ) { priv->rom_base = low_addr; dev->rom_size = (high_addr - low_addr) + 1; } } } return err; } static int pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) { char name[256]; int fd; struct stat st; int err = 0; size_t rom_size; size_t total_bytes; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_RDWR | O_CLOEXEC); if ( fd == -1 ) { #ifdef LINUX_ROM /* If reading the ROM using sysfs fails, fall back to the old * /dev/mem based interface. * disable this for newer kernels using configure */ return pci_device_linux_devmem_read_rom(dev, buffer); #else return errno; #endif } if ( fstat( fd, & st ) == -1 ) { close( fd ); return errno; } rom_size = st.st_size; if ( rom_size == 0 ) rom_size = 0x10000; /* This is a quirky thing on Linux. Even though the ROM and the file * for the ROM in sysfs are read-only, the string "1" must be written to * the file to enable the ROM. After the data has been read, "0" must be * written to the file to disable the ROM. */ write( fd, "1", 1 ); lseek( fd, 0, SEEK_SET ); for ( total_bytes = 0 ; total_bytes < rom_size ; /* empty */ ) { const int bytes = read( fd, (char *) buffer + total_bytes, rom_size - total_bytes ); if ( bytes == -1 ) { err = errno; break; } else if ( bytes == 0 ) { break; } total_bytes += bytes; } lseek( fd, 0, SEEK_SET ); write( fd, "0", 1 ); close( fd ); return err; } static int pci_device_linux_sysfs_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ) { char name[256]; pciaddr_t temp_size = size; int err = 0; int fd; char *data_bytes = data; if ( bytes_read != NULL ) { *bytes_read = 0; } /* Each device has a directory under sysfs. Within that directory there * is a file named "config". This file used to access the PCI config * space. It is used here to obtain most of the information about the * device. */ snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_RDONLY | O_CLOEXEC); if ( fd == -1 ) { return errno; } while ( temp_size > 0 ) { const ssize_t bytes = pread( fd, data_bytes, temp_size, offset ); /* If zero bytes were read, then we assume it's the end of the * config file. */ if (bytes == 0) break; if ( bytes < 0 ) { err = errno; break; } temp_size -= bytes; offset += bytes; data_bytes += bytes; } if ( bytes_read != NULL ) { *bytes_read = size - temp_size; } close( fd ); return err; } static int pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_written ) { char name[256]; pciaddr_t temp_size = size; int err = 0; int fd; const char *data_bytes = data; if ( bytes_written != NULL ) { *bytes_written = 0; } /* Each device has a directory under sysfs. Within that directory there * is a file named "config". This file used to access the PCI config * space. It is used here to obtain most of the information about the * device. */ snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_WRONLY | O_CLOEXEC); if ( fd == -1 ) { return errno; } while ( temp_size > 0 ) { const ssize_t bytes = pwrite( fd, data_bytes, temp_size, offset ); /* If zero bytes were written, then we assume it's the end of the * config file. */ if ( bytes == 0 ) break; if ( bytes < 0 ) { err = errno; break; } temp_size -= bytes; offset += bytes; data_bytes += bytes; } if ( bytes_written != NULL ) { *bytes_written = size - temp_size; } close( fd ); return err; } static int pci_device_linux_sysfs_map_range_wc(struct pci_device *dev, struct pci_device_mapping *map) { char name[256]; int fd; const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? (PROT_READ | PROT_WRITE) : PROT_READ; const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? O_RDWR : O_RDONLY; const off_t offset = map->base - dev->regions[map->region].base_addr; snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_wc", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, map->region); fd = open(name, open_flags | O_CLOEXEC); if (fd == -1) return errno; map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); if (map->memory == MAP_FAILED) { map->memory = NULL; close(fd); return errno; } close(fd); return 0; } /** * Map a memory region for a device using the Linux sysfs interface. * * \param dev Device whose memory region is to be mapped. * \param map Parameters of the mapping that is to be created. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range * * \todo * Some older 2.6.x kernels don't implement the resourceN files. On those * systems /dev/mem must be used. On these systems it is also possible that * \c mmap64 may need to be used. */ static int pci_device_linux_sysfs_map_range(struct pci_device *dev, struct pci_device_mapping *map) { char name[256]; int fd; int err = 0; const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? (PROT_READ | PROT_WRITE) : PROT_READ; const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? O_RDWR : O_RDONLY; const off_t offset = map->base - dev->regions[map->region].base_addr; #ifdef HAVE_MTRR struct mtrr_sentry sentry = { .base = map->base, .size = map->size, .type = MTRR_TYPE_UNCACHABLE }; #endif /* For WC mappings, try sysfs resourceN_wc file first */ if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) && !pci_device_linux_sysfs_map_range_wc(dev, map)) return 0; snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, map->region); fd = open(name, open_flags | O_CLOEXEC); if (fd == -1) { return errno; } map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); if (map->memory == MAP_FAILED) { map->memory = NULL; close(fd); return errno; } #ifdef HAVE_MTRR if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { sentry.type = MTRR_TYPE_WRBACK; } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { sentry.type = MTRR_TYPE_WRCOMB; } if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) { /* FIXME: Should we report an error in this case? */ fprintf(stderr, "error setting MTRR " "(base = 0x%016" PRIx64 ", size = 0x%08x, type = %u) %s (%d)\n", (pciaddr_t)sentry.base, sentry.size, sentry.type, strerror(errno), errno); /* err = errno;*/ } /* KLUDGE ALERT -- rewrite the PTEs to turn off the CD and WT bits */ mprotect (map->memory, map->size, PROT_NONE); err = mprotect (map->memory, map->size, PROT_READ|PROT_WRITE); if (err != 0) { fprintf(stderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n", strerror(errno)); fprintf(stderr, "remapping without mprotect performance kludge.\n"); munmap(map->memory, map->size); map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); if (map->memory == MAP_FAILED) { map->memory = NULL; close(fd); return errno; } } } #endif close(fd); return 0; } /** * Unmap a memory region for a device using the Linux sysfs interface. * * \param dev Device whose memory region is to be unmapped. * \param map Parameters of the mapping that is to be destroyed. * * \return * Zero on success or an \c errno value on failure. * * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range * * \todo * Some older 2.6.x kernels don't implement the resourceN files. On those * systems /dev/mem must be used. On these systems it is also possible that * \c mmap64 may need to be used. */ static int pci_device_linux_sysfs_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { int err = 0; #ifdef HAVE_MTRR struct mtrr_sentry sentry = { .base = map->base, .size = map->size, .type = MTRR_TYPE_UNCACHABLE }; #endif err = pci_device_generic_unmap_range (dev, map); if (err) return err; #ifdef HAVE_MTRR if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { sentry.type = MTRR_TYPE_WRBACK; } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { sentry.type = MTRR_TYPE_WRCOMB; } if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) { /* FIXME: Should we report an error in this case? */ fprintf(stderr, "error setting MTRR " "(base = 0x%016" PRIx64 ", size = 0x%08x, type = %u) %s (%d)\n", (pciaddr_t)sentry.base, sentry.size, sentry.type, strerror(errno), errno); /* err = errno;*/ } } #endif return err; } static void pci_device_linux_sysfs_set_enable(struct pci_device *dev, int enable) { char name[256]; int fd; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_RDWR | O_CLOEXEC); if (fd == -1) return; write( fd, enable ? "1" : "0" , 1 ); close(fd); } static void pci_device_linux_sysfs_enable(struct pci_device *dev) { return pci_device_linux_sysfs_set_enable(dev, 1); } static void pci_device_linux_sysfs_disable(struct pci_device *dev) { return pci_device_linux_sysfs_set_enable(dev, 0); } static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev) { char name[256]; char reply[3]; int fd, bytes_read; int ret = 0; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/boot_vga", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); fd = open( name, O_RDONLY | O_CLOEXEC); if (fd == -1) return 0; bytes_read = read(fd, reply, 1); if (bytes_read != 1) goto out; if (reply[0] == '1') ret = 1; out: close(fd); return ret; } static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev) { char name[256]; struct stat dummy; int ret; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func ); ret = stat(name, &dummy); if (ret < 0) return 0; return 1; } static struct pci_io_handle * pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret, struct pci_device *dev, int bar, pciaddr_t base, pciaddr_t size) { char name[PATH_MAX]; snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d", SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar); ret->fd = open(name, O_RDWR | O_CLOEXEC); if (ret->fd < 0) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 0; return ret; } static struct pci_io_handle * pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { char name[PATH_MAX]; /* First check if there's a legacy io method for the device */ while (dev) { snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io", dev->domain, dev->bus); ret->fd = open(name, O_RDWR | O_CLOEXEC); if (ret->fd >= 0) break; dev = pci_device_get_parent_bridge(dev); } /* * You would think you'd want to use /dev/port here. Don't make that * mistake, /dev/port only does byte-wide i/o cycles which means it * doesn't work. If you think this is stupid, well, you're right. */ /* If we've no other choice, iopl */ if (ret->fd < 0) { if (iopl(3)) return NULL; } ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; } static void pci_device_linux_sysfs_close_io(struct pci_device *dev, struct pci_io_handle *handle) { if (handle->fd > -1) close(handle->fd); } static uint32_t pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port) { uint32_t ret; if (handle->fd > -1) { if (handle->is_legacy) pread(handle->fd, &ret, 4, port + handle->base); else pread(handle->fd, &ret, 4, port); } else { ret = inl(port + handle->base); } return ret; } static uint16_t pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port) { uint16_t ret; if (handle->fd > -1) { if (handle->is_legacy) pread(handle->fd, &ret, 2, port + handle->base); else pread(handle->fd, &ret, 2, port); } else { ret = inw(port + handle->base); } return ret; } static uint8_t pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port) { uint8_t ret; if (handle->fd > -1) { if (handle->is_legacy) pread(handle->fd, &ret, 1, port + handle->base); else pread(handle->fd, &ret, 1, port); } else { ret = inb(port + handle->base); } return ret; } static void pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port, uint32_t data) { if (handle->fd > -1) { if (handle->is_legacy) pwrite(handle->fd, &data, 4, port + handle->base); else pwrite(handle->fd, &data, 4, port); } else { outl(data, port + handle->base); } } static void pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port, uint16_t data) { if (handle->fd > -1) { if (handle->is_legacy) pwrite(handle->fd, &data, 2, port + handle->base); else pwrite(handle->fd, &data, 2, port); } else { outw(data, port + handle->base); } } static void pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port, uint8_t data) { if (handle->fd > -1) { if (handle->is_legacy) pwrite(handle->fd, &data, 1, port + handle->base); else pwrite(handle->fd, &data, 1, port); } else { outb(data, port + handle->base); } } static int pci_device_linux_sysfs_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { char name[PATH_MAX]; int flags = O_RDONLY; int prot = PROT_READ; int fd; int ret=0; if (map_flags & PCI_DEV_MAP_FLAG_WRITABLE) { flags = O_RDWR; /* O_RDWR != O_WRONLY | O_RDONLY */ prot |= PROT_WRITE; } /* First check if there's a legacy memory method for the device */ while (dev) { snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_mem", dev->domain, dev->bus); fd = open(name, flags | O_CLOEXEC); if (fd >= 0) break; dev = pci_device_get_parent_bridge(dev); } /* If not, /dev/mem is the best we can do */ if (!dev) fd = open("/dev/mem", flags | O_CLOEXEC); if (fd < 0) return errno; *addr = mmap(NULL, size, prot, MAP_SHARED, fd, base); if (*addr == MAP_FAILED) { ret = errno; } close(fd); return ret; } static int pci_device_linux_sysfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { return munmap(addr, size); } static void pci_system_linux_destroy(void) { #ifdef HAVE_MTRR if (pci_sys->mtrr_fd != -1) close(pci_sys->mtrr_fd); #endif } static const struct pci_system_methods linux_sysfs_methods = { .destroy = pci_system_linux_destroy, .destroy_device = NULL, .read_rom = pci_device_linux_sysfs_read_rom, .probe = pci_device_linux_sysfs_probe, .map_range = pci_device_linux_sysfs_map_range, .unmap_range = pci_device_linux_sysfs_unmap_range, .read = pci_device_linux_sysfs_read, .write = pci_device_linux_sysfs_write, .fill_capabilities = pci_fill_capabilities_generic, .enable = pci_device_linux_sysfs_enable, .disable = pci_device_linux_sysfs_disable, .boot_vga = pci_device_linux_sysfs_boot_vga, .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver, .open_device_io = pci_device_linux_sysfs_open_device_io, .open_legacy_io = pci_device_linux_sysfs_open_legacy_io, .close_io = pci_device_linux_sysfs_close_io, .read32 = pci_device_linux_sysfs_read32, .read16 = pci_device_linux_sysfs_read16, .read8 = pci_device_linux_sysfs_read8, .write32 = pci_device_linux_sysfs_write32, .write16 = pci_device_linux_sysfs_write16, .write8 = pci_device_linux_sysfs_write8, .map_legacy = pci_device_linux_sysfs_map_legacy, .unmap_legacy = pci_device_linux_sysfs_unmap_legacy, }; ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/meson.build0000664014310600000120000000471114577654164016201 0ustar00alancstaff# Copyright © 2020 Intel Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. host = host_machine.system() _pci_access_host_files = [] if host == 'linux' _pci_access_host_files += ['linux_sysfs.c', 'linux_devmem.c', 'common_vgaarb.c'] elif host == 'freebsd' _pci_access_host_files += ['freebsd_pci.c', 'common_vgaarb_stub.c'] elif host == 'netbsd' _pci_access_host_files += ['netbsd_pci.c', 'common_vgaarb_stub.c'] elif host == 'openbsd' _pci_access_host_files += ['openbsd_pci.c'] # VGA arbiter code is in netbsd_pci.c elif host == 'cygwin' _pci_access_host_files += ['x86_pci.c', 'common_vgaarb_stub.c'] elif host == 'sunos' _pci_access_host_files += ['solx_devfs.c', 'common_vgaarb_stub.c'] elif host == 'gnu' _pci_access_host_files += ['hurd_pci.c', 'x86_pci.c', 'common_vgaarb_stub.c'] endif inc_src = include_directories('.') libpciaccess = library( 'pciaccess', [ 'common_bridge.c', 'common_iterator.c', 'common_init.c', 'common_interface.c', 'common_io.c', 'common_capability.c', 'common_device_name.c', 'common_map.c', _pci_access_host_files, config_h, ], include_directories : inc_include, dependencies : [dep_zlib, extra_libs], version : '0.11.1', install : true, ) dep_pciaccess = declare_dependency( link_with : libpciaccess, include_directories : [include_directories('.'), inc_include] ) if meson.version().version_compare('>= 0.54.0') meson.override_dependency('pciaccess', dep_pciaccess) endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/netbsd_pci.c0000664014310600000120000006023614577654164016321 0ustar00alancstaff/* * Copyright (c) 2008 Juan Romero Pardines * Copyright (c) 2008 Mark Kettenis * Copyright (c) 2009 Michael Lorenz * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_MTRR #include #include #ifdef _X86_SYSARCH_L /* NetBSD 5.x and newer */ #define netbsd_set_mtrr(mr, num) _X86_SYSARCH_L(set_mtrr)(mr, num) #else /* NetBSD 4.x and older */ #ifdef __i386__ #define netbsd_set_mtrr(mr, num) i386_set_mtrr((mr), (num)) #endif #ifdef __amd64__ #define netbsd_set_mtrr(mr, num) x86_64_set_mtrr((mr), (num)) #endif #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" typedef struct _pcibus { int fd; /* /dev/pci* */ int num; /* bus number */ int maxdevs; /* maximum number of devices */ } PciBus; static PciBus buses[32]; /* indexed by pci_device.domain */ static int nbuses = 0; /* number of buses found */ /* * NetBSD's userland has a /dev/pci* entry for each bus but userland has no way * to tell if a bus is a subordinate of another one or if it's on a different * host bridge. On some architectures ( macppc for example ) all root buses have * bus number 0 but on sparc64 for example the two roots in an Ultra60 have * different bus numbers - one is 0 and the other 128. * With each /dev/pci* we can map everything on the same root and we can also * see all devices on the same root, trying to do that causes problems though: * - since we can't tell which /dev/pci* is a subordinate we would find some * devices more than once * - we would have to guess subordinate bus numbers which is a waste of time * since we can ask each /dev/pci* for its bus number so we can scan only the * buses we know exist, not all 256 which may exist in each domain. * - some bus_space_mmap() methods may limit mappings to address ranges which * belong to known devices on that bus only. * Each host bridge may or may not have its own IO range, to avoid guesswork * here each /dev/pci* will let userland map its appropriate IO range at * PCI_MAGIC_IO_RANGE if defined in * With all this we should be able to use any PCI graphics device on any PCI * bus on any architecture as long as Xorg has a driver, without allowing * arbitrary mappings via /dev/mem and without userland having to know or care * about translating bus addresses to physical addresses or the other way * around. */ static int pci_read(int domain, int bus, int dev, int func, uint32_t reg, uint32_t *val) { uint32_t rval; if ((domain < 0) || (domain > nbuses)) return -1; if (pcibus_conf_read(buses[domain].fd, (unsigned int)bus, (unsigned int)dev, (unsigned int)func, reg, &rval) == -1) return (-1); *val = rval; return 0; } static int pci_write(int domain, int bus, int dev, int func, uint32_t reg, uint32_t val) { if ((domain < 0) || (domain > nbuses)) return -1; return pcibus_conf_write(buses[domain].fd, (unsigned int)bus, (unsigned int)dev, (unsigned int)func, reg, val); } static int pci_nfuncs(int domain, int bus, int dev) { uint32_t hdr; if ((domain < 0) || (domain > nbuses)) return -1; if (pci_read(domain, bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) return -1; return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); } /*ARGSUSED*/ static int pci_device_netbsd_map_range(struct pci_device *dev, struct pci_device_mapping *map) { #ifdef HAVE_MTRR struct mtrr m; int n = 1; #endif int prot, ret = 0; prot = PROT_READ; if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) prot |= PROT_WRITE; map->memory = mmap(NULL, (size_t)map->size, prot, MAP_SHARED, buses[dev->domain].fd, (off_t)map->base); if (map->memory == MAP_FAILED) return errno; #ifdef HAVE_MTRR memset(&m, 0, sizeof(m)); /* 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)) { m.base = map->base; m.flags = MTRR_VALID | MTRR_PRIVATE; m.len = map->size; m.owner = getpid(); if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) m.type = MTRR_TYPE_WB; if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) m.type = MTRR_TYPE_WC; if ((netbsd_set_mtrr(&m, &n)) == -1) { fprintf(stderr, "mtrr set failed: %s\n", strerror(errno)); } } #endif return ret; } static int pci_device_netbsd_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { #ifdef HAVE_MTRR struct mtrr m; int n = 1; memset(&m, 0, sizeof(m)); if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { m.base = map->base; m.flags = 0; m.len = map->size; m.type = MTRR_TYPE_UC; (void)netbsd_set_mtrr(&m, &n); } #endif return pci_device_generic_unmap_range(dev, map); } static int pci_device_netbsd_read(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { u_int reg, rval; *bytes_read = 0; while (size > 0) { size_t toread = MIN(size, 4 - (offset & 0x3)); reg = (u_int)(offset & ~0x3); if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, reg, &rval)) == -1) return errno; rval = htole32(rval); rval >>= ((offset & 0x3) * 8); memcpy(data, &rval, toread); offset += toread; data = (char *)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_netbsd_write(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { u_int reg, val; if ((offset % 4) != 0 || (size % 4) != 0) return EINVAL; *bytes_written = 0; while (size > 0) { reg = (u_int)offset; memcpy(&val, data, 4); if ((pcibus_conf_write(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, reg, val)) == -1) return errno; offset += 4; data = (const char *)data + 4; size -= 4; *bytes_written += 4; } return 0; } #if defined(WSDISPLAYIO_GET_BUSID) static int pci_device_netbsd_boot_vga(struct pci_device *dev) { int ret; struct wsdisplayio_bus_id busid; int fd; fd = open("/dev/ttyE0", O_RDONLY); if (fd == -1) { fprintf(stderr, "failed to open /dev/ttyE0: %s\n", strerror(errno)); return 0; } ret = ioctl(fd, WSDISPLAYIO_GET_BUSID, &busid); close(fd); if (ret == -1) { fprintf(stderr, "ioctl WSDISPLAYIO_GET_BUSID failed: %s\n", strerror(errno)); return 0; } if (busid.bus_type != WSDISPLAYIO_BUS_PCI) return 0; if (busid.ubus.pci.domain != dev->domain) return 0; if (busid.ubus.pci.bus != dev->bus) return 0; if (busid.ubus.pci.device != dev->dev) return 0; if (busid.ubus.pci.function != dev->func) return 0; return 1; } #endif static void pci_system_netbsd_destroy(void) { int i; for (i = 0; i < nbuses; i++) { close(buses[i].fd); } free(pci_sys); pci_sys = NULL; } static int pci_device_netbsd_probe(struct pci_device *device) { struct pci_device_private *priv = (struct pci_device_private *)(void *)device; struct pci_mem_region *region; uint64_t reg64, size64; uint32_t bar, reg, size; int bus, dev, func, err, domain; domain = device->domain; bus = device->bus; dev = device->dev; func = device->func; /* Enable the device if necessary */ err = pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, ®); if (err) return err; if ((reg & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) { reg |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE; err = pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, reg); if (err) return err; } err = pci_read(domain, 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(domain, bus, dev, func, bar, ®); if (err) return err; /* Probe the size of the region. */ err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0); if (err) return err; pci_read(domain, bus, dev, func, bar, &size); pci_write(domain, 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(domain, bus, dev, func, bar, ®); if (err) return err; reg64 |= (uint64_t)reg << 32; err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0); if (err) return err; pci_read(domain, bus, dev, func, bar, &size); pci_write(domain, bus, dev, func, bar, (unsigned int)(reg64 >> 32)); size64 |= (uint64_t)size << 32; region->base_addr = (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64); region->size = (unsigned long)PCI_MAPREG_MEM64_SIZE(size64); region++; break; } } } /* Probe expansion ROM if present */ err = pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, ®); if (err) return err; if (reg != 0) { err = pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, (uint32_t)(~PCI_MAPREG_ROM_ENABLE)); if (err) return err; pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &size); pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, reg); if ((reg & PCI_MAPREG_MEM_ADDR_MASK) != 0) { priv->rom_base = reg & PCI_MAPREG_MEM_ADDR_MASK; device->rom_size = -(size & PCI_MAPREG_MEM_ADDR_MASK); } } return 0; } /** * Read a VGA rom using the 0xc0000 mapping. * * This function should be extended to handle access through PCI resources, * which should be more reliable when available. */ static int pci_device_netbsd_read_rom(struct pci_device *dev, void *buffer) { struct pci_device_private *priv = (struct pci_device_private *)(void *)dev; void *bios; pciaddr_t rom_base; size_t rom_size; uint32_t bios_val, command_val; int pci_rom; if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY || ((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA) return ENOSYS; if (priv->rom_base == 0) { #if defined(__amd64__) || defined(__i386__) /* * We need a way to detect when this isn't the console and reject * this request outright. */ rom_base = 0xc0000; rom_size = 0x10000; pci_rom = 0; #else return ENOSYS; #endif } else { rom_base = priv->rom_base; rom_size = dev->rom_size; pci_rom = 1; if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_COMMAND_STATUS_REG, &command_val)) == -1) return errno; if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { if ((pcibus_conf_write(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_COMMAND_STATUS_REG, command_val | PCI_COMMAND_MEM_ENABLE)) == -1) return errno; } if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_MAPREG_ROM, &bios_val)) == -1) return errno; if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { if ((pcibus_conf_write(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_MAPREG_ROM, bios_val | PCI_MAPREG_ROM_ENABLE)) == -1) return errno; } } fprintf(stderr, "Using rom_base = 0x%lx 0x%lx (pci_rom=%d)\n", (long)rom_base, (long)rom_size, pci_rom); bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, buses[dev->domain].fd, (off_t)rom_base); if (bios == MAP_FAILED) { int serrno = errno; return serrno; } memcpy(buffer, bios, rom_size); munmap(bios, rom_size); if (pci_rom) { if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { if ((pcibus_conf_write(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_COMMAND_STATUS_REG, command_val)) == -1) return errno; } if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { if ((pcibus_conf_write(buses[dev->domain].fd, (unsigned int)dev->bus, (unsigned int)dev->dev, (unsigned int)dev->func, PCI_MAPREG_ROM, bios_val)) == -1) return errno; } } return 0; } #if defined(__i386__) || defined(__amd64__) #include /* * Functions to provide access to x86 programmed I/O instructions. * * The in[bwl]() and out[bwl]() functions are split into two varieties: one to * use a small, constant, 8-bit port number, and another to use a large or * variable port number. The former can be compiled as a smaller instruction. */ #ifdef __OPTIMIZE__ #define __use_immediate_port(port) \ (__builtin_constant_p((port)) && (port) < 0x100) #else #define __use_immediate_port(port) 0 #endif #define inb(port) \ (/* CONSTCOND */ __use_immediate_port(port) ? __inbc(port) : __inb(port)) static __inline u_int8_t __inbc(unsigned port) { u_int8_t data; __asm __volatile("inb %w1,%0" : "=a" (data) : "id" (port)); return data; } static __inline u_int8_t __inb(unsigned port) { u_int8_t data; __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); return data; } static __inline void insb(unsigned port, void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\tinsb" : "=D" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt) : "memory"); } #define inw(port) \ (/* CONSTCOND */ __use_immediate_port(port) ? __inwc(port) : __inw(port)) static __inline u_int16_t __inwc(unsigned port) { u_int16_t data; __asm __volatile("inw %w1,%0" : "=a" (data) : "id" (port)); return data; } static __inline u_int16_t __inw(unsigned port) { u_int16_t data; __asm __volatile("inw %w1,%0" : "=a" (data) : "d" (port)); return data; } static __inline void insw(unsigned port, void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\tinsw" : "=D" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt) : "memory"); } #define inl(port) \ (/* CONSTCOND */ __use_immediate_port(port) ? __inlc(port) : __inl(port)) static __inline u_int32_t __inlc(unsigned port) { u_int32_t data; __asm __volatile("inl %w1,%0" : "=a" (data) : "id" (port)); return data; } static __inline u_int32_t __inl(unsigned port) { u_int32_t data; __asm __volatile("inl %w1,%0" : "=a" (data) : "d" (port)); return data; } static __inline void insl(unsigned port, void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\tinsl" : "=D" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt) : "memory"); } #define outb(port, data) \ (/* CONSTCOND */__use_immediate_port(port) ? __outbc(port, data) : \ __outb(port, data)) static __inline void __outbc(unsigned port, u_int8_t data) { __asm __volatile("outb %0,%w1" : : "a" (data), "id" (port)); } static __inline void __outb(unsigned port, u_int8_t data) { __asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); } static __inline void outsb(unsigned port, const void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\toutsb" : "=S" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt)); } #define outw(port, data) \ (/* CONSTCOND */ __use_immediate_port(port) ? __outwc(port, data) : \ __outw(port, data)) static __inline void __outwc(unsigned port, u_int16_t data) { __asm __volatile("outw %0,%w1" : : "a" (data), "id" (port)); } static __inline void __outw(unsigned port, u_int16_t data) { __asm __volatile("outw %0,%w1" : : "a" (data), "d" (port)); } static __inline void outsw(unsigned port, const void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\toutsw" : "=S" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt)); } #define outl(port, data) \ (/* CONSTCOND */ __use_immediate_port(port) ? __outlc(port, data) : \ __outl(port, data)) static __inline void __outlc(unsigned port, u_int32_t data) { __asm __volatile("outl %0,%w1" : : "a" (data), "id" (port)); } static __inline void __outl(unsigned port, u_int32_t data) { __asm __volatile("outl %0,%w1" : : "a" (data), "d" (port)); } static __inline void outsl(unsigned port, const void *addr, int cnt) { void *dummy1; int dummy2; __asm __volatile("cld\n\trepne\n\toutsl" : "=S" (dummy1), "=c" (dummy2) : "d" (port), "0" (addr), "1" (cnt)); } #endif static struct pci_io_handle * pci_device_netbsd_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { #if defined(__i386__) struct i386_iopl_args ia; ia.iopl = 1; if (sysarch(I386_IOPL, &ia)) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; #elif defined(__amd64__) struct x86_64_iopl_args ia; ia.iopl = 1; if (sysarch(X86_64_IOPL, &ia)) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; #else return NULL; #endif } static uint32_t pci_device_netbsd_read32(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inl(handle->base + reg); #else return *(uint32_t *)((uintptr_t)handle->memory + reg); #endif } static uint16_t pci_device_netbsd_read16(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inw(handle->base + reg); #else return *(uint16_t *)((uintptr_t)handle->memory + reg); #endif } static uint8_t pci_device_netbsd_read8(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inb(handle->base + reg); #else return *(uint8_t *)((uintptr_t)handle->memory + reg); #endif } static void pci_device_netbsd_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { #if defined(__i386__) || defined(__amd64__) outl(handle->base + reg, data); #else *(uint16_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_netbsd_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { #if defined(__i386__) || defined(__amd64__) outw(handle->base + reg, data); #else *(uint8_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_netbsd_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { #if defined(__i386__) || defined(__amd64__) outb(handle->base + reg, data); #else *(uint32_t *)((uintptr_t)handle->memory + reg) = data; #endif } static int pci_device_netbsd_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_mapping map; int err; map.base = base; map.size = size; map.flags = map_flags; map.memory = NULL; err = pci_device_netbsd_map_range(dev, &map); *addr = map.memory; return err; } static int pci_device_netbsd_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map; map.memory = addr; map.size = size; map.flags = 0; return pci_device_netbsd_unmap_range(dev, &map); } static int pci_device_netbsd_has_kernel_driver(struct pci_device *dev) { #ifdef PCI_IOC_DRVNAME /* * NetBSD PCI_IOC_DRVNAME appears at the same time as pci_drvname(3) */ char drvname[16]; if (dev->bus >= nbuses) return 0; /* * vga(4) should be considered "not bound". */ if (pci_drvname(buses[dev->bus].fd, dev->dev, dev->func, drvname, sizeof drvname) == 0 && strncmp(drvname, "vga", 3) != 0) return 1; #endif return 0; } static const struct pci_system_methods netbsd_pci_methods = { .destroy = pci_system_netbsd_destroy, .destroy_device = NULL, .read_rom = pci_device_netbsd_read_rom, .probe = pci_device_netbsd_probe, .map_range = pci_device_netbsd_map_range, .unmap_range = pci_device_netbsd_unmap_range, .read = pci_device_netbsd_read, .write = pci_device_netbsd_write, .fill_capabilities = pci_fill_capabilities_generic, #if defined(WSDISPLAYIO_GET_BUSID) .boot_vga = pci_device_netbsd_boot_vga, #else .boot_vga = NULL, #endif .open_legacy_io = pci_device_netbsd_open_legacy_io, .read32 = pci_device_netbsd_read32, .read16 = pci_device_netbsd_read16, .read8 = pci_device_netbsd_read8, .write32 = pci_device_netbsd_write32, .write16 = pci_device_netbsd_write16, .write8 = pci_device_netbsd_write8, .map_legacy = pci_device_netbsd_map_legacy, .unmap_legacy = pci_device_netbsd_unmap_legacy, .has_kernel_driver = pci_device_netbsd_has_kernel_driver, }; int pci_system_netbsd_create(void) { struct pci_device_private *device; int bus, dev, func, ndevs, nfuncs, domain, pcifd; uint32_t reg; char netbsd_devname[32]; struct pciio_businfo businfo; pci_sys = calloc(1, sizeof(struct pci_system)); pci_sys->methods = &netbsd_pci_methods; ndevs = 0; nbuses = 0; snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses); pcifd = open(netbsd_devname, O_RDWR | O_CLOEXEC); while (pcifd > 0) { ioctl(pcifd, PCI_IOC_BUSINFO, &businfo); buses[nbuses].fd = pcifd; buses[nbuses].num = bus = businfo.busno; buses[nbuses].maxdevs = businfo.maxdevs; domain = nbuses; nbuses++; for (dev = 0; dev < businfo.maxdevs; dev++) { nfuncs = pci_nfuncs(domain, bus, dev); for (func = 0; func < nfuncs; func++) { if (pci_read(domain, bus, dev, func, PCI_ID_REG, ®) != 0) continue; if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || PCI_VENDOR(reg) == 0) continue; ndevs++; } } snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses); pcifd = open(netbsd_devname, O_RDWR); } pci_sys->num_devices = ndevs; pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); if (pci_sys->devices == NULL) { int i; for (i = 0; i < nbuses; i++) close(buses[i].fd); free(pci_sys); pci_sys = NULL; return ENOMEM; } device = pci_sys->devices; for (domain = 0; domain < nbuses; domain++) { bus = buses[domain].num; for (dev = 0; dev < buses[domain].maxdevs; dev++) { nfuncs = pci_nfuncs(domain, bus, dev); for (func = 0; func < nfuncs; func++) { if (pci_read(domain, bus, dev, func, PCI_ID_REG, ®) != 0) continue; if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || PCI_VENDOR(reg) == 0) continue; device->base.domain = domain; if (domain > 0xffff) device->base.domain_16 = 0xffff; else device->base.domain_16 = domain & 0xffff; 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(domain, 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(domain, bus, dev, func, PCI_SUBSYS_ID_REG, ®) != 0) continue; device->base.subvendor_id = PCI_VENDOR(reg); device->base.subdevice_id = PCI_PRODUCT(reg); device++; } } } return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/openbsd_pci.c0000664014310600000120000004457714577654164016506 0ustar00alancstaff/* * Copyright (c) 2008, 2011 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" /* * This should allow for 16 domains, which should cover everything * except perhaps the really big fridge-sized sparc64 server machines * that are unlikely to have any graphics hardware in them. */ static int pcifd[16]; static int ndomains; static int aperturefd = -1; static int pci_read(int domain, 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[domain], PCIOCREAD, &io); if (err) return (err); *val = io.pi_data; return 0; } static int pci_write(int domain, 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[domain], PCIOCWRITE, &io); } static int pci_readmask(int domain, 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[domain], PCIOCREADMASK, &io); if (err) return (err); *val = io.pi_data; return 0; } /** * Read a VGA ROM * */ static int pci_device_openbsd_read_rom(struct pci_device *device, void *buffer) { struct pci_device_private *priv = (struct pci_device_private *)device; unsigned char *bios; pciaddr_t rom_base; pciaddr_t rom_size; u_int32_t csr, rom; int pci_rom, domain, bus, dev, func; domain = device->domain; if (domain < 0 || domain >= ndomains) return ENXIO; bus = device->bus; dev = device->dev; func = device->func; if (aperturefd == -1) return ENOSYS; if (priv->base.rom_size == 0) { #if defined(__alpha__) || defined(__amd64__) || defined(__i386__) if ((device->device_class & 0x00ffff00) == ((PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8))) { rom_base = 0xc0000; rom_size = 0x10000; pci_rom = 0; } else #endif return ENOSYS; } else { rom_base = priv->rom_base; rom_size = priv->base.rom_size; pci_rom = 1; pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, &csr); pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, csr | PCI_COMMAND_MEM_ENABLE); pci_read(domain, bus, dev, func, PCI_ROM_REG, &rom); pci_write(domain, bus, dev, func, PCI_ROM_REG, rom | PCI_ROM_ENABLE); } bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, aperturefd, (off_t)rom_base); if (bios == MAP_FAILED) return errno; memcpy(buffer, bios, rom_size); munmap(bios, rom_size); if (pci_rom) { /* Restore PCI config space */ pci_write(domain, bus, dev, func, PCI_ROM_REG, rom); pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, csr); } return 0; } static int pci_nfuncs(int domain, int bus, int dev) { uint32_t hdr; if (domain < 0 || domain >= ndomains) return ENXIO; if (pci_read(domain, 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; #if defined(__i386__) || defined(__amd64__) /* 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)) (void)fprintf(stderr, "mtrr set failed: %s\n", strerror(errno)); } #endif return 0; } static int pci_device_openbsd_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { #if defined(__i386__) || defined(__amd64__) 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); } #endif 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[dev->domain], 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[dev->domain], 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) { int domain; for (domain = 0; domain < ndomains; domain++) close(pcifd[domain]); ndomains = 0; } 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 domain, bus, dev, func, err; domain = device->domain; bus = device->bus; dev = device->dev; func = device->func; err = pci_read(domain, 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(domain, bus, dev, func, bar, ®); if (err) return err; /* Probe the size of the region. */ err = pci_readmask(domain, bus, dev, func, bar, &size); if (err) return err; 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(domain, bus, dev, func, bar, ®); if (err) return err; reg64 |= (uint64_t)reg << 32; err = pci_readmask(domain, bus, dev, func, bar, &size); if (err) return err; size64 |= (uint64_t)size << 32; region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64); region->size = PCI_MAPREG_MEM64_SIZE(size64); region++; break; } } } /* Probe expansion ROM if present */ err = pci_read(domain, bus, dev, func, PCI_ROM_REG, ®); if (err) return err; if (reg != 0) { err = pci_write(domain, bus, dev, func, PCI_ROM_REG, ~PCI_ROM_ENABLE); if (err) return err; pci_read(domain, bus, dev, func, PCI_ROM_REG, &size); pci_write(domain, bus, dev, func, PCI_ROM_REG, reg); if (PCI_ROM_ADDR(reg) != 0) { priv->rom_base = PCI_ROM_ADDR(reg); device->rom_size = PCI_ROM_SIZE(size); } } return 0; } #if defined(__i386__) || defined(__amd64__) #include #include #endif static struct pci_io_handle * pci_device_openbsd_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { #if defined(__i386__) struct i386_iopl_args ia; ia.iopl = 1; if (sysarch(I386_IOPL, &ia)) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; #elif defined(__amd64__) struct amd64_iopl_args ia; ia.iopl = 1; if (sysarch(AMD64_IOPL, &ia)) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; #elif defined(PCI_MAGIC_IO_RANGE) ret->memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, aperturefd, PCI_MAGIC_IO_RANGE + base); if (ret->memory == MAP_FAILED) return NULL; ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; #else return NULL; #endif } static uint32_t pci_device_openbsd_read32(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inl(handle->base + reg); #else return *(uint32_t *)((uintptr_t)handle->memory + reg); #endif } static uint16_t pci_device_openbsd_read16(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inw(handle->base + reg); #else return *(uint16_t *)((uintptr_t)handle->memory + reg); #endif } static uint8_t pci_device_openbsd_read8(struct pci_io_handle *handle, uint32_t reg) { #if defined(__i386__) || defined(__amd64__) return inb(handle->base + reg); #else return *(uint8_t *)((uintptr_t)handle->memory + reg); #endif } static void pci_device_openbsd_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { #if defined(__i386__) || defined(__amd64__) outl(handle->base + reg, data); #else *(uint16_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_openbsd_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { #if defined(__i386__) || defined(__amd64__) outw(handle->base + reg, data); #else *(uint8_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_openbsd_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { #if defined(__i386__) || defined(__amd64__) outb(handle->base + reg, data); #else *(uint32_t *)((uintptr_t)handle->memory + reg) = data; #endif } static int pci_device_openbsd_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_mapping map; int err; map.base = base; map.size = size; map.flags = map_flags; map.memory = NULL; err = pci_device_openbsd_map_range(dev, &map); *addr = map.memory; return err; } static int pci_device_openbsd_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map; map.memory = addr; map.size = size; map.flags = 0; return pci_device_openbsd_unmap_range(dev, &map); } static const struct pci_system_methods openbsd_pci_methods = { pci_system_openbsd_destroy, NULL, pci_device_openbsd_read_rom, 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, NULL, NULL, NULL, NULL, pci_device_openbsd_open_legacy_io, NULL, pci_device_openbsd_read32, pci_device_openbsd_read16, pci_device_openbsd_read8, pci_device_openbsd_write32, pci_device_openbsd_write16, pci_device_openbsd_write8, pci_device_openbsd_map_legacy, pci_device_openbsd_unmap_legacy }; int pci_system_openbsd_create(void) { struct pci_device_private *device; int domain, bus, dev, func, ndevs, nfuncs; char path[MAXPATHLEN]; uint32_t reg; if (ndomains > 0) return 0; for (domain = 0; domain < sizeof(pcifd) / sizeof(pcifd[0]); domain++) { snprintf(path, sizeof(path), "/dev/pci%d", domain); pcifd[domain] = open(path, O_RDWR | O_CLOEXEC); if (pcifd[domain] == -1) break; ndomains++; } if (ndomains == 0) return ENXIO; pci_sys = calloc(1, sizeof(struct pci_system)); if (pci_sys == NULL) { for (domain = 0; domain < ndomains; domain++) close(pcifd[domain]); ndomains = 0; return ENOMEM; } pci_sys->methods = &openbsd_pci_methods; ndevs = 0; for (domain = 0; domain < ndomains; domain++) { for (bus = 0; bus < 256; bus++) { for (dev = 0; dev < 32; dev++) { nfuncs = pci_nfuncs(domain, bus, dev); for (func = 0; func < nfuncs; func++) { if (pci_read(domain, 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); pci_sys = NULL; for (domain = 0; domain < ndomains; domain++) close(pcifd[domain]); ndomains = 0; return ENOMEM; } device = pci_sys->devices; for (domain = 0; domain < ndomains; domain++) { for (bus = 0; bus < 256; bus++) { for (dev = 0; dev < 32; dev++) { nfuncs = pci_nfuncs(domain, bus, dev); for (func = 0; func < nfuncs; func++) { if (pci_read(domain, bus, dev, func, PCI_ID_REG, ®) != 0) continue; if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || PCI_VENDOR(reg) == 0) continue; device->base.domain = domain; if (domain > 0xffff) device->base.domain_16 = 0xffff; else device->base.domain_16 = domain & 0xffff; 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(domain, 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(domain, bus, dev, func, PCI_SUBVEND_0, ®) != 0) continue; device->base.subvendor_id = PCI_VENDOR(reg); device->base.subdevice_id = PCI_PRODUCT(reg); device->base.vgaarb_rsrc = VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM; device++; } } } } return 0; } void pci_system_openbsd_init_dev_mem(int fd) { aperturefd = fd; } int pci_device_vgaarb_init(void) { struct pci_device *dev = pci_sys->vga_target; struct pci_device_iterator *iter; struct pci_id_match vga_match = { PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, (PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8), 0x00ffff00 }; struct pci_vga pv; int err; pv.pv_sel.pc_bus = 0; pv.pv_sel.pc_dev = 0; pv.pv_sel.pc_func = 0; err = ioctl(pcifd[0], PCIOCGETVGA, &pv); if (err) return err; pci_sys->vga_target = pci_device_find_by_slot(0, pv.pv_sel.pc_bus, pv.pv_sel.pc_dev, pv.pv_sel.pc_func); /* Count the number of VGA devices in domain 0. */ iter = pci_id_match_iterator_create(&vga_match); if (iter == NULL) return -1; pci_sys->vga_count = 0; while ((dev = pci_device_next(iter)) != NULL) { if (dev->domain == 0) pci_sys->vga_count++; } pci_iterator_destroy(iter); return 0; } void pci_device_vgaarb_fini(void) { struct pci_device *dev; struct pci_vga pv; if (pci_sys == NULL) return; dev = pci_sys->vga_target; if (dev == NULL) return; pv.pv_sel.pc_bus = dev->bus; pv.pv_sel.pc_dev = dev->dev; pv.pv_sel.pc_func = dev->func; pv.pv_lock = PCI_VGA_UNLOCK; ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv); } int pci_device_vgaarb_set_target(struct pci_device *dev) { pci_sys->vga_target = dev; return (0); } int pci_device_vgaarb_lock(void) { struct pci_device *dev = pci_sys->vga_target; struct pci_vga pv; if (dev == NULL) return -1; #if 0 if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1) return 0; #else if (pci_sys->vga_count == 1) return 0; #endif pv.pv_sel.pc_bus = dev->bus; pv.pv_sel.pc_dev = dev->dev; pv.pv_sel.pc_func = dev->func; pv.pv_lock = PCI_VGA_LOCK; return ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv); } int pci_device_vgaarb_unlock(void) { struct pci_device *dev = pci_sys->vga_target; struct pci_vga pv; if (dev == NULL) return -1; #if 0 if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1) return 0; #else if (pci_sys->vga_count == 1) return 0; #endif pv.pv_sel.pc_bus = dev->bus; pv.pv_sel.pc_dev = dev->dev; pv.pv_sel.pc_func = dev->func; pv.pv_lock = PCI_VGA_UNLOCK; return ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv); } int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes) { *vga_count = pci_sys->vga_count; if (dev) *rsrc_decodes = dev->vgaarb_rsrc; return 0; } int pci_device_vgaarb_decodes(int rsrc_decodes) { struct pci_device *dev = pci_sys->vga_target; if (dev == NULL) return -1; dev->vgaarb_rsrc = rsrc_decodes; return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/pci_tools.h0000664014310600000120000001514414577654164016205 0ustar00alancstaff/* * Copyright (c) 2007, Oracle and/or its affiliates. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef _SYS_PCI_TOOLS_H #define _SYS_PCI_TOOLS_H #pragma ident "@(#)pci_tools.h 1.4 05/09/28 SMI" #include #ifdef __cplusplus extern "C" { #endif /* * Versioning. Have different versions for userland program and drivers, so * they can all stay in sync with each other. */ #define PCITOOL_USER_VERSION 1 #define PCITOOL_DRVR_VERSION 1 /* File suffixes for nexus pcitool nodes. */ #define PCI_MINOR_REG "reg" #define PCI_MINOR_INTR "intr" /* * Ioctls for PCI tools. */ #define PCITOOL_IOC (('P' << 24) | ('C' << 16) | ('T' << 8)) /* Read/write a device on a PCI bus, in physical space. */ #define PCITOOL_DEVICE_GET_REG (PCITOOL_IOC | 1) #define PCITOOL_DEVICE_SET_REG (PCITOOL_IOC | 2) /* Read/write the PCI nexus bridge, in physical space. */ #define PCITOOL_NEXUS_GET_REG (PCITOOL_IOC | 3) #define PCITOOL_NEXUS_SET_REG (PCITOOL_IOC | 4) /* Get/set interrupt-CPU mapping for PCI devices. */ #define PCITOOL_DEVICE_GET_INTR (PCITOOL_IOC | 5) #define PCITOOL_DEVICE_SET_INTR (PCITOOL_IOC | 6) /* Return the number of supported interrupts on a PCI bus. */ #define PCITOOL_DEVICE_NUM_INTR (PCITOOL_IOC | 7) /* * This file contains data structures for the pci tool. */ #define PCITOOL_CONFIG 0 #define PCITOOL_BAR0 1 #define PCITOOL_BAR1 2 #define PCITOOL_BAR2 3 #define PCITOOL_BAR3 4 #define PCITOOL_BAR4 5 #define PCITOOL_BAR5 6 #define PCITOOL_ROM 7 /* * Pass this through barnum to signal to use a base addr instead. * This is for platforms which do not have a way to automatically map * a selected bank to a base addr. */ #define PCITOOL_BASE 0xFF /* * BAR corresponding to space desired. */ typedef enum { config = PCITOOL_CONFIG, bar0 = PCITOOL_BAR0, bar1 = PCITOOL_BAR1, bar2 = PCITOOL_BAR2, bar3 = PCITOOL_BAR3, bar4 = PCITOOL_BAR4, bar5 = PCITOOL_BAR5, rom = PCITOOL_ROM } pcitool_bars_t; /* * PCITOOL error numbers. */ typedef enum { PCITOOL_SUCCESS = 0x0, PCITOOL_INVALID_CPUID, PCITOOL_INVALID_INO, PCITOOL_PENDING_INTRTIMEOUT, PCITOOL_REGPROP_NOTWELLFORMED, PCITOOL_INVALID_ADDRESS, PCITOOL_NOT_ALIGNED, PCITOOL_OUT_OF_RANGE, PCITOOL_END_OF_RANGE, PCITOOL_ROM_DISABLED, PCITOOL_ROM_WRITE, PCITOOL_IO_ERROR, PCITOOL_INVALID_SIZE } pcitool_errno_t; /* * PCITOOL_DEVICE_SET_INTR ioctl data structure to re-assign the interrupts. */ typedef struct pcitool_intr_set { uint16_t user_version; /* Userland program version - to krnl */ uint16_t drvr_version; /* Driver version - from kernel */ uint32_t ino; /* interrupt to set - to kernel */ uint32_t cpu_id; /* to: cpu to set / from: old cpu returned */ pcitool_errno_t status; /* from kernel */ } pcitool_intr_set_t; /* * PCITOOL_DEVICE_GET_INTR ioctl data structure to dump out the * ino mapping information. */ typedef struct pcitool_intr_dev { uint32_t dev_inst; /* device instance - from kernel */ char driver_name[MAXMODCONFNAME]; /* from kernel */ char path[MAXPATHLEN]; /* device path - from kernel */ } pcitool_intr_dev_t; typedef struct pcitool_intr_get { uint16_t user_version; /* Userland program version - to krnl */ uint16_t drvr_version; /* Driver version - from kernel */ uint32_t ino; /* interrupt number - to kernel */ uint8_t num_devs_ret; /* room for this # of devs to be */ /* returned - to kernel */ /* # devs returned - from kernel */ uint8_t num_devs; /* # devs on this ino - from kernel */ /* intrs enabled for devs if > 0 */ uint8_t ctlr; /* controller number - from kernel */ uint32_t cpu_id; /* cpu of interrupt - from kernel */ pcitool_errno_t status; /* returned status - from kernel */ pcitool_intr_dev_t dev[1]; /* start of variable device list */ /* from kernel */ } pcitool_intr_get_t; /* * Get the size needed to return the number of devices wanted. * Can't say num_devs - 1 as num_devs may be unsigned. */ #define PCITOOL_IGET_SIZE(num_devs) \ (sizeof (pcitool_intr_get_t) - \ sizeof (pcitool_intr_dev_t) + \ ((num_devs) * sizeof (pcitool_intr_dev_t))) /* * Size and endian fields for acc_attr bitmask. */ #define PCITOOL_ACC_ATTR_SIZE_MASK 0x3 #define PCITOOL_ACC_ATTR_SIZE_1 0x0 #define PCITOOL_ACC_ATTR_SIZE_2 0x1 #define PCITOOL_ACC_ATTR_SIZE_4 0x2 #define PCITOOL_ACC_ATTR_SIZE_8 0x3 #define PCITOOL_ACC_ATTR_SIZE(x) (1 << ((x) & PCITOOL_ACC_ATTR_SIZE_MASK)) #define PCITOOL_ACC_ATTR_ENDN_MASK 0x100 #define PCITOOL_ACC_ATTR_ENDN_LTL 0x0 #define PCITOOL_ACC_ATTR_ENDN_BIG 0x100 #define PCITOOL_ACC_IS_BIG_ENDIAN(x) ((x) & PCITOOL_ACC_ATTR_ENDN_BIG) /* * Data structure to read and write to pci device registers. * This is the argument to the following ioctls: * PCITOOL_DEVICE_SET/GET_REG * PCITOOL_NEXUS_SET/GET_REG */ typedef struct pcitool_reg { uint16_t user_version; /* Userland program version - to krnl */ uint16_t drvr_version; /* Driver version - from kernel */ uint8_t bus_no; /* pci bus - to kernel */ uint8_t dev_no; /* pci dev - to kernel */ uint8_t func_no; /* pci function - to kernel */ uint8_t barnum; /* bank (DEVCTL_NEXUS_SET/GET_REG) or */ /* BAR from pcitools_bar_t */ /* (DEVCTL_DEVICE_SET/GET_REG) */ /* to kernel */ uint64_t offset; /* to kernel */ uint32_t acc_attr; /* access attributes - to kernel */ uint32_t padding1; /* 8-byte align next uint64_t for X86 */ uint64_t data; /* to/from kernel, 64-bit alignment */ uint32_t status; /* from kernel */ uint32_t padding2; /* 8-byte align next uint64_t for X86 */ uint64_t phys_addr; /* from kernel, 64-bit alignment */ } pcitool_reg_t; #ifdef __cplusplus } #endif #endif /* _SYS_PCI_TOOLS_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/pciaccess_private.h0000664014310600000120000001403414577654164017676 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file pciaccess_private.h * Functions and datastructures that are private to the pciaccess library. * * \author Ian Romanick */ #ifndef PCIACCESS_PRIVATE_H #define PCIACCESS_PRIVATE_H #if defined(__GNUC__) && (__GNUC__ >= 4) # define _pci_hidden __attribute__((visibility("hidden"))) #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) # define _pci_hidden __hidden #else /* not gcc >= 4 and not Sun Studio >= 8 */ # define _pci_hidden #endif /* GNUC >= 4 */ /* * O_CLOEXEC fixes an fd leak case (see 'man 2 open' for details). I don't * know of any OS we support where this isn't available in a sufficiently * new version, so warn unconditionally. */ #include #ifndef O_CLOEXEC #warning O_CLOEXEC not available, please upgrade. #define O_CLOEXEC 0 #endif struct pci_device_mapping; int pci_fill_capabilities_generic( struct pci_device * dev ); int pci_device_generic_unmap_range(struct pci_device *dev, struct pci_device_mapping *map); struct pci_system_methods { void (*destroy)( void ); void (*destroy_device)( struct pci_device * dev ); int (*read_rom)( struct pci_device * dev, void * buffer ); int (*probe)( struct pci_device * dev ); int (*map_range)(struct pci_device *dev, struct pci_device_mapping *map); int (*unmap_range)(struct pci_device * dev, struct pci_device_mapping *map); int (*read)(struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ); int (*write)(struct pci_device * dev, const void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_written ); int (*fill_capabilities)( struct pci_device * dev ); void (*enable)( struct pci_device *dev ); void (*disable)( struct pci_device *dev ); int (*boot_vga)( struct pci_device *dev ); int (*has_kernel_driver)( struct pci_device *dev ); struct pci_io_handle *(*open_device_io)( struct pci_io_handle *handle, struct pci_device *dev, int bar, pciaddr_t base, pciaddr_t size ); struct pci_io_handle *(*open_legacy_io)( struct pci_io_handle *handle, struct pci_device *dev, pciaddr_t base, pciaddr_t size ); void (*close_io)( struct pci_device *dev, struct pci_io_handle *handle ); uint32_t (*read32)( struct pci_io_handle *handle, uint32_t reg ); uint16_t (*read16)( struct pci_io_handle *handle, uint32_t reg ); uint8_t (*read8)( struct pci_io_handle *handle, uint32_t reg ); void (*write32)( struct pci_io_handle *handle, uint32_t reg, uint32_t data ); void (*write16)( struct pci_io_handle *handle, uint32_t reg, uint16_t data ); void (*write8)( struct pci_io_handle *handle, uint32_t reg, uint8_t data ); int (*map_legacy)(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr); int (*unmap_legacy)(struct pci_device *dev, void *addr, pciaddr_t size); }; struct pci_device_mapping { pciaddr_t base; pciaddr_t size; unsigned region; unsigned flags; void *memory; }; struct pci_io_handle { pciaddr_t base; pciaddr_t size; void *memory; int fd; int is_legacy; }; struct pci_device_private { struct pci_device base; const char * device_string; uint8_t header_type; /** * \name PCI Capabilities */ /*@{*/ const struct pci_agp_info * agp; /**< AGP capability information. */ /*@}*/ /** * Base address of the device's expansion ROM. */ pciaddr_t rom_base; /** * \name Bridge information. */ /*@{*/ union { struct pci_bridge_info * pci; struct pci_pcmcia_bridge_info * pcmcia; } bridge; /*@}*/ /** * \name Mappings active on this device. */ /*@{*/ struct pci_device_mapping *mappings; unsigned num_mappings; /*@}*/ #ifdef __sun int is_primary; #endif #ifdef __GNU__ unsigned long device_port; #endif }; /** * Base type for tracking PCI subsystem information. */ struct pci_system { /** * Platform dependent implementations of specific API routines. */ const struct pci_system_methods * methods; /** * Number of known devices in the system. */ size_t num_devices; /** * Array of known devices. */ struct pci_device_private * devices; #ifdef HAVE_MTRR int mtrr_fd; #endif int vgaarb_fd; int vga_count; struct pci_device *vga_target; struct pci_device *vga_default_dev; }; 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_netbsd_create( void ); extern int pci_system_openbsd_create( void ); extern void pci_system_openbsd_init_dev_mem( int ); extern int pci_system_solx_devfs_create( void ); extern int pci_system_hurd_create( void ); extern int pci_system_x86_create( void ); extern void pci_io_cleanup( void ); #endif /* PCIACCESS_PRIVATE_H */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/solx_devfs.c0000664014310600000120000006523414577654164016366 0ustar00alancstaff/* * (C) Copyright IBM Corporation 2006 * Copyright (c) 2007, 2009, 2011, 2012, 2016 Oracle and/or its affiliates. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * Solaris devfs interfaces */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "pci_tools.h" #ifdef __x86 # include # include #endif #include "pciaccess.h" #include "pciaccess_private.h" /* #define DEBUG */ #define INITIAL_NUM_DEVICES 256 #define CELL_NUMS_1275 (sizeof(pci_regspec_t) / sizeof(uint_t)) typedef struct i_devnode { uint8_t bus; uint8_t dev; uint8_t func; di_node_t node; } i_devnode_t; typedef struct nexus { int first_bus; int last_bus; int domain; char *path; /* for open */ char *dev_path; struct nexus *next; } nexus_t; typedef struct probe_info { volatile size_t num_allocated_elems; volatile size_t num_devices; struct pci_device_private * volatile devices; } probe_info_t; typedef struct probe_args { probe_info_t *pinfo; nexus_t *nexus; int ret; } probe_args_t; typedef struct property_info { const char *name; int value; } property_info_t; static nexus_t *nexus_list = NULL; #if !defined(__sparc) static int xsvc_fd = -1; #endif #ifdef __sparc static di_prom_handle_t di_phdl; static size_t nexus_count = 0; #endif /* * Read config space in native processor endianness. Endian-neutral * processing can then take place. On big endian machines, MSB and LSB * of little endian data end up switched if read as little endian. * They are in correct order if read as big endian. */ #if defined(__sparc) # define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_BIG #elif defined(__x86) # define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_LTL #else # error "ISA is neither __sparc nor __x86" #endif #ifdef __sparc #define MAPPING_DEV_PATH(dev) (((struct pci_device_private *) dev)->device_string) #endif static nexus_t * find_nexus_for_bus( int domain, int bus ) { nexus_t *nexus; for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) { if ((domain == nexus->domain) && (bus >= nexus->first_bus) && (bus <= nexus->last_bus)) { return nexus; } } return NULL; } /* * Release all the resources * Solaris version */ static void pci_system_solx_devfs_destroy( void ) { /* * The memory allocated for pci_sys & devices in create routines * will be freed in pci_system_cleanup. * Need to free system-specific allocations here. */ nexus_t *nexus, *next; for (nexus = nexus_list ; nexus != NULL ; nexus = next) { next = nexus->next; free(nexus->path); free(nexus->dev_path); free(nexus); } nexus_list = NULL; #ifdef __sparc if (di_phdl != DI_PROM_HANDLE_NIL) (void) di_prom_fini(di_phdl); #else if (xsvc_fd >= 0) { close(xsvc_fd); xsvc_fd = -1; } #endif } #ifdef __sparc /* * Release resources per device */ static void pci_system_solx_devfs_destroy_device( struct pci_device *dev ) { if (MAPPING_DEV_PATH(dev)) di_devfs_path_free((char *) MAPPING_DEV_PATH(dev)); } #endif static int probe_device_node(di_node_t node, void *arg) { int *retbuf = NULL; int len = 0, i; struct pci_device *pci_base; probe_info_t *pinfo = ((probe_args_t *)arg)->pinfo; nexus_t *nexus = ((probe_args_t *)arg)->nexus; property_info_t property_list[] = { { "class-code", 0 }, { "device-id", 0 }, { "vendor-id", 0 }, { "revision-id", 0}, { "subsystem-vendor-id", 0}, { "subsystem-id", 0}, }; #define NUM_PROPERTIES sizeof(property_list)/sizeof(property_info_t) len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &retbuf); #ifdef __sparc if ((len <= 0) && di_phdl) len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &retbuf); #endif /* Exclude usb devices */ if (len < 5) { return DI_WALK_CONTINUE; } pci_base = &pinfo->devices[pinfo->num_devices].base; pci_base->domain = nexus->domain; pci_base->bus = PCI_REG_BUS_G(retbuf[0]); pci_base->dev = PCI_REG_DEV_G(retbuf[0]); pci_base->func = PCI_REG_FUNC_G(retbuf[0]); if (nexus->domain > 0xffff) pci_base->domain_16 = 0xffff; else pci_base->domain_16 = nexus->domain; /* Get property values */ for (i = 0; i < NUM_PROPERTIES; i++) { len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, property_list[i].name, &retbuf); #ifdef __sparc if ((len <= 0) && di_phdl) len = di_prom_prop_lookup_ints(di_phdl, node, property_list[i].name, &retbuf); #endif if (len > 0) property_list[i].value = retbuf[0]; else { /* a device must have property "class-code", "device-id", "vendor-id" */ if (i < 3) return DI_WALK_CONTINUE; #ifdef DEBUG fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n", property_list[i].name, nexus->path); fprintf(stderr, " domain = %x, busno = %x, devno = %x, funcno = %x\n", pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func); #endif } } if ((property_list[1].value == 0) && (property_list[2].value == 0)) return DI_WALK_CONTINUE; pci_base->device_class = property_list[0].value; pci_base->device_id = property_list[1].value; pci_base->vendor_id = property_list[2].value; pci_base->revision = property_list[3].value; pci_base->subvendor_id = property_list[4].value; pci_base->subdevice_id = property_list[5].value; #ifdef DEBUG fprintf(stderr, "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n", nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func); #endif pinfo->num_devices++; if (pinfo->num_devices == pinfo->num_allocated_elems) { struct pci_device_private *new_devs; size_t new_num_elems = pinfo->num_allocated_elems * 2; new_devs = realloc(pinfo->devices, new_num_elems * sizeof (struct pci_device_private)); if (new_devs == NULL) { (void) fprintf(stderr, "Error allocating memory for PCI devices:" " %s\n discarding additional devices\n", strerror(errno)); ((probe_args_t *)arg)->ret = 1; return (DI_WALK_TERMINATE); } (void) memset(&new_devs[pinfo->num_devices], 0, pinfo->num_allocated_elems * sizeof (struct pci_device_private)); pinfo->num_allocated_elems = new_num_elems; pinfo->devices = new_devs; } return (DI_WALK_CONTINUE); } /* * This function is called from di_walk_minor() when any PROBE is processed */ static int probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) { probe_info_t *pinfo = (probe_info_t *)arg; char *nexus_name, *nexus_dev_path; nexus_t *nexus; int fd; char nexus_path[MAXPATHLEN]; di_prop_t prop; char *strings; int *ints; int numval; int pci_node = 0; int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M); int domain = 0; #ifdef __sparc int bus_range_found = 0; int device_type_found = 0; di_prom_prop_t prom_prop; #endif #ifdef DEBUG nexus_name = di_devfs_minor_path(minor); fprintf(stderr, "-- device name: %s\n", nexus_name); di_devfs_path_free(nexus_name); #endif for (prop = di_prop_next(di_node, NULL); prop != NULL; prop = di_prop_next(di_node, prop)) { const char *prop_name = di_prop_name(prop); #ifdef DEBUG fprintf(stderr, " property: %s\n", prop_name); #endif if (strcmp(prop_name, "device_type") == 0) { numval = di_prop_strings(prop, &strings); if (numval == 1) { if (strncmp(strings, "pci", 3) != 0) /* not a PCI node, bail */ return (DI_WALK_CONTINUE); else { pci_node = 1; #ifdef __sparc device_type_found = 1; #endif } } } else if (strcmp(prop_name, "class-code") == 0) { /* not a root bus node, bail */ return (DI_WALK_CONTINUE); } else if (strcmp(prop_name, "bus-range") == 0) { numval = di_prop_ints(prop, &ints); if (numval == 2) { first_bus = ints[0]; last_bus = ints[1]; #ifdef __sparc bus_range_found = 1; #endif } } #ifdef __sparc domain = nexus_count; #else else if (strcmp(prop_name, "pciseg") == 0) { numval = di_prop_ints(prop, &ints); if (numval == 1) { domain = ints[0]; } } #endif } #ifdef __sparc if ((!device_type_found) && di_phdl) { numval = di_prom_prop_lookup_strings(di_phdl, di_node, "device_type", &strings); if (numval == 1) { if (strncmp(strings, "pci", 3) != 0) return (DI_WALK_CONTINUE); else pci_node = 1; } } if ((!bus_range_found) && di_phdl) { numval = di_prom_prop_lookup_ints(di_phdl, di_node, "bus-range", &ints); if (numval == 2) { first_bus = ints[0]; last_bus = ints[1]; } } #endif if (pci_node != 1) return (DI_WALK_CONTINUE); /* we have a PCI root bus node. */ nexus = calloc(1, sizeof(nexus_t)); if (nexus == NULL) { (void) fprintf(stderr, "Error allocating memory for nexus: %s\n", strerror(errno)); return (DI_WALK_TERMINATE); } nexus->first_bus = first_bus; nexus->last_bus = last_bus; nexus->domain = domain; #ifdef __sparc nexus_count++; #endif nexus_name = di_devfs_minor_path(minor); if (nexus_name == NULL) { (void) fprintf(stderr, "Error getting nexus path: %s\n", strerror(errno)); free(nexus); return (DI_WALK_CONTINUE); } snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name); di_devfs_path_free(nexus_name); #ifdef DEBUG fprintf(stderr, "nexus = %s, bus-range = %d - %d\n", nexus_path, first_bus, last_bus); #endif if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) { probe_args_t args; nexus->path = strdup(nexus_path); nexus_dev_path = di_devfs_path(di_node); nexus->dev_path = strdup(nexus_dev_path); di_devfs_path_free(nexus_dev_path); /* Walk through devices under the rnode */ args.pinfo = pinfo; args.nexus = nexus; args.ret = 0; (void) di_walk_node(di_node, DI_WALK_CLDFIRST, (void *)&args, probe_device_node); close(fd); if (args.ret) { free(nexus->path); free(nexus->dev_path); free(nexus); return (DI_WALK_TERMINATE); } nexus->next = nexus_list; nexus_list = nexus; } else { (void) fprintf(stderr, "Error opening %s: %s\n", nexus_path, strerror(errno)); free(nexus); } return DI_WALK_CONTINUE; } static int find_target_node(di_node_t node, void *arg) { int *regbuf = NULL; int len = 0; uint32_t busno, funcno, devno; i_devnode_t *devnode = (i_devnode_t *)arg; /* * Test the property functions, only for testing */ /* void *prop = DI_PROP_NIL; (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid); while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) { int i; (void) fprintf(stderr, "name=%s: ", di_prop_name(prop)); len = 0; if (!strcmp(di_prop_name(prop), "reg")) { len = di_prop_ints(prop, ®buf); } for (i = 0; i < len; i++) { fprintf(stderr, "0x%0x.", regbuf[i]); } fprintf(stderr, "\n"); } (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid); */ len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®buf); #ifdef __sparc if ((len <= 0) && di_phdl) len = di_prom_prop_lookup_ints(di_phdl, node, "reg", ®buf); #endif if (len <= 0) { #ifdef DEBUG fprintf(stderr, "error = %x\n", errno); fprintf(stderr, "can not find assigned-address\n"); #endif return (DI_WALK_CONTINUE); } busno = PCI_REG_BUS_G(regbuf[0]); devno = PCI_REG_DEV_G(regbuf[0]); funcno = PCI_REG_FUNC_G(regbuf[0]); if ((busno == devnode->bus) && (devno == devnode->dev) && (funcno == devnode->func)) { devnode->node = node; return (DI_WALK_TERMINATE); } return (DI_WALK_CONTINUE); } /* * Solaris version */ static int pci_device_solx_devfs_probe( struct pci_device * dev ) { int err = 0; di_node_t rnode = DI_NODE_NIL; i_devnode_t args = { 0, 0, 0, DI_NODE_NIL }; int *regbuf; pci_regspec_t *reg; int i; int len = 0; uint ent = 0; struct pci_device_private *priv = (struct pci_device_private *) dev; nexus_t *nexus; if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL ) return ENODEV; pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER); pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE); /* * starting to find if it is MEM/MEM64/IO * using libdevinfo */ if ((rnode = di_init(nexus->dev_path, DINFOCACHE)) == DI_NODE_NIL) { err = errno; (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno)); } else { args.bus = dev->bus; args.dev = dev->dev; args.func = dev->func; (void) di_walk_node(rnode, DI_WALK_CLDFIRST, (void *)&args, find_target_node); } if (args.node != DI_NODE_NIL) { int *prop; #ifdef __sparc di_minor_t minor; #endif priv->is_primary = 0; #ifdef __sparc if (minor = di_minor_next(args.node, DI_MINOR_NIL)) MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor); else MAPPING_DEV_PATH(dev) = NULL; #endif if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node, "primary-controller", &prop) >= 1) { if (prop[0]) priv->is_primary = 1; } /* * It will succeed for sure, because it was * successfully called in find_target_node */ len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node, "assigned-addresses", ®buf); #ifdef __sparc if ((len <= 0) && di_phdl) { len = di_prom_prop_lookup_ints(di_phdl, args.node, "assigned-addresses", ®buf); } #endif } if (len <= 0) goto cleanup; /* * Each BAR address get its own region slot in sequence. * 32 bit BAR: * BAR 0x10 -> slot0, BAR 0x14 -> slot1... * 64 bit BAR: * BAR 0x10 -> slot0, BAR 0x18 -> slot2..., * slot1 is part of BAR 0x10 * Linux give two region slot for 64 bit address. */ for (i = 0; i < len; i = i + (int)CELL_NUMS_1275) { reg = (pci_regspec_t *)®buf[i]; ent = reg->pci_phys_hi & 0xff; if (ent > PCI_CONF_ROM) { fprintf(stderr, "error ent = %d\n", ent); break; } /* * G35 broken in BAR0 */ if (ent < PCI_CONF_BASE0) { /* * VGA resource here and ignore it */ break; } else if (ent == PCI_CONF_ROM) { priv->rom_base = reg->pci_phys_low | ((uint64_t)reg->pci_phys_mid << 32); dev->rom_size = reg->pci_size_low; } else { ent = (ent - PCI_CONF_BASE0) >> 2; /* * non relocatable resource is excluded * such like 0xa0000, 0x3b0. If it is met, * the loop is broken; */ if (!PCI_REG_REG_G(reg->pci_phys_hi)) break; if (reg->pci_phys_hi & PCI_PREFETCH_B) { dev->regions[ent].is_prefetchable = 1; } dev->regions[ent].base_addr = reg->pci_phys_low | ((uint64_t)reg->pci_phys_mid << 32); dev->regions[ent].size = reg->pci_size_low | ((uint64_t)reg->pci_size_hi << 32); switch (reg->pci_phys_hi & PCI_REG_ADDR_M) { case PCI_ADDR_IO: dev->regions[ent].is_IO = 1; break; case PCI_ADDR_MEM32: break; case PCI_ADDR_MEM64: dev->regions[ent].is_64 = 1; /* * Skip one slot for 64 bit address */ break; } } } cleanup: if (rnode != DI_NODE_NIL) { di_fini(rnode); } return (err); } /** * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc) * * \param dev Device whose memory region is to be mapped. * \param map Parameters of the mapping that is to be created. * * \return * Zero on success or an \c errno value on failure. */ static int pci_device_solx_devfs_map_range(struct pci_device *dev, struct pci_device_mapping *map) { const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? (PROT_READ | PROT_WRITE) : PROT_READ; int err = 0; const char *map_dev; int map_fd; #ifdef __sparc char map_dev_buf[128]; if (MAPPING_DEV_PATH(dev)) { snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s", "/devices", MAPPING_DEV_PATH(dev)); map_dev = map_dev_buf; } else map_dev = "/dev/fb0"; map_fd = -1; #else /* * Still uses xsvc to do the user space mapping on x86/x64, * caches open fd across multiple calls. */ map_dev = "/dev/xsvc"; map_fd = xsvc_fd; #endif if (map_fd < 0) { if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) { err = errno; (void) fprintf(stderr, "can not open %s: %s\n", map_dev, strerror(errno)); return err; } #ifndef __sparc xsvc_fd = map_fd; #endif } map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base); if (map->memory == MAP_FAILED) { err = errno; (void) fprintf(stderr, "map rom region =%llx failed: %s\n", (unsigned long long) map->base, strerror(errno)); } #ifdef __sparc close (map_fd); #endif return err; } /* * Solaris version: read the VGA ROM data */ static int pci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer ) { int err; struct pci_device_mapping prom = { .base = 0xC0000, .size = 0x10000, .flags = 0 }; struct pci_device_private *priv = (struct pci_device_private *) dev; if (priv->rom_base) { prom.base = priv->rom_base; prom.size = dev->rom_size; } err = pci_device_solx_devfs_map_range(dev, &prom); if (err == 0) { (void) bcopy(prom.memory, buffer, dev->rom_size); if (munmap(prom.memory, prom.size) == -1) { err = errno; } } return err; } /* * solaris version: Read the configurations space of the devices */ static int pci_device_solx_devfs_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ) { pcitool_reg_t cfg_prg; int err = 0; unsigned int i = 0; nexus_t *nexus; int fd; nexus = find_nexus_for_bus(dev->domain, dev->bus); *bytes_read = 0; if ( nexus == NULL ) { return ENODEV; } cfg_prg.offset = offset; cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; cfg_prg.bus_no = dev->bus; cfg_prg.dev_no = dev->dev; cfg_prg.func_no = dev->func; cfg_prg.barnum = 0; cfg_prg.user_version = PCITOOL_USER_VERSION; if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0) return ENOENT; for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1)) { cfg_prg.offset = offset + i; if ((err = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) { fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n", nexus->path, cfg_prg.bus_no, cfg_prg.dev_no, cfg_prg.func_no, (unsigned long long) cfg_prg.offset); fprintf(stderr, "Failure cause = %x\n", err); break; } ((uint8_t *)data)[i] = (uint8_t)cfg_prg.data; /* * DWORDS Offset or bytes Offset ?? */ } *bytes_read = i; close(fd); return (err); } /* * Solaris version */ static int pci_device_solx_devfs_write( struct pci_device * dev, const void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_written ) { pcitool_reg_t cfg_prg; int err = 0; int cmd; nexus_t *nexus; int fd; nexus = find_nexus_for_bus(dev->domain, dev->bus); if ( bytes_written != NULL ) { *bytes_written = 0; } if ( nexus == NULL ) { return ENODEV; } cfg_prg.offset = offset; switch (size) { case 1: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; cfg_prg.data = *((const uint8_t *)data); break; case 2: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN; cfg_prg.data = *((const uint16_t *)data); break; case 4: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; cfg_prg.data = *((const uint32_t *)data); break; case 8: cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN; cfg_prg.data = *((const uint64_t *)data); break; default: return EINVAL; } cfg_prg.bus_no = dev->bus; cfg_prg.dev_no = dev->dev; cfg_prg.func_no = dev->func; cfg_prg.barnum = 0; cfg_prg.user_version = PCITOOL_USER_VERSION; /* * Check if this device is bridge device. * If it is, it is also a nexus node??? * It seems that there is no explicit * PCI nexus device for X86, so not applicable * from pcitool_bus_reg_ops in pci_tools.c */ cmd = PCITOOL_DEVICE_SET_REG; if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0) return ENOENT; if ((err = ioctl(fd, cmd, &cfg_prg)) != 0) { close(fd); return (err); } *bytes_written = size; close(fd); return (err); } static int pci_device_solx_devfs_boot_vga(struct pci_device *dev) { struct pci_device_private *priv = (struct pci_device_private *) dev; return (priv->is_primary); } static struct pci_io_handle * pci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { #ifdef __x86 if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) { ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; } #endif return NULL; } static uint32_t pci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); uint32_t ret; __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port)); return ret; #else return *(uint32_t *)((uintptr_t)handle->memory + reg); #endif } static uint16_t pci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); uint16_t ret; __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port)); return ret; #else return *(uint16_t *)((uintptr_t)handle->memory + reg); #endif } static uint8_t pci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); uint8_t ret; __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port)); return ret; #else return *(uint8_t *)((uintptr_t)handle->memory + reg); #endif } static void pci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port)); #else *(uint16_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port)); #else *(uint8_t *)((uintptr_t)handle->memory + reg) = data; #endif } static void pci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { #ifdef __x86 uint16_t port = (uint16_t) (handle->base + reg); __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port)); #else *(uint32_t *)((uintptr_t)handle->memory + reg) = data; #endif } static int pci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { int err; struct pci_device_mapping map = { .base = base, .size = size, .flags = map_flags, }; err = pci_device_solx_devfs_map_range(dev, &map); if (err == 0) *addr = map.memory; return err; } static int pci_device_solx_devfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map = { .memory = addr, .size = size, }; return pci_device_generic_unmap_range(dev, &map); } static const struct pci_system_methods solx_devfs_methods = { .destroy = pci_system_solx_devfs_destroy, #ifdef __sparc .destroy_device = pci_system_solx_devfs_destroy_device, #else .destroy_device = NULL, #endif .read_rom = pci_device_solx_devfs_read_rom, .probe = pci_device_solx_devfs_probe, .map_range = pci_device_solx_devfs_map_range, .unmap_range = pci_device_generic_unmap_range, .read = pci_device_solx_devfs_read, .write = pci_device_solx_devfs_write, .fill_capabilities = pci_fill_capabilities_generic, .boot_vga = pci_device_solx_devfs_boot_vga, .open_legacy_io = pci_device_solx_devfs_open_legacy_io, .read32 = pci_device_solx_devfs_read32, .read16 = pci_device_solx_devfs_read16, .read8 = pci_device_solx_devfs_read8, .write32 = pci_device_solx_devfs_write32, .write16 = pci_device_solx_devfs_write16, .write8 = pci_device_solx_devfs_write8, .map_legacy = pci_device_solx_devfs_map_legacy, .unmap_legacy = pci_device_solx_devfs_unmap_legacy, }; /* * Attempt to access PCI subsystem using Solaris's devfs interface. * Solaris version */ _pci_hidden int pci_system_solx_devfs_create( void ) { int err = 0; di_node_t di_node; probe_info_t pinfo; struct pci_device_private *devices; if (nexus_list != NULL) { return 0; } if ((di_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { err = errno; (void) fprintf(stderr, "di_init() failed: %s\n", strerror(errno)); return (err); } if ((devices = calloc(INITIAL_NUM_DEVICES, sizeof (struct pci_device_private))) == NULL) { err = errno; di_fini(di_node); return (err); } #ifdef __sparc if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) (void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno)); #endif pinfo.num_allocated_elems = INITIAL_NUM_DEVICES; pinfo.num_devices = 0; pinfo.devices = devices; #ifdef __sparc nexus_count = 0; #endif (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node); di_fini(di_node); if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) { err = errno; free(devices); return (err); } pci_sys->methods = &solx_devfs_methods; pci_sys->devices = pinfo.devices; pci_sys->num_devices = pinfo.num_devices; return (err); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/x86_pci.c0000664014310600000120000006626314577654164015475 0ustar00alancstaff/* * Copyright (c) 2018 Damien Zammit * Copyright (c) 2017 Joan Lledó * Copyright (c) 2009, 2012, 2020 Samuel Thibault * Heavily inspired from the freebsd, netbsd, and openbsd backends * (C) Copyright Eric Anholt 2006 * (C) Copyright IBM Corporation 2006 * Copyright (c) 2008 Juan Romero Pardines * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "x86_pci.h" #include #include #include #include #include #include #include #include #include "pciaccess.h" #include "pciaccess_private.h" #if defined(__GNU__) #include int x86_enable_io(void) { if (!ioperm(0, 0xffff, 1)) return 0; return errno; } int x86_disable_io(void) { if (!ioperm(0, 0xffff, 0)) return 0; return errno; } #elif defined(__GLIBC__) #include static int x86_enable_io(void) { if (!iopl(3)) return 0; return errno; } static int x86_disable_io(void) { if (!iopl(0)) return 0; return errno; } #elif defined(__CYGWIN__) #include /* WinIo declarations */ typedef BYTE bool; typedef struct tagPhysStruct { DWORD64 dwPhysMemSizeInBytes; DWORD64 pvPhysAddress; DWORD64 PhysicalMemoryHandle; DWORD64 pvPhysMemLin; DWORD64 pvPhysSection; } tagPhysStruct; typedef bool (_stdcall* INITIALIZEWINIO)(void); typedef void (_stdcall* SHUTDOWNWINIO)(void); typedef bool (_stdcall* GETPORTVAL)(WORD,PDWORD,BYTE); typedef bool (_stdcall* SETPORTVAL)(WORD,DWORD,BYTE); typedef PBYTE (_stdcall* MAPPHYSTOLIN)(tagPhysStruct*); typedef bool (_stdcall* UNMAPPHYSMEM)(tagPhysStruct*); SHUTDOWNWINIO ShutdownWinIo; GETPORTVAL GetPortVal; SETPORTVAL SetPortVal; INITIALIZEWINIO InitializeWinIo; MAPPHYSTOLIN MapPhysToLin; UNMAPPHYSMEM UnmapPhysicalMemory; static int x86_enable_io(void) { HMODULE lib = NULL; if ((GetVersion() & 0x80000000) == 0) { /* running on NT, try WinIo version 3 (32 or 64 bits) */ #ifdef WIN64 lib = LoadLibrary("WinIo64.dll"); #else lib = LoadLibrary("WinIo32.dll"); #endif } if (!lib) { fprintf(stderr, "Failed to load WinIo library.\n"); return 1; } #define GETPROC(n, d) \ n = (d) GetProcAddress(lib, #n); \ if (!n) { \ fprintf(stderr, "Failed to load " #n " function.\n"); \ return 1; \ } GETPROC(InitializeWinIo, INITIALIZEWINIO); GETPROC(ShutdownWinIo, SHUTDOWNWINIO); GETPROC(GetPortVal, GETPORTVAL); GETPROC(SetPortVal, SETPORTVAL); GETPROC(MapPhysToLin, MAPPHYSTOLIN); GETPROC(UnmapPhysicalMemory, UNMAPPHYSMEM); #undef GETPROC if (!InitializeWinIo()) { fprintf(stderr, "Failed to initialize WinIo.\n" "NOTE: WinIo.dll and WinIo.sys must be in the same directory as the executable!\n"); return 0; } return 0; } static int x86_disable_io(void) { ShutdownWinIo(); return 1; } static inline uint8_t inb(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 1)) return (uint8_t)pv; return 0; } static inline uint16_t inw(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 2)) return (uint16_t)pv; return 0; } static inline uint32_t inl(uint16_t port) { DWORD pv; if (GetPortVal(port, &pv, 4)) return (uint32_t)pv; return 0; } static inline void outb(uint8_t value, uint16_t port) { SetPortVal(port, value, 1); } static inline void outw(uint16_t value, uint16_t port) { SetPortVal(port, value, 2); } static inline void outl(uint32_t value, uint16_t port) { SetPortVal(port, value, 4); } #else #error How to enable IO ports on this system? #endif static int cmp_devices(const void *dev1, const void *dev2) { const struct pci_device *d1 = dev1; const struct pci_device *d2 = dev2; if (d1->bus != d2->bus) { return (d1->bus > d2->bus) ? 1 : -1; } if (d1->dev != d2->dev) { return (d1->dev > d2->dev) ? 1 : -1; } return (d1->func > d2->func) ? 1 : -1; } static void sort_devices(void) { qsort(pci_sys->devices, pci_sys->num_devices, sizeof (pci_sys->devices[0]), &cmp_devices); } #if defined(__GNU__) #include #include #include #endif int pci_system_x86_map_dev_mem(void **dest, size_t mem_offset, size_t mem_size, int write) { #if defined(__GNU__) int err; mach_port_t master_device; mach_port_t devmem; mach_port_t pager; dev_mode_t mode = D_READ; vm_prot_t prot = VM_PROT_READ; int pagesize; if (get_privileged_ports (NULL, &master_device)) { *dest = 0; return EPERM; } if (write) { mode |= D_WRITE; prot |= VM_PROT_WRITE; } err = device_open (master_device, mode, "mem", &devmem); mach_port_deallocate (mach_task_self (), master_device); if (err) return err; pagesize = getpagesize(); if (mem_size % pagesize) mem_size += pagesize - (mem_size % pagesize); err = device_map (devmem, prot, mem_offset, mem_size, &pager, 0); device_close (devmem); mach_port_deallocate (mach_task_self (), devmem); if (err) return err; err = vm_map (mach_task_self (), (vm_address_t *)dest, mem_size, (vm_address_t) 0, /* mask */ 1, /* anywhere? */ pager, 0, 0, /* copy */ prot, VM_PROT_ALL, VM_INHERIT_SHARE); mach_port_deallocate (mach_task_self (), pager); if (err) return err; return err; #else int prot = PROT_READ; int flags = O_RDONLY; int memfd; if (write) { prot |= PROT_WRITE; flags = O_RDWR; } memfd = open("/dev/mem", flags | O_CLOEXEC); if (memfd == -1) return errno; *dest = mmap(NULL, mem_size, prot, MAP_SHARED, memfd, mem_offset); if (*dest == MAP_FAILED) { close(memfd); *dest = NULL; return errno; } close(memfd); return 0; #endif } static int pci_system_x86_conf1_probe(void) { unsigned long sav; int res = ENODEV; outb(0x01, 0xCFB); sav = inl(0xCF8); outl(0x80000000, 0xCF8); if (inl(0xCF8) == 0x80000000) res = 0; outl(sav, 0xCF8); return res; } static int pci_system_x86_conf1_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size) { unsigned addr = 0xCFC + (reg & 3); unsigned long sav; int ret = 0; if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3) return EIO; sav = inl(0xCF8); outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8); /* NOTE: x86 is already LE */ switch (size) { case 1: { uint8_t *val = data; *val = inb(addr); break; } case 2: { uint16_t *val = data; *val = inw(addr); break; } case 4: { uint32_t *val = data; *val = inl(addr); break; } } outl(sav, 0xCF8); return ret; } static int pci_system_x86_conf1_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size) { unsigned addr = 0xCFC + (reg & 3); unsigned long sav; int ret = 0; if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3) return EIO; sav = inl(0xCF8); outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8); /* NOTE: x86 is already LE */ switch (size) { case 1: { const uint8_t *val = data; outb(*val, addr); break; } case 2: { const uint16_t *val = data; outw(*val, addr); break; } case 4: { const uint32_t *val = data; outl(*val, addr); break; } } outl(sav, 0xCF8); return ret; } static int pci_system_x86_conf2_probe(void) { outb(0, 0xCFB); outb(0, 0xCF8); outb(0, 0xCFA); if (inb(0xCF8) == 0 && inb(0xCFA) == 0) return 0; return ENODEV; } static int pci_system_x86_conf2_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size) { unsigned addr = 0xC000 | dev << 8 | reg; int ret = 0; if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100) return EIO; outb((func << 1) | 0xF0, 0xCF8); outb(bus, 0xCFA); /* NOTE: x86 is already LE */ switch (size) { case 1: { uint8_t *val = data; *val = inb(addr); break; } case 2: { uint16_t *val = data; *val = inw(addr); break; } case 4: { uint32_t *val = data; *val = inl(addr); break; } default: ret = EIO; break; } outb(0, 0xCF8); return ret; } static int pci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size) { unsigned addr = 0xC000 | dev << 8 | reg; int ret = 0; if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100) return EIO; outb((func << 1) | 0xF0, 0xCF8); outb(bus, 0xCFA); /* NOTE: x86 is already LE */ switch (size) { case 1: { const uint8_t *val = data; outb(*val, addr); break; } case 2: { const uint16_t *val = data; outw(*val, addr); break; } case 4: { const uint32_t *val = data; outl(*val, addr); break; } default: ret = EIO; break; } outb(0, 0xCF8); return ret; } /* Check that this really looks like a PCI configuration. */ static error_t pci_system_x86_check (void) { int dev; uint16_t class, vendor; struct pci_device tmpdev = { 0 }; /* Look on bus 0 for a device that is a host bridge, a VGA card, * or an intel or compaq device. */ tmpdev.bus = 0; tmpdev.func = 0; class = 0; vendor = 0; for (dev = 0; dev < 32; dev++) { tmpdev.dev = dev; if (pci_device_cfg_read_u16 (&tmpdev, &class, PCI_CLASS_DEVICE)) continue; if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA) return 0; if (pci_device_cfg_read_u16 (&tmpdev, &vendor, PCI_VENDOR_ID)) continue; if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ) return 0; } return ENODEV; } static int pci_nfuncs(struct pci_device *dev, uint8_t *nfuncs) { uint8_t hdr; int err; struct pci_device tmpdev = *dev; tmpdev.func = 0; err = pci_device_cfg_read_u8 (&tmpdev, &hdr, PCI_HDRTYPE); if (err) return err; *nfuncs = hdr & 0x80 ? 8 : 1; return err; } /** * Read a PCI rom. */ static error_t pci_device_x86_read_rom(struct pci_device *dev, void *buffer) { void *bios = NULL; struct pci_device_private *d = (struct pci_device_private *)dev; int err; if ( (err = pci_system_x86_map_dev_mem(&bios, d->rom_base, dev->rom_size, 0)) ) return err; memcpy(buffer, bios, dev->rom_size); munmap(bios, dev->rom_size); return 0; } /** Returns the number of regions (base address registers) the device has */ static int pci_device_x86_get_num_regions(uint8_t header_type) { switch (header_type & 0x7f) { case 0: return 6; case 1: return 2; case 2: return 1; default: fprintf(stderr,"unknown header type %02x\n", header_type); return 0; } } /** Masks out the flag bigs of the base address register value */ static uint32_t get_map_base( uint32_t val ) { if (val & 0x01) return val & ~0x03; else return val & ~0x0f; } /** Returns the size of a region based on the all-ones test value */ static unsigned get_test_val_size( uint32_t testval ) { unsigned size = 1; if (testval == 0) return 0; /* Mask out the flag bits */ testval = get_map_base( testval ); if (!testval) return 0; while ((testval & 1) == 0) { size <<= 1; testval >>= 1; } return size; } /* Read BAR `reg_num' in `dev' and map the data if any */ static error_t pci_device_x86_region_probe (struct pci_device *dev, int reg_num) { error_t err; uint8_t offset; uint32_t reg, addr, testval; offset = PCI_BAR_ADDR_0 + 0x4 * reg_num; /* Get the base address */ err = pci_device_cfg_read_u32 (dev, &addr, offset); if (err) return err; /* Test write all ones to the register, then restore it. */ reg = 0xffffffff; err = pci_device_cfg_write_u32 (dev, reg, offset); if (err) return err; err = pci_device_cfg_read_u32 (dev, &testval, offset); if (err) return err; err = pci_device_cfg_write_u32 (dev, addr, offset); if (err) return err; if (addr & 0x01) dev->regions[reg_num].is_IO = 1; if (addr & 0x04) dev->regions[reg_num].is_64 = 1; if (addr & 0x08) dev->regions[reg_num].is_prefetchable = 1; /* Set the size */ dev->regions[reg_num].size = get_test_val_size (testval); /* Set the base address value */ dev->regions[reg_num].base_addr = get_map_base (addr); if (dev->regions[reg_num].is_64) { err = pci_device_cfg_read_u32 (dev, &addr, offset + 4); if (err) return err; dev->regions[reg_num].base_addr |= ((uint64_t) addr << 32); } if (dev->regions[reg_num].is_IO) { /* Enable the I/O Space bit */ err = pci_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x1)) { reg |= 0x1; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } } else if (dev->regions[reg_num].size > 0) { /* Enable the Memory Space bit */ err = pci_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x2)) { reg |= 0x2; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } } /* Clear the map pointer */ dev->regions[reg_num].memory = 0; return 0; } /* Read the XROMBAR in `dev' and save the rom size and rom base */ static error_t pci_device_x86_probe_rom (struct pci_device *dev) { error_t err; uint8_t reg_8, xrombar_addr; uint32_t reg, reg_back; pciaddr_t rom_size; pciaddr_t rom_base; struct pci_device_private *d = (struct pci_device_private *)dev; /* First we need to know which type of header is this */ err = pci_device_cfg_read_u8 (dev, ®_8, PCI_HDRTYPE); if (err) return err; /* Get the XROMBAR register address */ switch (reg_8 & 0x3) { case PCI_HDRTYPE_DEVICE: xrombar_addr = PCI_XROMBAR_ADDR_00; break; case PCI_HDRTYPE_BRIDGE: xrombar_addr = PCI_XROMBAR_ADDR_01; break; default: return -1; } /* Get size and physical address */ err = pci_device_cfg_read_u32 (dev, ®, xrombar_addr); if (err) return err; reg_back = reg; reg = 0xFFFFF800; /* Base address: first 21 bytes */ err = pci_device_cfg_write_u32 (dev, reg, xrombar_addr); if (err) return err; err = pci_device_cfg_read_u32 (dev, ®, xrombar_addr); if (err) return err; rom_size = (~reg + 1); rom_base = reg_back & reg; if (rom_size == 0) return 0; /* Enable the address decoder and write the physical address back */ reg_back |= 0x1; err = pci_device_cfg_write_u32 (dev, reg_back, xrombar_addr); if (err) return err; /* Enable the Memory Space bit */ err = pci_device_cfg_read_u32 (dev, ®, PCI_COMMAND); if (err) return err; if (!(reg & 0x2)) { reg |= 0x2; err = pci_device_cfg_write_u32 (dev, reg, PCI_COMMAND); if (err) return err; } dev->rom_size = rom_size; d->rom_base = rom_base; return 0; } /* Configure BARs and ROM */ static error_t pci_device_x86_probe (struct pci_device *dev) { error_t err; uint8_t hdrtype; int i; /* Probe BARs */ err = pci_device_cfg_read_u8 (dev, &hdrtype, PCI_HDRTYPE); if (err) return err; for (i = 0; i < pci_device_x86_get_num_regions (hdrtype); i++) { err = pci_device_x86_region_probe (dev, i); if (err) return err; if (dev->regions[i].is_64) /* Move the pointer one BAR ahead */ i++; } /* Probe ROM */ pci_device_x86_probe_rom(dev); return 0; } /* Recursively scan bus number `bus' */ static error_t pci_system_x86_scan_bus (uint8_t bus) { error_t err; uint8_t dev, func, nfuncs, hdrtype, secbus; uint32_t reg; struct pci_device_private *d, *devices; struct pci_device scratchdev; scratchdev.bus = bus; for (dev = 0; dev < 32; dev++) { scratchdev.dev = dev; scratchdev.func = 0; err = pci_nfuncs (&scratchdev, &nfuncs); if (err) return err; for (func = 0; func < nfuncs; func++) { scratchdev.func = func; err = pci_device_cfg_read_u32 (&scratchdev, ®, PCI_VENDOR_ID); if (err) return err; if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0) continue; err = pci_device_cfg_read_u32 (&scratchdev, ®, PCI_CLASS); if (err) return err; err = pci_device_cfg_read_u8 (&scratchdev, &hdrtype, PCI_HDRTYPE); if (err) return err; devices = realloc (pci_sys->devices, (pci_sys->num_devices + 1) * sizeof (struct pci_device_private)); if (!devices) return ENOMEM; d = devices + pci_sys->num_devices; memset (d, 0, sizeof (struct pci_device_private)); /* Fixed values as PCI express is still not supported */ d->base.domain = 0; d->base.bus = bus; d->base.dev = dev; d->base.func = func; d->base.device_class = reg >> 8; pci_sys->devices = devices; pci_sys->num_devices++; switch (hdrtype & 0x3) { case PCI_HDRTYPE_DEVICE: break; case PCI_HDRTYPE_BRIDGE: case PCI_HDRTYPE_CARDBUS: { err = pci_device_cfg_read_u8 (&scratchdev, &secbus, PCI_SECONDARY_BUS); if (err) return err; err = pci_system_x86_scan_bus (secbus); if (err) return err; break; } default: /* Unknown header, do nothing */ break; } } } return 0; } #if defined(__CYGWIN__) static int pci_device_x86_map_range(struct pci_device *dev, struct pci_device_mapping *map) { tagPhysStruct phys; phys.pvPhysAddress = (DWORD64)(DWORD32)map->base; phys.dwPhysMemSizeInBytes = map->size; map->memory = (PDWORD)MapPhysToLin(&phys); if (map->memory == NULL) return EFAULT; return 0; } static int pci_device_x86_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { tagPhysStruct phys; phys.pvPhysAddress = (DWORD64)(DWORD32)map->base; phys.dwPhysMemSizeInBytes = map->size; if (!UnmapPhysicalMemory(&phys)) return EFAULT; return 0; } #else static int pci_device_x86_map_range(struct pci_device *dev, struct pci_device_mapping *map) { int err; if ( (err = pci_system_x86_map_dev_mem(&map->memory, map->base, map->size, map->flags & PCI_DEV_MAP_FLAG_WRITABLE))) return err; return 0; } static int pci_device_x86_unmap_range(struct pci_device *dev, struct pci_device_mapping *map) { int err; err = pci_device_generic_unmap_range(dev, map); map->memory = NULL; return err; } #endif static int pci_device_x86_read_conf1(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { int err; *bytes_read = 0; while (size > 0) { int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); if (toread > size) toread = size; err = pci_system_x86_conf1_read(dev->bus, dev->dev, dev->func, offset, data, toread); if (err) return err; offset += toread; data = (char*)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_x86_read_conf2(struct pci_device *dev, void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) { int err; *bytes_read = 0; while (size > 0) { int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1); if (toread > size) toread = size; err = pci_system_x86_conf2_read(dev->bus, dev->dev, dev->func, offset, data, toread); if (err) return err; offset += toread; data = (char*)data + toread; size -= toread; *bytes_read += toread; } return 0; } static int pci_device_x86_write_conf1(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { int err; *bytes_written = 0; while (size > 0) { int towrite = 4; if (towrite > size) towrite = size; if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); err = pci_system_x86_conf1_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; offset += towrite; data = (const char*)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } static int pci_device_x86_write_conf2(struct pci_device *dev, const void *data, pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) { int err; *bytes_written = 0; while (size > 0) { int towrite = 4; if (towrite > size) towrite = size; if (towrite > 4 - (offset & 0x3)) towrite = 4 - (offset & 0x3); err = pci_system_x86_conf2_write(dev->bus, dev->dev, dev->func, offset, data, towrite); if (err) return err; offset += towrite; data = (const char*)data + towrite; size -= towrite; *bytes_written += towrite; } return 0; } void pci_system_x86_destroy(void) { x86_disable_io(); } struct pci_io_handle * pci_device_x86_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size) { x86_enable_io(); ret->base = base; ret->size = size; ret->is_legacy = 1; return ret; } void pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle) { /* Like in the Linux case, do not disable I/O, as it may be opened several * times, and closed fewer times. */ /* x86_disable_io(); */ } uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg) { return inl(reg + handle->base); } uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg) { return inw(reg + handle->base); } uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg) { return inb(reg + handle->base); } void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data) { outl(data, reg + handle->base); } void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data) { outw(data, reg + handle->base); } void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data) { outb(data, reg + handle->base); } static int pci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size, unsigned map_flags, void **addr) { struct pci_device_mapping map; int err; map.base = base; map.size = size; map.flags = map_flags; err = pci_device_x86_map_range(dev, &map); *addr = map.memory; return err; } static int pci_device_x86_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) { struct pci_device_mapping map; map.size = size; map.flags = 0; map.memory = addr; return pci_device_x86_unmap_range(dev, &map); } static const struct pci_system_methods x86_pci_method_conf1 = { .destroy = pci_system_x86_destroy, .read_rom = pci_device_x86_read_rom, .probe = pci_device_x86_probe, .map_range = pci_device_x86_map_range, .unmap_range = pci_device_x86_unmap_range, .read = pci_device_x86_read_conf1, .write = pci_device_x86_write_conf1, .fill_capabilities = pci_fill_capabilities_generic, .open_legacy_io = pci_device_x86_open_legacy_io, .close_io = pci_device_x86_close_io, .read32 = pci_device_x86_read32, .read16 = pci_device_x86_read16, .read8 = pci_device_x86_read8, .write32 = pci_device_x86_write32, .write16 = pci_device_x86_write16, .write8 = pci_device_x86_write8, .map_legacy = pci_device_x86_map_legacy, .unmap_legacy = pci_device_x86_unmap_legacy, }; static const struct pci_system_methods x86_pci_method_conf2 = { .destroy = pci_system_x86_destroy, .read_rom = pci_device_x86_read_rom, .probe = pci_device_x86_probe, .map_range = pci_device_x86_map_range, .unmap_range = pci_device_x86_unmap_range, .read = pci_device_x86_read_conf2, .write = pci_device_x86_write_conf2, .fill_capabilities = pci_fill_capabilities_generic, .open_legacy_io = pci_device_x86_open_legacy_io, .close_io = pci_device_x86_close_io, .read32 = pci_device_x86_read32, .read16 = pci_device_x86_read16, .read8 = pci_device_x86_read8, .write32 = pci_device_x86_write32, .write16 = pci_device_x86_write16, .write8 = pci_device_x86_write8, .map_legacy = pci_device_x86_map_legacy, .unmap_legacy = pci_device_x86_unmap_legacy, }; static int pci_probe(void) { pci_sys->methods = &x86_pci_method_conf1; if (pci_system_x86_conf1_probe() == 0) { if (pci_system_x86_check() == 0) return 1; } pci_sys->methods = &x86_pci_method_conf2; if (pci_system_x86_conf2_probe() == 0) { if (pci_system_x86_check() == 0) return 2; } pci_sys->methods = NULL; return 0; } _pci_hidden int pci_system_x86_create(void) { error_t err; int confx; err = x86_enable_io (); if (err) return err; pci_sys = calloc (1, sizeof (struct pci_system)); if (pci_sys == NULL) { x86_disable_io (); return ENOMEM; } confx = pci_probe (); if (!confx) { x86_disable_io (); free (pci_sys); pci_sys = NULL; return ENODEV; } else if (confx == 1) pci_sys->methods = &x86_pci_method_conf1; else pci_sys->methods = &x86_pci_method_conf2; /* Recursive scan */ pci_sys->num_devices = 0; err = pci_system_x86_scan_bus (0); if (err) { x86_disable_io (); if (pci_sys->num_devices) { free (pci_sys->devices); } free (pci_sys); pci_sys = NULL; return err; } sort_devices (); return 0; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1711233140.0 libpciaccess-0.18.1/src/x86_pci.h0000664014310600000120000000562114577654164015471 0ustar00alancstaff/* * Copyright (c) 2017 Joan Lledó * Copyright (c) 2009, 2012 Samuel Thibault * Heavily inspired from the freebsd, netbsd, and openbsd backends * (C) Copyright Eric Anholt 2006 * (C) Copyright IBM Corporation 2006 * Copyright (c) 2008 Juan Romero Pardines * 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. */ /* Macros and declarations used by both x86 and Hurd modules. */ #ifndef X86_PCI_H #define X86_PCI_H #include "pciaccess.h" #include "pciaccess_private.h" #define PCI_VENDOR(reg) ((reg) & 0xFFFF) #define PCI_VENDOR_INVALID 0xFFFF #define PCI_VENDOR_ID 0x00 #define PCI_SUB_VENDOR_ID 0x2c #define PCI_VENDOR_ID_COMPAQ 0x0e11 #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF) #define PCI_DEVICE_INVALID 0xFFFF #define PCI_CLASS 0x08 #define PCI_CLASS_DEVICE 0x0a #define PCI_CLASS_DISPLAY_VGA 0x0300 #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCIC_DISPLAY 0x03 #define PCIS_DISPLAY_VGA 0x00 #define PCI_HDRTYPE 0x0E #define PCI_HDRTYPE_DEVICE 0x00 #define PCI_HDRTYPE_BRIDGE 0x01 #define PCI_HDRTYPE_CARDBUS 0x02 #define PCI_IRQ 0x3C #define PCI_BAR_ADDR_0 0x10 #define PCI_XROMBAR_ADDR_00 0x30 #define PCI_XROMBAR_ADDR_01 0x38 #define PCI_COMMAND 0x04 #define PCI_SECONDARY_BUS 0x19 int x86_enable_io(void); int x86_disable_io(void); void pci_system_x86_destroy(void); struct pci_io_handle *pci_device_x86_open_legacy_io(struct pci_io_handle *ret, struct pci_device *dev, pciaddr_t base, pciaddr_t size); void pci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle); uint32_t pci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg); uint16_t pci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg); uint8_t pci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg); void pci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data); void pci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data); void pci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data); int pci_system_x86_map_dev_mem(void **dest, size_t mem_offset, size_t mem_size, int write); #endif /* X86_PCI_H */