1aad970f1SDavid E. O'Brien /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4fce5d19dSStefan Eßer * Copyright 1997, Stefan Esser <se@freebsd.org> 58983cfbfSMike Smith * 68983cfbfSMike Smith * Redistribution and use in source and binary forms, with or without 78983cfbfSMike Smith * modification, are permitted provided that the following conditions 88983cfbfSMike Smith * are met: 98983cfbfSMike Smith * 1. Redistributions of source code must retain the above copyright 108983cfbfSMike Smith * notice unmodified, this list of conditions, and the following 118983cfbfSMike Smith * disclaimer. 128983cfbfSMike Smith * 2. Redistributions in binary form must reproduce the above copyright 138983cfbfSMike Smith * notice, this list of conditions and the following disclaimer in the 148983cfbfSMike Smith * documentation and/or other materials provided with the distribution. 158983cfbfSMike Smith * 168983cfbfSMike Smith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 178983cfbfSMike Smith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 188983cfbfSMike Smith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 198983cfbfSMike Smith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 208983cfbfSMike Smith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 218983cfbfSMike Smith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 228983cfbfSMike Smith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 238983cfbfSMike Smith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 248983cfbfSMike Smith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 258983cfbfSMike Smith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 268983cfbfSMike Smith */ 278983cfbfSMike Smith 28aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 298983cfbfSMike Smith #include "opt_bus.h" /* XXX trim includes */ 308983cfbfSMike Smith 3187842989SKonstantin Belousov #include <sys/types.h> 328983cfbfSMike Smith #include <sys/param.h> 338983cfbfSMike Smith #include <sys/systm.h> 348983cfbfSMike Smith #include <sys/malloc.h> 358983cfbfSMike Smith #include <sys/module.h> 368983cfbfSMike Smith #include <sys/linker.h> 378983cfbfSMike Smith #include <sys/fcntl.h> 388983cfbfSMike Smith #include <sys/conf.h> 398983cfbfSMike Smith #include <sys/kernel.h> 4087842989SKonstantin Belousov #include <sys/mman.h> 418002488bSRobert Watson #include <sys/proc.h> 428983cfbfSMike Smith #include <sys/queue.h> 4387842989SKonstantin Belousov #include <sys/rwlock.h> 4487842989SKonstantin Belousov #include <sys/sglist.h> 458983cfbfSMike Smith 468983cfbfSMike Smith #include <vm/vm.h> 478983cfbfSMike Smith #include <vm/pmap.h> 488983cfbfSMike Smith #include <vm/vm_extern.h> 4987842989SKonstantin Belousov #include <vm/vm_map.h> 5087842989SKonstantin Belousov #include <vm/vm_object.h> 5187842989SKonstantin Belousov #include <vm/vm_page.h> 5287842989SKonstantin Belousov #include <vm/vm_pager.h> 538983cfbfSMike Smith 548983cfbfSMike Smith #include <sys/bus.h> 558983cfbfSMike Smith #include <machine/bus.h> 568983cfbfSMike Smith #include <sys/rman.h> 578983cfbfSMike Smith #include <machine/resource.h> 588983cfbfSMike Smith 598983cfbfSMike Smith #include <sys/pciio.h> 6038d8c994SWarner Losh #include <dev/pci/pcireg.h> 6138d8c994SWarner Losh #include <dev/pci/pcivar.h> 628983cfbfSMike Smith 638983cfbfSMike Smith #include "pcib_if.h" 648983cfbfSMike Smith #include "pci_if.h" 658983cfbfSMike Smith 66b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 67b7edb6faSBrooks Davis struct pci_conf32 { 68b7edb6faSBrooks Davis struct pcisel pc_sel; /* domain+bus+slot+function */ 69b7edb6faSBrooks Davis u_int8_t pc_hdr; /* PCI header type */ 70b7edb6faSBrooks Davis u_int16_t pc_subvendor; /* card vendor ID */ 71b7edb6faSBrooks Davis u_int16_t pc_subdevice; /* card device ID, assigned by 72b7edb6faSBrooks Davis card vendor */ 73b7edb6faSBrooks Davis u_int16_t pc_vendor; /* chip vendor ID */ 74b7edb6faSBrooks Davis u_int16_t pc_device; /* chip device ID, assigned by 75b7edb6faSBrooks Davis chip vendor */ 76b7edb6faSBrooks Davis u_int8_t pc_class; /* chip PCI class */ 77b7edb6faSBrooks Davis u_int8_t pc_subclass; /* chip PCI subclass */ 78b7edb6faSBrooks Davis u_int8_t pc_progif; /* chip PCI programming interface */ 79b7edb6faSBrooks Davis u_int8_t pc_revid; /* chip revision ID */ 80b7edb6faSBrooks Davis char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 81b7edb6faSBrooks Davis u_int32_t pd_unit; /* device unit number */ 82b7edb6faSBrooks Davis }; 83b7edb6faSBrooks Davis 84b7edb6faSBrooks Davis struct pci_match_conf32 { 85b7edb6faSBrooks Davis struct pcisel pc_sel; /* domain+bus+slot+function */ 86b7edb6faSBrooks Davis char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 87b7edb6faSBrooks Davis u_int32_t pd_unit; /* Unit number */ 88b7edb6faSBrooks Davis u_int16_t pc_vendor; /* PCI Vendor ID */ 89b7edb6faSBrooks Davis u_int16_t pc_device; /* PCI Device ID */ 90b7edb6faSBrooks Davis u_int8_t pc_class; /* PCI class */ 91b7edb6faSBrooks Davis u_int32_t flags; /* Matching expression */ 92b7edb6faSBrooks Davis }; 93b7edb6faSBrooks Davis 94b7edb6faSBrooks Davis struct pci_conf_io32 { 95b7edb6faSBrooks Davis u_int32_t pat_buf_len; /* pattern buffer length */ 96b7edb6faSBrooks Davis u_int32_t num_patterns; /* number of patterns */ 97b7edb6faSBrooks Davis u_int32_t patterns; /* struct pci_match_conf ptr */ 98b7edb6faSBrooks Davis u_int32_t match_buf_len; /* match buffer length */ 99b7edb6faSBrooks Davis u_int32_t num_matches; /* number of matches returned */ 100b7edb6faSBrooks Davis u_int32_t matches; /* struct pci_conf ptr */ 101b7edb6faSBrooks Davis u_int32_t offset; /* offset into device list */ 102b7edb6faSBrooks Davis u_int32_t generation; /* device list generation */ 103b7edb6faSBrooks Davis u_int32_t status; /* request status */ 104b7edb6faSBrooks Davis }; 105b7edb6faSBrooks Davis 106b7edb6faSBrooks Davis #define PCIOCGETCONF32 _IOC_NEWTYPE(PCIOCGETCONF, struct pci_conf_io32) 107b7edb6faSBrooks Davis #endif 108b7edb6faSBrooks Davis 1098983cfbfSMike Smith /* 1108983cfbfSMike Smith * This is the user interface to PCI configuration space. 1118983cfbfSMike Smith */ 1128983cfbfSMike Smith 113b40ce416SJulian Elischer static d_open_t pci_open; 114b40ce416SJulian Elischer static d_close_t pci_close; 115b40ce416SJulian Elischer static d_ioctl_t pci_ioctl; 1168983cfbfSMike Smith 1178983cfbfSMike Smith struct cdevsw pcicdev = { 118dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 119dd615d09SWarner Losh .d_flags = 0, 1207ac40f5fSPoul-Henning Kamp .d_open = pci_open, 1217ac40f5fSPoul-Henning Kamp .d_close = pci_close, 1227ac40f5fSPoul-Henning Kamp .d_ioctl = pci_ioctl, 1237ac40f5fSPoul-Henning Kamp .d_name = "pci", 1248983cfbfSMike Smith }; 1258983cfbfSMike Smith 1268983cfbfSMike Smith static int 12789c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 1288983cfbfSMike Smith { 1298002488bSRobert Watson int error; 1308002488bSRobert Watson 1318002488bSRobert Watson if (oflags & FWRITE) { 132a854ed98SJohn Baldwin error = securelevel_gt(td->td_ucred, 0); 1338002488bSRobert Watson if (error) 1348002488bSRobert Watson return (error); 1358983cfbfSMike Smith } 1368002488bSRobert Watson 1378002488bSRobert Watson return (0); 1388983cfbfSMike Smith } 1398983cfbfSMike Smith 1408983cfbfSMike Smith static int 14189c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 1428983cfbfSMike Smith { 1438983cfbfSMike Smith return 0; 1448983cfbfSMike Smith } 1458983cfbfSMike Smith 1468983cfbfSMike Smith /* 1478983cfbfSMike Smith * Match a single pci_conf structure against an array of pci_match_conf 1488983cfbfSMike Smith * structures. The first argument, 'matches', is an array of num_matches 1498983cfbfSMike Smith * pci_match_conf structures. match_buf is a pointer to the pci_conf 1508983cfbfSMike Smith * structure that will be compared to every entry in the matches array. 1518983cfbfSMike Smith * This function returns 1 on failure, 0 on success. 1528983cfbfSMike Smith */ 1538983cfbfSMike Smith static int 154e9ed3a70SBrooks Davis pci_conf_match_native(struct pci_match_conf *matches, int num_matches, 1558983cfbfSMike Smith struct pci_conf *match_buf) 1568983cfbfSMike Smith { 1578983cfbfSMike Smith int i; 1588983cfbfSMike Smith 1598983cfbfSMike Smith if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 1608983cfbfSMike Smith return(1); 1618983cfbfSMike Smith 1628983cfbfSMike Smith for (i = 0; i < num_matches; i++) { 1638983cfbfSMike Smith /* 1648983cfbfSMike Smith * I'm not sure why someone would do this...but... 1658983cfbfSMike Smith */ 1668983cfbfSMike Smith if (matches[i].flags == PCI_GETCONF_NO_MATCH) 1678983cfbfSMike Smith continue; 1688983cfbfSMike Smith 1698983cfbfSMike Smith /* 1708983cfbfSMike Smith * Look at each of the match flags. If it's set, do the 1718983cfbfSMike Smith * comparison. If the comparison fails, we don't have a 1728983cfbfSMike Smith * match, go on to the next item if there is one. 1738983cfbfSMike Smith */ 17455aaf894SMarius Strobl if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0) 17555aaf894SMarius Strobl && (match_buf->pc_sel.pc_domain != 17655aaf894SMarius Strobl matches[i].pc_sel.pc_domain)) 17755aaf894SMarius Strobl continue; 17855aaf894SMarius Strobl 1798983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 1808983cfbfSMike Smith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 1818983cfbfSMike Smith continue; 1828983cfbfSMike Smith 1838983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 1848983cfbfSMike Smith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 1858983cfbfSMike Smith continue; 1868983cfbfSMike Smith 1878983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 1888983cfbfSMike Smith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 1898983cfbfSMike Smith continue; 1908983cfbfSMike Smith 1918983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 1928983cfbfSMike Smith && (match_buf->pc_vendor != matches[i].pc_vendor)) 1938983cfbfSMike Smith continue; 1948983cfbfSMike Smith 1958983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 1968983cfbfSMike Smith && (match_buf->pc_device != matches[i].pc_device)) 1978983cfbfSMike Smith continue; 1988983cfbfSMike Smith 1998983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 2008983cfbfSMike Smith && (match_buf->pc_class != matches[i].pc_class)) 2018983cfbfSMike Smith continue; 2028983cfbfSMike Smith 2038983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 2048983cfbfSMike Smith && (match_buf->pd_unit != matches[i].pd_unit)) 2058983cfbfSMike Smith continue; 2068983cfbfSMike Smith 2078983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 2088983cfbfSMike Smith && (strncmp(matches[i].pd_name, match_buf->pd_name, 2098983cfbfSMike Smith sizeof(match_buf->pd_name)) != 0)) 2108983cfbfSMike Smith continue; 2118983cfbfSMike Smith 2128983cfbfSMike Smith return(0); 2138983cfbfSMike Smith } 2148983cfbfSMike Smith 2158983cfbfSMike Smith return(1); 2168983cfbfSMike Smith } 2178983cfbfSMike Smith 218b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 219b7edb6faSBrooks Davis static int 220b7edb6faSBrooks Davis pci_conf_match32(struct pci_match_conf32 *matches, int num_matches, 221b7edb6faSBrooks Davis struct pci_conf *match_buf) 222b7edb6faSBrooks Davis { 223b7edb6faSBrooks Davis int i; 224b7edb6faSBrooks Davis 225b7edb6faSBrooks Davis if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 226b7edb6faSBrooks Davis return(1); 227b7edb6faSBrooks Davis 228b7edb6faSBrooks Davis for (i = 0; i < num_matches; i++) { 229b7edb6faSBrooks Davis /* 230b7edb6faSBrooks Davis * I'm not sure why someone would do this...but... 231b7edb6faSBrooks Davis */ 232b7edb6faSBrooks Davis if (matches[i].flags == PCI_GETCONF_NO_MATCH) 233b7edb6faSBrooks Davis continue; 234b7edb6faSBrooks Davis 235b7edb6faSBrooks Davis /* 236b7edb6faSBrooks Davis * Look at each of the match flags. If it's set, do the 237b7edb6faSBrooks Davis * comparison. If the comparison fails, we don't have a 238b7edb6faSBrooks Davis * match, go on to the next item if there is one. 239b7edb6faSBrooks Davis */ 240b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0) 241b7edb6faSBrooks Davis && (match_buf->pc_sel.pc_domain != 242b7edb6faSBrooks Davis matches[i].pc_sel.pc_domain)) 243b7edb6faSBrooks Davis continue; 244b7edb6faSBrooks Davis 245b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 246b7edb6faSBrooks Davis && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 247b7edb6faSBrooks Davis continue; 248b7edb6faSBrooks Davis 249b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 250b7edb6faSBrooks Davis && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 251b7edb6faSBrooks Davis continue; 252b7edb6faSBrooks Davis 253b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 254b7edb6faSBrooks Davis && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 255b7edb6faSBrooks Davis continue; 256b7edb6faSBrooks Davis 257b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 258b7edb6faSBrooks Davis && (match_buf->pc_vendor != matches[i].pc_vendor)) 259b7edb6faSBrooks Davis continue; 260b7edb6faSBrooks Davis 261b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 262b7edb6faSBrooks Davis && (match_buf->pc_device != matches[i].pc_device)) 263b7edb6faSBrooks Davis continue; 264b7edb6faSBrooks Davis 265b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 266b7edb6faSBrooks Davis && (match_buf->pc_class != matches[i].pc_class)) 267b7edb6faSBrooks Davis continue; 268b7edb6faSBrooks Davis 269b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 270b7edb6faSBrooks Davis && (match_buf->pd_unit != matches[i].pd_unit)) 271b7edb6faSBrooks Davis continue; 272b7edb6faSBrooks Davis 273b7edb6faSBrooks Davis if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 274b7edb6faSBrooks Davis && (strncmp(matches[i].pd_name, match_buf->pd_name, 275b7edb6faSBrooks Davis sizeof(match_buf->pd_name)) != 0)) 276b7edb6faSBrooks Davis continue; 277b7edb6faSBrooks Davis 278b7edb6faSBrooks Davis return(0); 279b7edb6faSBrooks Davis } 280b7edb6faSBrooks Davis 281b7edb6faSBrooks Davis return(1); 282b7edb6faSBrooks Davis } 283b7edb6faSBrooks Davis #endif /* COMPAT_FREEBSD32 */ 284b7edb6faSBrooks Davis 28533d3fffaSMarius Strobl #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ 28633d3fffaSMarius Strobl defined(COMPAT_FREEBSD6) 287b2068c0cSWarner Losh #define PRE7_COMPAT 28833d3fffaSMarius Strobl 28933d3fffaSMarius Strobl typedef enum { 290*56ecc8a9SWarner Losh PCI_GETCONF_NO_MATCH_FREEBSD6 = 0x00, 291*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_BUS_FREEBSD6 = 0x01, 292*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_DEV_FREEBSD6 = 0x02, 293*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_FUNC_FREEBSD6 = 0x04, 294*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_NAME_FREEBSD6 = 0x08, 295*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_UNIT_FREEBSD6 = 0x10, 296*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_VENDOR_FREEBSD6 = 0x20, 297*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_DEVICE_FREEBSD6 = 0x40, 298*56ecc8a9SWarner Losh PCI_GETCONF_MATCH_CLASS_FREEBSD6 = 0x80 299*56ecc8a9SWarner Losh } pci_getconf_flags_freebsd6; 30033d3fffaSMarius Strobl 301*56ecc8a9SWarner Losh struct pcisel_freebsd6 { 30233d3fffaSMarius Strobl u_int8_t pc_bus; /* bus number */ 30333d3fffaSMarius Strobl u_int8_t pc_dev; /* device on this bus */ 30433d3fffaSMarius Strobl u_int8_t pc_func; /* function on this device */ 30533d3fffaSMarius Strobl }; 30633d3fffaSMarius Strobl 307*56ecc8a9SWarner Losh struct pci_conf_freebsd6 { 308*56ecc8a9SWarner Losh struct pcisel_freebsd6 pc_sel; /* bus+slot+function */ 30933d3fffaSMarius Strobl u_int8_t pc_hdr; /* PCI header type */ 31033d3fffaSMarius Strobl u_int16_t pc_subvendor; /* card vendor ID */ 31133d3fffaSMarius Strobl u_int16_t pc_subdevice; /* card device ID, assigned by 31233d3fffaSMarius Strobl card vendor */ 31333d3fffaSMarius Strobl u_int16_t pc_vendor; /* chip vendor ID */ 31433d3fffaSMarius Strobl u_int16_t pc_device; /* chip device ID, assigned by 31533d3fffaSMarius Strobl chip vendor */ 31633d3fffaSMarius Strobl u_int8_t pc_class; /* chip PCI class */ 31733d3fffaSMarius Strobl u_int8_t pc_subclass; /* chip PCI subclass */ 31833d3fffaSMarius Strobl u_int8_t pc_progif; /* chip PCI programming interface */ 31933d3fffaSMarius Strobl u_int8_t pc_revid; /* chip revision ID */ 32033d3fffaSMarius Strobl char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 32133d3fffaSMarius Strobl u_long pd_unit; /* device unit number */ 32233d3fffaSMarius Strobl }; 32333d3fffaSMarius Strobl 324*56ecc8a9SWarner Losh struct pci_match_conf_freebsd6 { 325*56ecc8a9SWarner Losh struct pcisel_freebsd6 pc_sel; /* bus+slot+function */ 32633d3fffaSMarius Strobl char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 32733d3fffaSMarius Strobl u_long pd_unit; /* Unit number */ 32833d3fffaSMarius Strobl u_int16_t pc_vendor; /* PCI Vendor ID */ 32933d3fffaSMarius Strobl u_int16_t pc_device; /* PCI Device ID */ 33033d3fffaSMarius Strobl u_int8_t pc_class; /* PCI class */ 331*56ecc8a9SWarner Losh pci_getconf_flags_freebsd6 flags; /* Matching expression */ 33233d3fffaSMarius Strobl }; 33333d3fffaSMarius Strobl 334*56ecc8a9SWarner Losh struct pci_io_freebsd6 { 335*56ecc8a9SWarner Losh struct pcisel_freebsd6 pi_sel; /* device to operate on */ 33633d3fffaSMarius Strobl int pi_reg; /* configuration register to examine */ 33733d3fffaSMarius Strobl int pi_width; /* width (in bytes) of read or write */ 33833d3fffaSMarius Strobl u_int32_t pi_data; /* data to write or result of read */ 33933d3fffaSMarius Strobl }; 34033d3fffaSMarius Strobl 341b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32 342*56ecc8a9SWarner Losh struct pci_conf_freebsd6_32 { 343*56ecc8a9SWarner Losh struct pcisel_freebsd6 pc_sel; /* bus+slot+function */ 344e5280830SGleb Smirnoff uint8_t pc_hdr; /* PCI header type */ 345e5280830SGleb Smirnoff uint16_t pc_subvendor; /* card vendor ID */ 346e5280830SGleb Smirnoff uint16_t pc_subdevice; /* card device ID, assigned by 347b01bf72bSMaxim Sobolev card vendor */ 348e5280830SGleb Smirnoff uint16_t pc_vendor; /* chip vendor ID */ 349e5280830SGleb Smirnoff uint16_t pc_device; /* chip device ID, assigned by 350b01bf72bSMaxim Sobolev chip vendor */ 351e5280830SGleb Smirnoff uint8_t pc_class; /* chip PCI class */ 352e5280830SGleb Smirnoff uint8_t pc_subclass; /* chip PCI subclass */ 353e5280830SGleb Smirnoff uint8_t pc_progif; /* chip PCI programming interface */ 354e5280830SGleb Smirnoff uint8_t pc_revid; /* chip revision ID */ 355b01bf72bSMaxim Sobolev char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 356e5280830SGleb Smirnoff uint32_t pd_unit; /* device unit number (u_long) */ 357b01bf72bSMaxim Sobolev }; 358b01bf72bSMaxim Sobolev 359*56ecc8a9SWarner Losh struct pci_match_conf_freebsd6_32 { 360*56ecc8a9SWarner Losh struct pcisel_freebsd6 pc_sel; /* bus+slot+function */ 361b01bf72bSMaxim Sobolev char pd_name[PCI_MAXNAMELEN + 1]; /* device name */ 362e5280830SGleb Smirnoff uint32_t pd_unit; /* Unit number (u_long) */ 363e5280830SGleb Smirnoff uint16_t pc_vendor; /* PCI Vendor ID */ 364e5280830SGleb Smirnoff uint16_t pc_device; /* PCI Device ID */ 365e5280830SGleb Smirnoff uint8_t pc_class; /* PCI class */ 366*56ecc8a9SWarner Losh pci_getconf_flags_freebsd6 flags; /* Matching expression */ 367b01bf72bSMaxim Sobolev }; 368b01bf72bSMaxim Sobolev 369*56ecc8a9SWarner Losh #define PCIOCGETCONF_FREEBSD6_32 _IOWR('p', 1, struct pci_conf_io32) 370904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */ 371b01bf72bSMaxim Sobolev 372*56ecc8a9SWarner Losh #define PCIOCGETCONF_FREEBSD6 _IOWR('p', 1, struct pci_conf_io) 373*56ecc8a9SWarner Losh #define PCIOCREAD_FREEBSD6 _IOWR('p', 2, struct pci_io_freebsd6) 374*56ecc8a9SWarner Losh #define PCIOCWRITE_FREEBSD6 _IOWR('p', 3, struct pci_io_freebsd6) 37533d3fffaSMarius Strobl 37633d3fffaSMarius Strobl static int 377*56ecc8a9SWarner Losh pci_conf_match_freebsd6(struct pci_match_conf_freebsd6 *matches, int num_matches, 37833d3fffaSMarius Strobl struct pci_conf *match_buf) 37933d3fffaSMarius Strobl { 38033d3fffaSMarius Strobl int i; 38133d3fffaSMarius Strobl 38233d3fffaSMarius Strobl if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 38333d3fffaSMarius Strobl return(1); 38433d3fffaSMarius Strobl 38533d3fffaSMarius Strobl for (i = 0; i < num_matches; i++) { 38633d3fffaSMarius Strobl if (match_buf->pc_sel.pc_domain != 0) 38733d3fffaSMarius Strobl continue; 38833d3fffaSMarius Strobl 38933d3fffaSMarius Strobl /* 39033d3fffaSMarius Strobl * I'm not sure why someone would do this...but... 39133d3fffaSMarius Strobl */ 392*56ecc8a9SWarner Losh if (matches[i].flags == PCI_GETCONF_NO_MATCH_FREEBSD6) 39333d3fffaSMarius Strobl continue; 39433d3fffaSMarius Strobl 39533d3fffaSMarius Strobl /* 39633d3fffaSMarius Strobl * Look at each of the match flags. If it's set, do the 39733d3fffaSMarius Strobl * comparison. If the comparison fails, we don't have a 39833d3fffaSMarius Strobl * match, go on to the next item if there is one. 39933d3fffaSMarius Strobl */ 400*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_FREEBSD6) != 0) 40133d3fffaSMarius Strobl && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 40233d3fffaSMarius Strobl continue; 40333d3fffaSMarius Strobl 404*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_FREEBSD6) != 0) 40533d3fffaSMarius Strobl && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 40633d3fffaSMarius Strobl continue; 40733d3fffaSMarius Strobl 408*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_FREEBSD6) != 0) 40933d3fffaSMarius Strobl && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 41033d3fffaSMarius Strobl continue; 41133d3fffaSMarius Strobl 412*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_FREEBSD6) != 0) 41333d3fffaSMarius Strobl && (match_buf->pc_vendor != matches[i].pc_vendor)) 41433d3fffaSMarius Strobl continue; 41533d3fffaSMarius Strobl 416*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_FREEBSD6) != 0) 41733d3fffaSMarius Strobl && (match_buf->pc_device != matches[i].pc_device)) 41833d3fffaSMarius Strobl continue; 41933d3fffaSMarius Strobl 420*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_FREEBSD6) != 0) 42133d3fffaSMarius Strobl && (match_buf->pc_class != matches[i].pc_class)) 42233d3fffaSMarius Strobl continue; 42333d3fffaSMarius Strobl 424*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_FREEBSD6) != 0) 42533d3fffaSMarius Strobl && (match_buf->pd_unit != matches[i].pd_unit)) 42633d3fffaSMarius Strobl continue; 42733d3fffaSMarius Strobl 428*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_FREEBSD6) != 0) 42933d3fffaSMarius Strobl && (strncmp(matches[i].pd_name, match_buf->pd_name, 43033d3fffaSMarius Strobl sizeof(match_buf->pd_name)) != 0)) 43133d3fffaSMarius Strobl continue; 43233d3fffaSMarius Strobl 43333d3fffaSMarius Strobl return(0); 43433d3fffaSMarius Strobl } 43533d3fffaSMarius Strobl 43633d3fffaSMarius Strobl return(1); 43733d3fffaSMarius Strobl } 43833d3fffaSMarius Strobl 439904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32 440b01bf72bSMaxim Sobolev static int 441*56ecc8a9SWarner Losh pci_conf_match_freebsd6_32(struct pci_match_conf_freebsd6_32 *matches, int num_matches, 442b01bf72bSMaxim Sobolev struct pci_conf *match_buf) 443b01bf72bSMaxim Sobolev { 444b01bf72bSMaxim Sobolev int i; 445b01bf72bSMaxim Sobolev 446b01bf72bSMaxim Sobolev if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 447b01bf72bSMaxim Sobolev return(1); 448b01bf72bSMaxim Sobolev 449b01bf72bSMaxim Sobolev for (i = 0; i < num_matches; i++) { 450b01bf72bSMaxim Sobolev if (match_buf->pc_sel.pc_domain != 0) 451b01bf72bSMaxim Sobolev continue; 452b01bf72bSMaxim Sobolev 453b01bf72bSMaxim Sobolev /* 454b01bf72bSMaxim Sobolev * I'm not sure why someone would do this...but... 455b01bf72bSMaxim Sobolev */ 456*56ecc8a9SWarner Losh if (matches[i].flags == PCI_GETCONF_NO_MATCH_FREEBSD6) 457b01bf72bSMaxim Sobolev continue; 458b01bf72bSMaxim Sobolev 459b01bf72bSMaxim Sobolev /* 460b01bf72bSMaxim Sobolev * Look at each of the match flags. If it's set, do the 461b01bf72bSMaxim Sobolev * comparison. If the comparison fails, we don't have a 462b01bf72bSMaxim Sobolev * match, go on to the next item if there is one. 463b01bf72bSMaxim Sobolev */ 464*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_FREEBSD6) != 0) && 465e5280830SGleb Smirnoff (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 466b01bf72bSMaxim Sobolev continue; 467b01bf72bSMaxim Sobolev 468*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_FREEBSD6) != 0) && 469e5280830SGleb Smirnoff (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 470b01bf72bSMaxim Sobolev continue; 471b01bf72bSMaxim Sobolev 472*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_FREEBSD6) != 0) && 473e5280830SGleb Smirnoff (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 474b01bf72bSMaxim Sobolev continue; 475b01bf72bSMaxim Sobolev 476*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_FREEBSD6) != 0) && 477e5280830SGleb Smirnoff (match_buf->pc_vendor != matches[i].pc_vendor)) 478b01bf72bSMaxim Sobolev continue; 479b01bf72bSMaxim Sobolev 480*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_FREEBSD6) != 0) && 481e5280830SGleb Smirnoff (match_buf->pc_device != matches[i].pc_device)) 482b01bf72bSMaxim Sobolev continue; 483b01bf72bSMaxim Sobolev 484*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_FREEBSD6) != 0) && 485e5280830SGleb Smirnoff (match_buf->pc_class != matches[i].pc_class)) 486b01bf72bSMaxim Sobolev continue; 487b01bf72bSMaxim Sobolev 488*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_FREEBSD6) != 0) && 489e5280830SGleb Smirnoff ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit)) 490b01bf72bSMaxim Sobolev continue; 491b01bf72bSMaxim Sobolev 492*56ecc8a9SWarner Losh if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_FREEBSD6) != 0) && 493e5280830SGleb Smirnoff (strncmp(matches[i].pd_name, match_buf->pd_name, 494b01bf72bSMaxim Sobolev sizeof(match_buf->pd_name)) != 0)) 495b01bf72bSMaxim Sobolev continue; 496b01bf72bSMaxim Sobolev 497b01bf72bSMaxim Sobolev return (0); 498b01bf72bSMaxim Sobolev } 499b01bf72bSMaxim Sobolev 500b01bf72bSMaxim Sobolev return (1); 501b01bf72bSMaxim Sobolev } 502904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */ 503e9ed3a70SBrooks Davis #endif /* !PRE7_COMPAT */ 504e9ed3a70SBrooks Davis 505e9ed3a70SBrooks Davis union pci_conf_union { 506e9ed3a70SBrooks Davis struct pci_conf pc; 507b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 508b7edb6faSBrooks Davis struct pci_conf32 pc32; 509b7edb6faSBrooks Davis #endif 510e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 511*56ecc8a9SWarner Losh struct pci_conf_freebsd6 pco; 512e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32 513*56ecc8a9SWarner Losh struct pci_conf_freebsd6_32 pco32; 514e9ed3a70SBrooks Davis #endif 515e9ed3a70SBrooks Davis #endif 516e9ed3a70SBrooks Davis }; 517e9ed3a70SBrooks Davis 518e9ed3a70SBrooks Davis static int 519e9ed3a70SBrooks Davis pci_conf_match(u_long cmd, struct pci_match_conf *matches, int num_matches, 520e9ed3a70SBrooks Davis struct pci_conf *match_buf) 521e9ed3a70SBrooks Davis { 522e9ed3a70SBrooks Davis 523e9ed3a70SBrooks Davis switch (cmd) { 524e9ed3a70SBrooks Davis case PCIOCGETCONF: 525e9ed3a70SBrooks Davis return (pci_conf_match_native( 526e9ed3a70SBrooks Davis (struct pci_match_conf *)matches, num_matches, match_buf)); 527b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 528b7edb6faSBrooks Davis case PCIOCGETCONF32: 529b7edb6faSBrooks Davis return (pci_conf_match32((struct pci_match_conf32 *)matches, 530b7edb6faSBrooks Davis num_matches, match_buf)); 531b7edb6faSBrooks Davis #endif 532e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 533*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 534*56ecc8a9SWarner Losh return (pci_conf_match_freebsd6( 535*56ecc8a9SWarner Losh (struct pci_match_conf_freebsd6 *)matches, num_matches, 536e9ed3a70SBrooks Davis match_buf)); 537e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32 538*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 539*56ecc8a9SWarner Losh return (pci_conf_match_freebsd6_32( 540*56ecc8a9SWarner Losh (struct pci_match_conf_freebsd6_32 *)matches, num_matches, 541e9ed3a70SBrooks Davis match_buf)); 542e9ed3a70SBrooks Davis #endif 543e9ed3a70SBrooks Davis #endif 544e9ed3a70SBrooks Davis default: 545e9ed3a70SBrooks Davis /* programmer error */ 546e9ed3a70SBrooks Davis return (0); 547e9ed3a70SBrooks Davis } 548e9ed3a70SBrooks Davis } 54933d3fffaSMarius Strobl 55074aa2d49SJohn Baldwin /* 55174aa2d49SJohn Baldwin * Like PVE_NEXT but takes an explicit length since 'pve' is a user 55274aa2d49SJohn Baldwin * pointer that cannot be dereferenced. 55374aa2d49SJohn Baldwin */ 55474aa2d49SJohn Baldwin #define PVE_NEXT_LEN(pve, datalen) \ 55574aa2d49SJohn Baldwin ((struct pci_vpd_element *)((char *)(pve) + \ 55674aa2d49SJohn Baldwin sizeof(struct pci_vpd_element) + (datalen))) 55774aa2d49SJohn Baldwin 5588983cfbfSMike Smith static int 55984b755dfSJohn Baldwin pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio) 56084b755dfSJohn Baldwin { 56184b755dfSJohn Baldwin struct pci_vpd_element vpd_element, *vpd_user; 56284b755dfSJohn Baldwin struct pcicfg_vpd *vpd; 563f01c8633SStefan Eßer size_t len, datalen; 56484b755dfSJohn Baldwin int error, i; 56584b755dfSJohn Baldwin 56684b755dfSJohn Baldwin vpd = pci_fetch_vpd_list(dev); 56784b755dfSJohn Baldwin if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL) 56884b755dfSJohn Baldwin return (ENXIO); 56984b755dfSJohn Baldwin 57084b755dfSJohn Baldwin /* 57184b755dfSJohn Baldwin * Calculate the amount of space needed in the data buffer. An 57284b755dfSJohn Baldwin * identifier element is always present followed by the read-only 57384b755dfSJohn Baldwin * and read-write keywords. 57484b755dfSJohn Baldwin */ 57584b755dfSJohn Baldwin len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident); 57684b755dfSJohn Baldwin for (i = 0; i < vpd->vpd_rocnt; i++) 57784b755dfSJohn Baldwin len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len; 57884b755dfSJohn Baldwin for (i = 0; i < vpd->vpd_wcnt; i++) 57984b755dfSJohn Baldwin len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len; 58084b755dfSJohn Baldwin 58184b755dfSJohn Baldwin if (lvio->plvi_len == 0) { 58284b755dfSJohn Baldwin lvio->plvi_len = len; 58384b755dfSJohn Baldwin return (0); 58484b755dfSJohn Baldwin } 58584b755dfSJohn Baldwin if (lvio->plvi_len < len) { 58684b755dfSJohn Baldwin lvio->plvi_len = len; 58784b755dfSJohn Baldwin return (ENOMEM); 58884b755dfSJohn Baldwin } 58984b755dfSJohn Baldwin 59084b755dfSJohn Baldwin /* 59184b755dfSJohn Baldwin * Copyout the identifier string followed by each keyword and 59284b755dfSJohn Baldwin * value. 59384b755dfSJohn Baldwin */ 594f01c8633SStefan Eßer datalen = strlen(vpd->vpd_ident); 595f01c8633SStefan Eßer KASSERT(datalen <= 255, ("invalid VPD ident length")); 59684b755dfSJohn Baldwin vpd_user = lvio->plvi_data; 59784b755dfSJohn Baldwin vpd_element.pve_keyword[0] = '\0'; 59884b755dfSJohn Baldwin vpd_element.pve_keyword[1] = '\0'; 59984b755dfSJohn Baldwin vpd_element.pve_flags = PVE_FLAG_IDENT; 600f01c8633SStefan Eßer vpd_element.pve_datalen = datalen; 60184b755dfSJohn Baldwin error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); 60284b755dfSJohn Baldwin if (error) 60384b755dfSJohn Baldwin return (error); 604f01c8633SStefan Eßer error = copyout(vpd->vpd_ident, vpd_user->pve_data, datalen); 60584b755dfSJohn Baldwin if (error) 60684b755dfSJohn Baldwin return (error); 60774aa2d49SJohn Baldwin vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen); 60884b755dfSJohn Baldwin vpd_element.pve_flags = 0; 60984b755dfSJohn Baldwin for (i = 0; i < vpd->vpd_rocnt; i++) { 61084b755dfSJohn Baldwin vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0]; 61184b755dfSJohn Baldwin vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1]; 61284b755dfSJohn Baldwin vpd_element.pve_datalen = vpd->vpd_ros[i].len; 61384b755dfSJohn Baldwin error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); 61484b755dfSJohn Baldwin if (error) 61584b755dfSJohn Baldwin return (error); 61684b755dfSJohn Baldwin error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data, 61784b755dfSJohn Baldwin vpd->vpd_ros[i].len); 61884b755dfSJohn Baldwin if (error) 61984b755dfSJohn Baldwin return (error); 62074aa2d49SJohn Baldwin vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen); 62184b755dfSJohn Baldwin } 62284b755dfSJohn Baldwin vpd_element.pve_flags = PVE_FLAG_RW; 62384b755dfSJohn Baldwin for (i = 0; i < vpd->vpd_wcnt; i++) { 62484b755dfSJohn Baldwin vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0]; 62584b755dfSJohn Baldwin vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1]; 62684b755dfSJohn Baldwin vpd_element.pve_datalen = vpd->vpd_w[i].len; 62784b755dfSJohn Baldwin error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); 62884b755dfSJohn Baldwin if (error) 62984b755dfSJohn Baldwin return (error); 63084b755dfSJohn Baldwin error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data, 63184b755dfSJohn Baldwin vpd->vpd_w[i].len); 63284b755dfSJohn Baldwin if (error) 63384b755dfSJohn Baldwin return (error); 63474aa2d49SJohn Baldwin vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen); 63584b755dfSJohn Baldwin } 63684b755dfSJohn Baldwin KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len, 63784b755dfSJohn Baldwin ("length mismatch")); 63884b755dfSJohn Baldwin lvio->plvi_len = len; 63984b755dfSJohn Baldwin return (0); 64084b755dfSJohn Baldwin } 64184b755dfSJohn Baldwin 642e9ed3a70SBrooks Davis static size_t 643e9ed3a70SBrooks Davis pci_match_conf_size(u_long cmd) 6448983cfbfSMike Smith { 6458983cfbfSMike Smith 64684b755dfSJohn Baldwin switch (cmd) { 647e9ed3a70SBrooks Davis case PCIOCGETCONF: 648e9ed3a70SBrooks Davis return (sizeof(struct pci_match_conf)); 649b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 650b7edb6faSBrooks Davis case PCIOCGETCONF32: 651b7edb6faSBrooks Davis return (sizeof(struct pci_match_conf32)); 652b7edb6faSBrooks Davis #endif 65384b755dfSJohn Baldwin #ifdef PRE7_COMPAT 654*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 655*56ecc8a9SWarner Losh return (sizeof(struct pci_match_conf_freebsd6)); 65684b755dfSJohn Baldwin #ifdef COMPAT_FREEBSD32 657*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 658*56ecc8a9SWarner Losh return (sizeof(struct pci_match_conf_freebsd6_32)); 65984b755dfSJohn Baldwin #endif 660e9ed3a70SBrooks Davis #endif 661e9ed3a70SBrooks Davis default: 662e9ed3a70SBrooks Davis /* programmer error */ 663e9ed3a70SBrooks Davis return (0); 664e9ed3a70SBrooks Davis } 665e9ed3a70SBrooks Davis } 666e9ed3a70SBrooks Davis 667e9ed3a70SBrooks Davis static size_t 668e9ed3a70SBrooks Davis pci_conf_size(u_long cmd) 669e9ed3a70SBrooks Davis { 670e9ed3a70SBrooks Davis 671e9ed3a70SBrooks Davis switch (cmd) { 672e9ed3a70SBrooks Davis case PCIOCGETCONF: 673e9ed3a70SBrooks Davis return (sizeof(struct pci_conf)); 674b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 675b7edb6faSBrooks Davis case PCIOCGETCONF32: 676b7edb6faSBrooks Davis return (sizeof(struct pci_conf32)); 677b7edb6faSBrooks Davis #endif 678e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 679*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 680*56ecc8a9SWarner Losh return (sizeof(struct pci_conf_freebsd6)); 681e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32 682*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 683*56ecc8a9SWarner Losh return (sizeof(struct pci_conf_freebsd6_32)); 684e9ed3a70SBrooks Davis #endif 685e9ed3a70SBrooks Davis #endif 686e9ed3a70SBrooks Davis default: 687e9ed3a70SBrooks Davis /* programmer error */ 688e9ed3a70SBrooks Davis return (0); 689e9ed3a70SBrooks Davis } 690e9ed3a70SBrooks Davis } 691e9ed3a70SBrooks Davis 692e9ed3a70SBrooks Davis static void 693e9ed3a70SBrooks Davis pci_conf_io_init(struct pci_conf_io *cio, caddr_t data, u_long cmd) 694e9ed3a70SBrooks Davis { 695b7edb6faSBrooks Davis #if defined(COMPAT_FREEBSD32) 696e9ed3a70SBrooks Davis struct pci_conf_io32 *cio32; 697e9ed3a70SBrooks Davis #endif 698e9ed3a70SBrooks Davis 699e9ed3a70SBrooks Davis switch (cmd) { 700e9ed3a70SBrooks Davis case PCIOCGETCONF: 701e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 702*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 70384b755dfSJohn Baldwin #endif 704e9ed3a70SBrooks Davis *cio = *(struct pci_conf_io *)data; 705e9ed3a70SBrooks Davis return; 70684b755dfSJohn Baldwin 707b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 708b7edb6faSBrooks Davis case PCIOCGETCONF32: 709b7edb6faSBrooks Davis #ifdef PRE7_COMPAT 710*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 711b7edb6faSBrooks Davis #endif 712b01bf72bSMaxim Sobolev cio32 = (struct pci_conf_io32 *)data; 713b01bf72bSMaxim Sobolev cio->pat_buf_len = cio32->pat_buf_len; 714b01bf72bSMaxim Sobolev cio->num_patterns = cio32->num_patterns; 715b01bf72bSMaxim Sobolev cio->patterns = (void *)(uintptr_t)cio32->patterns; 716b01bf72bSMaxim Sobolev cio->match_buf_len = cio32->match_buf_len; 717b01bf72bSMaxim Sobolev cio->num_matches = cio32->num_matches; 718b01bf72bSMaxim Sobolev cio->matches = (void *)(uintptr_t)cio32->matches; 719b01bf72bSMaxim Sobolev cio->offset = cio32->offset; 720b01bf72bSMaxim Sobolev cio->generation = cio32->generation; 721b01bf72bSMaxim Sobolev cio->status = cio32->status; 722e9ed3a70SBrooks Davis return; 723904c3909SGleb Smirnoff #endif 724e9ed3a70SBrooks Davis 725e9ed3a70SBrooks Davis default: 726e9ed3a70SBrooks Davis /* programmer error */ 727e9ed3a70SBrooks Davis return; 728e9ed3a70SBrooks Davis } 729904c3909SGleb Smirnoff } 730904c3909SGleb Smirnoff 731e9ed3a70SBrooks Davis static void 732e9ed3a70SBrooks Davis pci_conf_io_update_data(const struct pci_conf_io *cio, caddr_t data, 733e9ed3a70SBrooks Davis u_long cmd) 734e9ed3a70SBrooks Davis { 735e9ed3a70SBrooks Davis struct pci_conf_io *d_cio; 736b7edb6faSBrooks Davis #if defined(COMPAT_FREEBSD32) 737e9ed3a70SBrooks Davis struct pci_conf_io32 *cio32; 738e9ed3a70SBrooks Davis #endif 739e9ed3a70SBrooks Davis 740904c3909SGleb Smirnoff switch (cmd) { 741e9ed3a70SBrooks Davis case PCIOCGETCONF: 742e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 743*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 744e9ed3a70SBrooks Davis #endif 745e9ed3a70SBrooks Davis d_cio = (struct pci_conf_io *)data; 746e9ed3a70SBrooks Davis d_cio->status = cio->status; 747e9ed3a70SBrooks Davis d_cio->generation = cio->generation; 748e9ed3a70SBrooks Davis d_cio->offset = cio->offset; 749e9ed3a70SBrooks Davis d_cio->num_matches = cio->num_matches; 750e9ed3a70SBrooks Davis return; 751e9ed3a70SBrooks Davis 752b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 753b7edb6faSBrooks Davis case PCIOCGETCONF32: 754b7edb6faSBrooks Davis #ifdef PRE7_COMPAT 755*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 756b7edb6faSBrooks Davis #endif 757e9ed3a70SBrooks Davis cio32 = (struct pci_conf_io32 *)data; 758e9ed3a70SBrooks Davis 759e9ed3a70SBrooks Davis cio32->status = cio->status; 760e9ed3a70SBrooks Davis cio32->generation = cio->generation; 761e9ed3a70SBrooks Davis cio32->offset = cio->offset; 762e9ed3a70SBrooks Davis cio32->num_matches = cio->num_matches; 763e9ed3a70SBrooks Davis return; 764e9ed3a70SBrooks Davis #endif 765e9ed3a70SBrooks Davis 766e9ed3a70SBrooks Davis default: 767e9ed3a70SBrooks Davis /* programmer error */ 768e9ed3a70SBrooks Davis return; 769e9ed3a70SBrooks Davis } 770e9ed3a70SBrooks Davis } 771e9ed3a70SBrooks Davis 772e9ed3a70SBrooks Davis static void 773e9ed3a70SBrooks Davis pci_conf_for_copyout(const struct pci_conf *pcp, union pci_conf_union *pcup, 774e9ed3a70SBrooks Davis u_long cmd) 775e9ed3a70SBrooks Davis { 776e9ed3a70SBrooks Davis 777e9ed3a70SBrooks Davis memset(pcup, 0, sizeof(*pcup)); 778e9ed3a70SBrooks Davis 779e9ed3a70SBrooks Davis switch (cmd) { 780e9ed3a70SBrooks Davis case PCIOCGETCONF: 781e9ed3a70SBrooks Davis pcup->pc = *pcp; 782e9ed3a70SBrooks Davis return; 783e9ed3a70SBrooks Davis 784b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32 785b7edb6faSBrooks Davis case PCIOCGETCONF32: 786b7edb6faSBrooks Davis pcup->pc32.pc_sel = pcp->pc_sel; 787b7edb6faSBrooks Davis pcup->pc32.pc_hdr = pcp->pc_hdr; 788b7edb6faSBrooks Davis pcup->pc32.pc_subvendor = pcp->pc_subvendor; 789b7edb6faSBrooks Davis pcup->pc32.pc_subdevice = pcp->pc_subdevice; 790b7edb6faSBrooks Davis pcup->pc32.pc_vendor = pcp->pc_vendor; 791b7edb6faSBrooks Davis pcup->pc32.pc_device = pcp->pc_device; 792b7edb6faSBrooks Davis pcup->pc32.pc_class = pcp->pc_class; 793b7edb6faSBrooks Davis pcup->pc32.pc_subclass = pcp->pc_subclass; 794b7edb6faSBrooks Davis pcup->pc32.pc_progif = pcp->pc_progif; 795b7edb6faSBrooks Davis pcup->pc32.pc_revid = pcp->pc_revid; 796b7edb6faSBrooks Davis strlcpy(pcup->pc32.pd_name, pcp->pd_name, 797b7edb6faSBrooks Davis sizeof(pcup->pc32.pd_name)); 798b7edb6faSBrooks Davis pcup->pc32.pd_unit = (uint32_t)pcp->pd_unit; 799b7edb6faSBrooks Davis return; 800b7edb6faSBrooks Davis #endif 801b7edb6faSBrooks Davis 802904c3909SGleb Smirnoff #ifdef PRE7_COMPAT 803904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32 804*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 805e9ed3a70SBrooks Davis pcup->pco32.pc_sel.pc_bus = pcp->pc_sel.pc_bus; 806e9ed3a70SBrooks Davis pcup->pco32.pc_sel.pc_dev = pcp->pc_sel.pc_dev; 807e9ed3a70SBrooks Davis pcup->pco32.pc_sel.pc_func = pcp->pc_sel.pc_func; 808e9ed3a70SBrooks Davis pcup->pco32.pc_hdr = pcp->pc_hdr; 809e9ed3a70SBrooks Davis pcup->pco32.pc_subvendor = pcp->pc_subvendor; 810e9ed3a70SBrooks Davis pcup->pco32.pc_subdevice = pcp->pc_subdevice; 811e9ed3a70SBrooks Davis pcup->pco32.pc_vendor = pcp->pc_vendor; 812e9ed3a70SBrooks Davis pcup->pco32.pc_device = pcp->pc_device; 813e9ed3a70SBrooks Davis pcup->pco32.pc_class = pcp->pc_class; 814e9ed3a70SBrooks Davis pcup->pco32.pc_subclass = pcp->pc_subclass; 815e9ed3a70SBrooks Davis pcup->pco32.pc_progif = pcp->pc_progif; 816e9ed3a70SBrooks Davis pcup->pco32.pc_revid = pcp->pc_revid; 817e9ed3a70SBrooks Davis strlcpy(pcup->pco32.pd_name, pcp->pd_name, 818e9ed3a70SBrooks Davis sizeof(pcup->pco32.pd_name)); 819e9ed3a70SBrooks Davis pcup->pco32.pd_unit = (uint32_t)pcp->pd_unit; 820e9ed3a70SBrooks Davis return; 8218983cfbfSMike Smith 822e9ed3a70SBrooks Davis #endif /* COMPAT_FREEBSD32 */ 823*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 824e9ed3a70SBrooks Davis pcup->pco.pc_sel.pc_bus = pcp->pc_sel.pc_bus; 825e9ed3a70SBrooks Davis pcup->pco.pc_sel.pc_dev = pcp->pc_sel.pc_dev; 826e9ed3a70SBrooks Davis pcup->pco.pc_sel.pc_func = pcp->pc_sel.pc_func; 827e9ed3a70SBrooks Davis pcup->pco.pc_hdr = pcp->pc_hdr; 828e9ed3a70SBrooks Davis pcup->pco.pc_subvendor = pcp->pc_subvendor; 829e9ed3a70SBrooks Davis pcup->pco.pc_subdevice = pcp->pc_subdevice; 830e9ed3a70SBrooks Davis pcup->pco.pc_vendor = pcp->pc_vendor; 831e9ed3a70SBrooks Davis pcup->pco.pc_device = pcp->pc_device; 832e9ed3a70SBrooks Davis pcup->pco.pc_class = pcp->pc_class; 833e9ed3a70SBrooks Davis pcup->pco.pc_subclass = pcp->pc_subclass; 834e9ed3a70SBrooks Davis pcup->pco.pc_progif = pcp->pc_progif; 835e9ed3a70SBrooks Davis pcup->pco.pc_revid = pcp->pc_revid; 836e9ed3a70SBrooks Davis strlcpy(pcup->pco.pd_name, pcp->pd_name, 837e9ed3a70SBrooks Davis sizeof(pcup->pco.pd_name)); 838e9ed3a70SBrooks Davis pcup->pco.pd_unit = pcp->pd_unit; 839e9ed3a70SBrooks Davis return; 840e9ed3a70SBrooks Davis #endif /* PRE7_COMPAT */ 841e9ed3a70SBrooks Davis 842e9ed3a70SBrooks Davis default: 843e9ed3a70SBrooks Davis /* programmer error */ 844e9ed3a70SBrooks Davis return; 845e9ed3a70SBrooks Davis } 846e9ed3a70SBrooks Davis } 847e9ed3a70SBrooks Davis 848e9ed3a70SBrooks Davis static int 84987842989SKonstantin Belousov pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm) 85087842989SKonstantin Belousov { 85187842989SKonstantin Belousov vm_map_t map; 85287842989SKonstantin Belousov vm_object_t obj; 85387842989SKonstantin Belousov struct thread *td; 85487842989SKonstantin Belousov struct sglist *sg; 85587842989SKonstantin Belousov struct pci_map *pm; 8569857e00aSMarcin Wojtas rman_res_t membase; 85787842989SKonstantin Belousov vm_paddr_t pbase; 85887842989SKonstantin Belousov vm_size_t plen; 85987842989SKonstantin Belousov vm_offset_t addr; 86087842989SKonstantin Belousov vm_prot_t prot; 86187842989SKonstantin Belousov int error, flags; 86287842989SKonstantin Belousov 86387842989SKonstantin Belousov td = curthread; 86487842989SKonstantin Belousov map = &td->td_proc->p_vmspace->vm_map; 86587842989SKonstantin Belousov if ((pbm->pbm_flags & ~(PCIIO_BAR_MMAP_FIXED | PCIIO_BAR_MMAP_EXCL | 86687842989SKonstantin Belousov PCIIO_BAR_MMAP_RW | PCIIO_BAR_MMAP_ACTIVATE)) != 0 || 86787842989SKonstantin Belousov pbm->pbm_memattr != (vm_memattr_t)pbm->pbm_memattr || 86887842989SKonstantin Belousov !pmap_is_valid_memattr(map->pmap, pbm->pbm_memattr)) 86987842989SKonstantin Belousov return (EINVAL); 87087842989SKonstantin Belousov 87187842989SKonstantin Belousov /* Fetch the BAR physical base and length. */ 87287842989SKonstantin Belousov pm = pci_find_bar(pcidev, pbm->pbm_reg); 87387842989SKonstantin Belousov if (pm == NULL) 87487842989SKonstantin Belousov return (EINVAL); 87587842989SKonstantin Belousov if (!pci_bar_enabled(pcidev, pm)) 87687842989SKonstantin Belousov return (EBUSY); /* XXXKIB enable if _ACTIVATE */ 87787842989SKonstantin Belousov if (!PCI_BAR_MEM(pm->pm_value)) 87887842989SKonstantin Belousov return (EIO); 8791fb99e97SMark Johnston error = bus_translate_resource(pcidev, SYS_RES_MEMORY, 8809857e00aSMarcin Wojtas pm->pm_value & PCIM_BAR_MEM_BASE, &membase); 881f2f1ab39SMarcin Wojtas if (error != 0) 882f2f1ab39SMarcin Wojtas return (error); 883f2f1ab39SMarcin Wojtas 884f48c41acSHans Petter Selasky pbase = trunc_page(membase); 885f48c41acSHans Petter Selasky plen = round_page(membase + ((pci_addr_t)1 << pm->pm_size)) - 88687842989SKonstantin Belousov pbase; 88787842989SKonstantin Belousov prot = VM_PROT_READ | (((pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) ? 88887842989SKonstantin Belousov VM_PROT_WRITE : 0); 88987842989SKonstantin Belousov 89087842989SKonstantin Belousov /* Create vm structures and mmap. */ 89187842989SKonstantin Belousov sg = sglist_alloc(1, M_WAITOK); 89287842989SKonstantin Belousov error = sglist_append_phys(sg, pbase, plen); 89387842989SKonstantin Belousov if (error != 0) 89487842989SKonstantin Belousov goto out; 89587842989SKonstantin Belousov obj = vm_pager_allocate(OBJT_SG, sg, plen, prot, 0, td->td_ucred); 89687842989SKonstantin Belousov if (obj == NULL) { 89787842989SKonstantin Belousov error = EIO; 89887842989SKonstantin Belousov goto out; 89987842989SKonstantin Belousov } 90087842989SKonstantin Belousov obj->memattr = pbm->pbm_memattr; 90187842989SKonstantin Belousov flags = MAP_SHARED; 90287842989SKonstantin Belousov addr = 0; 90387842989SKonstantin Belousov if ((pbm->pbm_flags & PCIIO_BAR_MMAP_FIXED) != 0) { 90487842989SKonstantin Belousov addr = (uintptr_t)pbm->pbm_map_base; 90587842989SKonstantin Belousov flags |= MAP_FIXED; 90687842989SKonstantin Belousov } 90787842989SKonstantin Belousov if ((pbm->pbm_flags & PCIIO_BAR_MMAP_EXCL) != 0) 90887842989SKonstantin Belousov flags |= MAP_CHECK_EXCL; 90987842989SKonstantin Belousov error = vm_mmap_object(map, &addr, plen, prot, prot, flags, obj, 0, 91087842989SKonstantin Belousov FALSE, td); 91187842989SKonstantin Belousov if (error != 0) { 91287842989SKonstantin Belousov vm_object_deallocate(obj); 91387842989SKonstantin Belousov goto out; 91487842989SKonstantin Belousov } 91587842989SKonstantin Belousov pbm->pbm_map_base = (void *)addr; 91687842989SKonstantin Belousov pbm->pbm_map_length = plen; 917f48c41acSHans Petter Selasky pbm->pbm_bar_off = membase - pbase; 91887842989SKonstantin Belousov pbm->pbm_bar_length = (pci_addr_t)1 << pm->pm_size; 91987842989SKonstantin Belousov 92087842989SKonstantin Belousov out: 92187842989SKonstantin Belousov sglist_free(sg); 92287842989SKonstantin Belousov return (error); 92387842989SKonstantin Belousov } 92487842989SKonstantin Belousov 92587842989SKonstantin Belousov static int 9267e14be0bSMark Johnston pci_bar_io(device_t pcidev, struct pci_bar_ioreq *pbi) 9277e14be0bSMark Johnston { 9287e14be0bSMark Johnston struct pci_map *pm; 9297e14be0bSMark Johnston struct resource *res; 9307e14be0bSMark Johnston uint32_t offset, width; 9317e14be0bSMark Johnston int bar, error, type; 9327e14be0bSMark Johnston 9337e14be0bSMark Johnston if (pbi->pbi_op != PCIBARIO_READ && 9347e14be0bSMark Johnston pbi->pbi_op != PCIBARIO_WRITE) 9357e14be0bSMark Johnston return (EINVAL); 9367e14be0bSMark Johnston 9377e14be0bSMark Johnston bar = PCIR_BAR(pbi->pbi_bar); 9387e14be0bSMark Johnston pm = pci_find_bar(pcidev, bar); 9397e14be0bSMark Johnston if (pm == NULL) 9407e14be0bSMark Johnston return (EINVAL); 9417e14be0bSMark Johnston 9427e14be0bSMark Johnston offset = pbi->pbi_offset; 9437e14be0bSMark Johnston width = pbi->pbi_width; 9447e14be0bSMark Johnston 9457e14be0bSMark Johnston if (offset + width < offset || 9467e14be0bSMark Johnston ((pci_addr_t)1 << pm->pm_size) < offset + width) 9477e14be0bSMark Johnston return (EINVAL); 9487e14be0bSMark Johnston 9497e14be0bSMark Johnston type = PCI_BAR_MEM(pm->pm_value) ? SYS_RES_MEMORY : SYS_RES_IOPORT; 9507e14be0bSMark Johnston 9517e14be0bSMark Johnston /* 9527e14be0bSMark Johnston * This will fail if a driver has allocated the resource. This could be 9537e14be0bSMark Johnston * worked around by detecting that case and using bus_map_resource() to 9547e14be0bSMark Johnston * populate the handle, but so far this is not needed. 9557e14be0bSMark Johnston */ 9567e14be0bSMark Johnston res = bus_alloc_resource_any(pcidev, type, &bar, RF_ACTIVE); 9577e14be0bSMark Johnston if (res == NULL) 9587e14be0bSMark Johnston return (ENOENT); 9597e14be0bSMark Johnston 9607e14be0bSMark Johnston error = 0; 9617e14be0bSMark Johnston switch (pbi->pbi_op) { 9627e14be0bSMark Johnston case PCIBARIO_READ: 9637e14be0bSMark Johnston switch (pbi->pbi_width) { 9647e14be0bSMark Johnston case 1: 9657e14be0bSMark Johnston pbi->pbi_value = bus_read_1(res, offset); 9667e14be0bSMark Johnston break; 9677e14be0bSMark Johnston case 2: 9687e14be0bSMark Johnston pbi->pbi_value = bus_read_2(res, offset); 9697e14be0bSMark Johnston break; 9707e14be0bSMark Johnston case 4: 9717e14be0bSMark Johnston pbi->pbi_value = bus_read_4(res, offset); 9727e14be0bSMark Johnston break; 9737e14be0bSMark Johnston #ifndef __i386__ 9747e14be0bSMark Johnston case 8: 9757e14be0bSMark Johnston pbi->pbi_value = bus_read_8(res, offset); 9767e14be0bSMark Johnston break; 9777e14be0bSMark Johnston #endif 9787e14be0bSMark Johnston default: 9797e14be0bSMark Johnston error = EINVAL; 9807e14be0bSMark Johnston break; 9817e14be0bSMark Johnston } 9827e14be0bSMark Johnston break; 9837e14be0bSMark Johnston case PCIBARIO_WRITE: 9847e14be0bSMark Johnston switch (pbi->pbi_width) { 9857e14be0bSMark Johnston case 1: 9867e14be0bSMark Johnston bus_write_1(res, offset, pbi->pbi_value); 9877e14be0bSMark Johnston break; 9887e14be0bSMark Johnston case 2: 9897e14be0bSMark Johnston bus_write_2(res, offset, pbi->pbi_value); 9907e14be0bSMark Johnston break; 9917e14be0bSMark Johnston case 4: 9927e14be0bSMark Johnston bus_write_4(res, offset, pbi->pbi_value); 9937e14be0bSMark Johnston break; 9947e14be0bSMark Johnston #ifndef __i386__ 9957e14be0bSMark Johnston case 8: 9967e14be0bSMark Johnston bus_write_8(res, offset, pbi->pbi_value); 9977e14be0bSMark Johnston break; 9987e14be0bSMark Johnston #endif 9997e14be0bSMark Johnston default: 10007e14be0bSMark Johnston error = EINVAL; 10017e14be0bSMark Johnston break; 10027e14be0bSMark Johnston } 10037e14be0bSMark Johnston break; 10047e14be0bSMark Johnston } 10057e14be0bSMark Johnston 10067e14be0bSMark Johnston bus_release_resource(pcidev, type, bar, res); 10077e14be0bSMark Johnston 10087e14be0bSMark Johnston return (error); 10097e14be0bSMark Johnston } 10107e14be0bSMark Johnston 10117e14be0bSMark Johnston static int 1012e9ed3a70SBrooks Davis pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1013e9ed3a70SBrooks Davis { 1014e9ed3a70SBrooks Davis device_t pcidev; 1015e9ed3a70SBrooks Davis const char *name; 1016e9ed3a70SBrooks Davis struct devlist *devlist_head; 1017e9ed3a70SBrooks Davis struct pci_conf_io *cio = NULL; 1018e9ed3a70SBrooks Davis struct pci_devinfo *dinfo; 1019e9ed3a70SBrooks Davis struct pci_io *io; 10207e14be0bSMark Johnston struct pci_bar_ioreq *pbi; 1021e9ed3a70SBrooks Davis struct pci_bar_io *bio; 1022e9ed3a70SBrooks Davis struct pci_list_vpd_io *lvio; 1023e9ed3a70SBrooks Davis struct pci_match_conf *pattern_buf; 1024e9ed3a70SBrooks Davis struct pci_map *pm; 102587842989SKonstantin Belousov struct pci_bar_mmap *pbm; 1026e9ed3a70SBrooks Davis size_t confsz, iolen; 1027e9ed3a70SBrooks Davis int error, ionum, i, num_patterns; 1028e9ed3a70SBrooks Davis union pci_conf_union pcu; 1029e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 1030e9ed3a70SBrooks Davis struct pci_io iodata; 1031*56ecc8a9SWarner Losh struct pci_io_freebsd6 *io_freebsd6; 1032e9ed3a70SBrooks Davis 1033*56ecc8a9SWarner Losh io_freebsd6 = NULL; 1034e9ed3a70SBrooks Davis #endif 1035e9ed3a70SBrooks Davis 103685ae35efSKonstantin Belousov /* 103785ae35efSKonstantin Belousov * Interpret read-only opened /dev/pci as a promise that no 103885ae35efSKonstantin Belousov * operation of the file descriptor could modify system state, 103985ae35efSKonstantin Belousov * including side-effects due to reading devices registers. 104085ae35efSKonstantin Belousov */ 104185ae35efSKonstantin Belousov if ((flag & FWRITE) == 0) { 1042e9ed3a70SBrooks Davis switch (cmd) { 1043e9ed3a70SBrooks Davis case PCIOCGETCONF: 1044b56f51f1SBrooks Davis #ifdef COMPAT_FREEBSD32 1045b56f51f1SBrooks Davis case PCIOCGETCONF32: 1046b56f51f1SBrooks Davis #endif 1047e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 1048*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 1049e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32 1050*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 1051e9ed3a70SBrooks Davis #endif 1052e9ed3a70SBrooks Davis #endif 1053e9ed3a70SBrooks Davis case PCIOCGETBAR: 1054e9ed3a70SBrooks Davis case PCIOCLISTVPD: 1055e9ed3a70SBrooks Davis break; 1056e9ed3a70SBrooks Davis default: 1057e9ed3a70SBrooks Davis return (EPERM); 1058e9ed3a70SBrooks Davis } 1059e9ed3a70SBrooks Davis } 1060e9ed3a70SBrooks Davis 1061c6df6f53SWarner Losh /* 1062c6df6f53SWarner Losh * Use bus topology lock to ensure that the pci list of devies doesn't 1063c6df6f53SWarner Losh * change while we're traversing the list, in some cases multiple times. 1064c6df6f53SWarner Losh */ 1065c6df6f53SWarner Losh bus_topo_lock(); 106696b506a5SWarner Losh 1067e9ed3a70SBrooks Davis switch (cmd) { 1068e9ed3a70SBrooks Davis case PCIOCGETCONF: 1069b56f51f1SBrooks Davis #ifdef COMPAT_FREEBSD32 1070b56f51f1SBrooks Davis case PCIOCGETCONF32: 1071b56f51f1SBrooks Davis #endif 1072e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT 1073*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6: 1074e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32 1075*56ecc8a9SWarner Losh case PCIOCGETCONF_FREEBSD6_32: 1076e9ed3a70SBrooks Davis #endif 1077e9ed3a70SBrooks Davis #endif 1078e9ed3a70SBrooks Davis cio = malloc(sizeof(struct pci_conf_io), M_TEMP, 1079e9ed3a70SBrooks Davis M_WAITOK | M_ZERO); 1080e9ed3a70SBrooks Davis pci_conf_io_init(cio, data, cmd); 108133d3fffaSMarius Strobl pattern_buf = NULL; 10828983cfbfSMike Smith num_patterns = 0; 10838983cfbfSMike Smith dinfo = NULL; 10848983cfbfSMike Smith 108533d3fffaSMarius Strobl cio->num_matches = 0; 108633d3fffaSMarius Strobl 10878983cfbfSMike Smith /* 10888983cfbfSMike Smith * If the user specified an offset into the device list, 10898983cfbfSMike Smith * but the list has changed since they last called this 10908983cfbfSMike Smith * ioctl, tell them that the list has changed. They will 10918983cfbfSMike Smith * have to get the list from the beginning. 10928983cfbfSMike Smith */ 10938983cfbfSMike Smith if ((cio->offset != 0) 10948983cfbfSMike Smith && (cio->generation != pci_generation)){ 10958983cfbfSMike Smith cio->status = PCI_GETCONF_LIST_CHANGED; 10968983cfbfSMike Smith error = 0; 1097b01bf72bSMaxim Sobolev goto getconfexit; 10988983cfbfSMike Smith } 10998983cfbfSMike Smith 11008983cfbfSMike Smith /* 11018983cfbfSMike Smith * Check to see whether the user has asked for an offset 11028983cfbfSMike Smith * past the end of our list. 11038983cfbfSMike Smith */ 11048983cfbfSMike Smith if (cio->offset >= pci_numdevs) { 11058983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 11068983cfbfSMike Smith error = 0; 1107b01bf72bSMaxim Sobolev goto getconfexit; 11088983cfbfSMike Smith } 11098983cfbfSMike Smith 11108983cfbfSMike Smith /* get the head of the device queue */ 11118983cfbfSMike Smith devlist_head = &pci_devq; 11128983cfbfSMike Smith 11138983cfbfSMike Smith /* 11148983cfbfSMike Smith * Determine how much room we have for pci_conf structures. 11158983cfbfSMike Smith * Round the user's buffer size down to the nearest 11168983cfbfSMike Smith * multiple of sizeof(struct pci_conf) in case the user 11178983cfbfSMike Smith * didn't specify a multiple of that size. 11188983cfbfSMike Smith */ 1119e9ed3a70SBrooks Davis confsz = pci_conf_size(cmd); 112033d3fffaSMarius Strobl iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz), 112133d3fffaSMarius Strobl pci_numdevs * confsz); 11228983cfbfSMike Smith 11238983cfbfSMike Smith /* 11248983cfbfSMike Smith * Since we know that iolen is a multiple of the size of 11258983cfbfSMike Smith * the pciconf union, it's okay to do this. 11268983cfbfSMike Smith */ 112733d3fffaSMarius Strobl ionum = iolen / confsz; 11288983cfbfSMike Smith 11298983cfbfSMike Smith /* 11308983cfbfSMike Smith * If this test is true, the user wants the pci_conf 11318983cfbfSMike Smith * structures returned to match the supplied entries. 11328983cfbfSMike Smith */ 1133e3f932deSJohn-Mark Gurney if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 11348983cfbfSMike Smith && (cio->pat_buf_len > 0)) { 11358983cfbfSMike Smith /* 11368983cfbfSMike Smith * pat_buf_len needs to be: 11378983cfbfSMike Smith * num_patterns * sizeof(struct pci_match_conf) 11388983cfbfSMike Smith * While it is certainly possible the user just 11398983cfbfSMike Smith * allocated a large buffer, but set the number of 11408983cfbfSMike Smith * matches correctly, it is far more likely that 11418983cfbfSMike Smith * their kernel doesn't match the userland utility 11428983cfbfSMike Smith * they're using. It's also possible that the user 11438983cfbfSMike Smith * forgot to initialize some variables. Yes, this 11448983cfbfSMike Smith * may be overly picky, but I hazard to guess that 11458983cfbfSMike Smith * it's far more likely to just catch folks that 11468983cfbfSMike Smith * updated their kernel but not their userland. 11478983cfbfSMike Smith */ 1148e9ed3a70SBrooks Davis if (cio->num_patterns * pci_match_conf_size(cmd) != 1149e9ed3a70SBrooks Davis cio->pat_buf_len) { 115033d3fffaSMarius Strobl /* The user made a mistake, return an error. */ 11518983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 11528983cfbfSMike Smith error = EINVAL; 1153b01bf72bSMaxim Sobolev goto getconfexit; 11548983cfbfSMike Smith } 11558983cfbfSMike Smith 11568983cfbfSMike Smith /* 11578983cfbfSMike Smith * Allocate a buffer to hold the patterns. 11588983cfbfSMike Smith */ 11598983cfbfSMike Smith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 1160a163d034SWarner Losh M_WAITOK); 11618983cfbfSMike Smith error = copyin(cio->patterns, pattern_buf, 11628983cfbfSMike Smith cio->pat_buf_len); 1163d08239c1SJohn-Mark Gurney if (error != 0) { 1164d08239c1SJohn-Mark Gurney error = EINVAL; 1165d08239c1SJohn-Mark Gurney goto getconfexit; 1166d08239c1SJohn-Mark Gurney } 11678983cfbfSMike Smith num_patterns = cio->num_patterns; 11688983cfbfSMike Smith } else if ((cio->num_patterns > 0) 11698983cfbfSMike Smith || (cio->pat_buf_len > 0)) { 11708983cfbfSMike Smith /* 11718983cfbfSMike Smith * The user made a mistake, spit out an error. 11728983cfbfSMike Smith */ 11738983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 11748983cfbfSMike Smith error = EINVAL; 1175b01bf72bSMaxim Sobolev goto getconfexit; 117633d3fffaSMarius Strobl } 11778983cfbfSMike Smith 11788983cfbfSMike Smith /* 11798983cfbfSMike Smith * Go through the list of devices and copy out the devices 11808983cfbfSMike Smith * that match the user's criteria. 11818983cfbfSMike Smith */ 118210012d53SJohn Baldwin for (cio->num_matches = 0, i = 0, 11838983cfbfSMike Smith dinfo = STAILQ_FIRST(devlist_head); 118410012d53SJohn Baldwin dinfo != NULL; 11858983cfbfSMike Smith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 11868983cfbfSMike Smith if (i < cio->offset) 11878983cfbfSMike Smith continue; 11888983cfbfSMike Smith 11898983cfbfSMike Smith /* Populate pd_name and pd_unit */ 11908983cfbfSMike Smith name = NULL; 11910678f786SJohn Baldwin if (dinfo->cfg.dev) 11928983cfbfSMike Smith name = device_get_name(dinfo->cfg.dev); 11938983cfbfSMike Smith if (name) { 11948983cfbfSMike Smith strncpy(dinfo->conf.pd_name, name, 11958983cfbfSMike Smith sizeof(dinfo->conf.pd_name)); 11968983cfbfSMike Smith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 11978983cfbfSMike Smith dinfo->conf.pd_unit = 11988983cfbfSMike Smith device_get_unit(dinfo->cfg.dev); 11990678f786SJohn Baldwin } else { 12000678f786SJohn Baldwin dinfo->conf.pd_name[0] = '\0'; 12010678f786SJohn Baldwin dinfo->conf.pd_unit = 0; 12028983cfbfSMike Smith } 12038983cfbfSMike Smith 120433d3fffaSMarius Strobl if (pattern_buf == NULL || 1205e9ed3a70SBrooks Davis pci_conf_match(cmd, pattern_buf, num_patterns, 120633d3fffaSMarius Strobl &dinfo->conf) == 0) { 12078983cfbfSMike Smith /* 12088983cfbfSMike Smith * If we've filled up the user's buffer, 12098983cfbfSMike Smith * break out at this point. Since we've 12108983cfbfSMike Smith * got a match here, we'll pick right back 12118983cfbfSMike Smith * up at the matching entry. We can also 12128983cfbfSMike Smith * tell the user that there are more matches 12138983cfbfSMike Smith * left. 12148983cfbfSMike Smith */ 1215fadd3f8aSConrad Meyer if (cio->num_matches >= ionum) { 1216fadd3f8aSConrad Meyer error = 0; 12178983cfbfSMike Smith break; 1218fadd3f8aSConrad Meyer } 12198983cfbfSMike Smith 1220e9ed3a70SBrooks Davis pci_conf_for_copyout(&dinfo->conf, &pcu, cmd); 1221e9ed3a70SBrooks Davis error = copyout(&pcu, 1222c5860546SMarius Strobl (caddr_t)cio->matches + 122310012d53SJohn Baldwin confsz * cio->num_matches, confsz); 122410012d53SJohn Baldwin if (error) 122510012d53SJohn Baldwin break; 122633d3fffaSMarius Strobl cio->num_matches++; 12278983cfbfSMike Smith } 1228d08239c1SJohn-Mark Gurney } 12298983cfbfSMike Smith 12308983cfbfSMike Smith /* 12318983cfbfSMike Smith * Set the pointer into the list, so if the user is getting 12328983cfbfSMike Smith * n records at a time, where n < pci_numdevs, 12338983cfbfSMike Smith */ 12348983cfbfSMike Smith cio->offset = i; 12358983cfbfSMike Smith 12368983cfbfSMike Smith /* 12378983cfbfSMike Smith * Set the generation, the user will need this if they make 12388983cfbfSMike Smith * another ioctl call with offset != 0. 12398983cfbfSMike Smith */ 12408983cfbfSMike Smith cio->generation = pci_generation; 12418983cfbfSMike Smith 12428983cfbfSMike Smith /* 12438983cfbfSMike Smith * If this is the last device, inform the user so he won't 12448983cfbfSMike Smith * bother asking for more devices. If dinfo isn't NULL, we 12458983cfbfSMike Smith * know that there are more matches in the list because of 12468983cfbfSMike Smith * the way the traversal is done. 12478983cfbfSMike Smith */ 12488983cfbfSMike Smith if (dinfo == NULL) 12498983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 12508983cfbfSMike Smith else 12518983cfbfSMike Smith cio->status = PCI_GETCONF_MORE_DEVS; 12528983cfbfSMike Smith 1253d08239c1SJohn-Mark Gurney getconfexit: 1254e9ed3a70SBrooks Davis pci_conf_io_update_data(cio, data, cmd); 1255b01bf72bSMaxim Sobolev free(cio, M_TEMP); 1256904c3909SGleb Smirnoff free(pattern_buf, M_TEMP); 12578983cfbfSMike Smith 12588983cfbfSMike Smith break; 12598983cfbfSMike Smith 1260b2068c0cSWarner Losh #ifdef PRE7_COMPAT 1261*56ecc8a9SWarner Losh case PCIOCREAD_FREEBSD6: 1262*56ecc8a9SWarner Losh case PCIOCWRITE_FREEBSD6: 1263*56ecc8a9SWarner Losh io_freebsd6 = (struct pci_io_freebsd6 *)data; 126433d3fffaSMarius Strobl iodata.pi_sel.pc_domain = 0; 1265*56ecc8a9SWarner Losh iodata.pi_sel.pc_bus = io_freebsd6->pi_sel.pc_bus; 1266*56ecc8a9SWarner Losh iodata.pi_sel.pc_dev = io_freebsd6->pi_sel.pc_dev; 1267*56ecc8a9SWarner Losh iodata.pi_sel.pc_func = io_freebsd6->pi_sel.pc_func; 1268*56ecc8a9SWarner Losh iodata.pi_reg = io_freebsd6->pi_reg; 1269*56ecc8a9SWarner Losh iodata.pi_width = io_freebsd6->pi_width; 1270*56ecc8a9SWarner Losh iodata.pi_data = io_freebsd6->pi_data; 127133d3fffaSMarius Strobl data = (caddr_t)&iodata; 127233d3fffaSMarius Strobl /* FALLTHROUGH */ 127333d3fffaSMarius Strobl #endif 127466f314b5SStefan Eßer case PCIOCREAD: 12758983cfbfSMike Smith case PCIOCWRITE: 12768983cfbfSMike Smith io = (struct pci_io *)data; 12778983cfbfSMike Smith switch(io->pi_width) { 12788983cfbfSMike Smith case 4: 12798983cfbfSMike Smith case 2: 12808983cfbfSMike Smith case 1: 1281d16d35fdSAndriy Gapon /* Make sure register is not negative and aligned. */ 128233d3fffaSMarius Strobl if (io->pi_reg < 0 || 128333d3fffaSMarius Strobl io->pi_reg & (io->pi_width - 1)) { 128466f314b5SStefan Eßer error = EINVAL; 128540ed3f47SRuslan Ermilov break; 128640ed3f47SRuslan Ermilov } 12878983cfbfSMike Smith /* 12888983cfbfSMike Smith * Assume that the user-level bus number is 128937ce43b7SBruce M Simpson * in fact the physical PCI bus number. 129037ce43b7SBruce M Simpson * Look up the grandparent, i.e. the bridge device, 129137ce43b7SBruce M Simpson * so that we can issue configuration space cycles. 12928983cfbfSMike Smith */ 129355aaf894SMarius Strobl pcidev = pci_find_dbsf(io->pi_sel.pc_domain, 129455aaf894SMarius Strobl io->pi_sel.pc_bus, io->pi_sel.pc_dev, 129555aaf894SMarius Strobl io->pi_sel.pc_func); 129637ce43b7SBruce M Simpson if (pcidev) { 1297b2068c0cSWarner Losh #ifdef PRE7_COMPAT 1298*56ecc8a9SWarner Losh if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_FREEBSD6) 129933d3fffaSMarius Strobl #else 130066f314b5SStefan Eßer if (cmd == PCIOCWRITE) 130133d3fffaSMarius Strobl #endif 13025060ec97SRyan Stone pci_write_config(pcidev, 13038983cfbfSMike Smith io->pi_reg, 13048983cfbfSMike Smith io->pi_data, 13058983cfbfSMike Smith io->pi_width); 1306b2068c0cSWarner Losh #ifdef PRE7_COMPAT 1307*56ecc8a9SWarner Losh else if (cmd == PCIOCREAD_FREEBSD6) 1308*56ecc8a9SWarner Losh io_freebsd6->pi_data = 13095060ec97SRyan Stone pci_read_config(pcidev, 131033d3fffaSMarius Strobl io->pi_reg, 131133d3fffaSMarius Strobl io->pi_width); 131233d3fffaSMarius Strobl #endif 131366f314b5SStefan Eßer else 131466f314b5SStefan Eßer io->pi_data = 13155060ec97SRyan Stone pci_read_config(pcidev, 131666f314b5SStefan Eßer io->pi_reg, 131766f314b5SStefan Eßer io->pi_width); 13188983cfbfSMike Smith error = 0; 13198983cfbfSMike Smith } else { 13208910aa92SPaul Saab #ifdef COMPAT_FREEBSD4 1321*56ecc8a9SWarner Losh if (cmd == PCIOCREAD_FREEBSD6) { 1322*56ecc8a9SWarner Losh io_freebsd6->pi_data = -1; 13238910aa92SPaul Saab error = 0; 13248910aa92SPaul Saab } else 13258910aa92SPaul Saab #endif 13268983cfbfSMike Smith error = ENODEV; 13278983cfbfSMike Smith } 13288983cfbfSMike Smith break; 13298983cfbfSMike Smith default: 1330d08239c1SJohn-Mark Gurney error = EINVAL; 13318983cfbfSMike Smith break; 13328983cfbfSMike Smith } 13338983cfbfSMike Smith break; 13348983cfbfSMike Smith 1335da1e0915SJohn Baldwin case PCIOCGETBAR: 1336da1e0915SJohn Baldwin bio = (struct pci_bar_io *)data; 1337da1e0915SJohn Baldwin 1338da1e0915SJohn Baldwin /* 1339da1e0915SJohn Baldwin * Assume that the user-level bus number is 1340da1e0915SJohn Baldwin * in fact the physical PCI bus number. 1341da1e0915SJohn Baldwin */ 1342da1e0915SJohn Baldwin pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, 1343da1e0915SJohn Baldwin bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, 1344da1e0915SJohn Baldwin bio->pbi_sel.pc_func); 1345da1e0915SJohn Baldwin if (pcidev == NULL) { 1346da1e0915SJohn Baldwin error = ENODEV; 1347da1e0915SJohn Baldwin break; 1348da1e0915SJohn Baldwin } 1349a90dd577SJohn Baldwin pm = pci_find_bar(pcidev, bio->pbi_reg); 1350a90dd577SJohn Baldwin if (pm == NULL) { 1351da1e0915SJohn Baldwin error = EINVAL; 1352da1e0915SJohn Baldwin break; 1353da1e0915SJohn Baldwin } 1354a90dd577SJohn Baldwin bio->pbi_base = pm->pm_value; 1355a90dd577SJohn Baldwin bio->pbi_length = (pci_addr_t)1 << pm->pm_size; 1356a90dd577SJohn Baldwin bio->pbi_enabled = pci_bar_enabled(pcidev, pm); 1357da1e0915SJohn Baldwin error = 0; 1358da1e0915SJohn Baldwin break; 1359762aad81SNeel Natu case PCIOCATTACHED: 1360762aad81SNeel Natu error = 0; 1361762aad81SNeel Natu io = (struct pci_io *)data; 1362762aad81SNeel Natu pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, 1363762aad81SNeel Natu io->pi_sel.pc_dev, io->pi_sel.pc_func); 1364762aad81SNeel Natu if (pcidev != NULL) 1365762aad81SNeel Natu io->pi_data = device_is_attached(pcidev); 1366762aad81SNeel Natu else 1367762aad81SNeel Natu error = ENODEV; 1368762aad81SNeel Natu break; 136984b755dfSJohn Baldwin case PCIOCLISTVPD: 137084b755dfSJohn Baldwin lvio = (struct pci_list_vpd_io *)data; 137184b755dfSJohn Baldwin 137284b755dfSJohn Baldwin /* 137384b755dfSJohn Baldwin * Assume that the user-level bus number is 137484b755dfSJohn Baldwin * in fact the physical PCI bus number. 137584b755dfSJohn Baldwin */ 137684b755dfSJohn Baldwin pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain, 137784b755dfSJohn Baldwin lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev, 137884b755dfSJohn Baldwin lvio->plvi_sel.pc_func); 137984b755dfSJohn Baldwin if (pcidev == NULL) { 138084b755dfSJohn Baldwin error = ENODEV; 138184b755dfSJohn Baldwin break; 138284b755dfSJohn Baldwin } 138384b755dfSJohn Baldwin error = pci_list_vpd(pcidev, lvio); 138484b755dfSJohn Baldwin break; 138587842989SKonstantin Belousov 138687842989SKonstantin Belousov case PCIOCBARMMAP: 138787842989SKonstantin Belousov pbm = (struct pci_bar_mmap *)data; 138887842989SKonstantin Belousov if ((flag & FWRITE) == 0 && 138996b506a5SWarner Losh (pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) { 139096b506a5SWarner Losh error = EPERM; 139196b506a5SWarner Losh break; 139296b506a5SWarner Losh } 139387842989SKonstantin Belousov pcidev = pci_find_dbsf(pbm->pbm_sel.pc_domain, 139487842989SKonstantin Belousov pbm->pbm_sel.pc_bus, pbm->pbm_sel.pc_dev, 139587842989SKonstantin Belousov pbm->pbm_sel.pc_func); 139687842989SKonstantin Belousov error = pcidev == NULL ? ENODEV : pci_bar_mmap(pcidev, pbm); 139787842989SKonstantin Belousov break; 139887842989SKonstantin Belousov 13997e14be0bSMark Johnston case PCIOCBARIO: 14007e14be0bSMark Johnston pbi = (struct pci_bar_ioreq *)data; 14017e14be0bSMark Johnston 14027e14be0bSMark Johnston pcidev = pci_find_dbsf(pbi->pbi_sel.pc_domain, 14037e14be0bSMark Johnston pbi->pbi_sel.pc_bus, pbi->pbi_sel.pc_dev, 14047e14be0bSMark Johnston pbi->pbi_sel.pc_func); 14057e14be0bSMark Johnston if (pcidev == NULL) { 14067e14be0bSMark Johnston error = ENODEV; 14077e14be0bSMark Johnston break; 14087e14be0bSMark Johnston } 14097e14be0bSMark Johnston error = pci_bar_io(pcidev, pbi); 14107e14be0bSMark Johnston break; 14117e14be0bSMark Johnston 14128983cfbfSMike Smith default: 14138983cfbfSMike Smith error = ENOTTY; 14148983cfbfSMike Smith break; 14158983cfbfSMike Smith } 14168983cfbfSMike Smith 1417c6df6f53SWarner Losh bus_topo_unlock(); 141896b506a5SWarner Losh 14198983cfbfSMike Smith return (error); 14208983cfbfSMike Smith } 1421