xref: /freebsd/sys/dev/pci/pci_user.c (revision fadd3f8a66faf682d83a1e0e45c03ba3b21155ac)
1aad970f1SDavid E. O'Brien /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
48983cfbfSMike Smith  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
58983cfbfSMike Smith  * All rights reserved.
68983cfbfSMike Smith  *
78983cfbfSMike Smith  * Redistribution and use in source and binary forms, with or without
88983cfbfSMike Smith  * modification, are permitted provided that the following conditions
98983cfbfSMike Smith  * are met:
108983cfbfSMike Smith  * 1. Redistributions of source code must retain the above copyright
118983cfbfSMike Smith  *    notice unmodified, this list of conditions, and the following
128983cfbfSMike Smith  *    disclaimer.
138983cfbfSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
148983cfbfSMike Smith  *    notice, this list of conditions and the following disclaimer in the
158983cfbfSMike Smith  *    documentation and/or other materials provided with the distribution.
168983cfbfSMike Smith  *
178983cfbfSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
188983cfbfSMike Smith  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
198983cfbfSMike Smith  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
208983cfbfSMike Smith  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
218983cfbfSMike Smith  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
228983cfbfSMike Smith  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
238983cfbfSMike Smith  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
248983cfbfSMike Smith  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
258983cfbfSMike Smith  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
268983cfbfSMike Smith  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
278983cfbfSMike Smith  */
288983cfbfSMike Smith 
29aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
30aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
31aad970f1SDavid E. O'Brien 
328983cfbfSMike Smith #include "opt_bus.h"	/* XXX trim includes */
338910aa92SPaul Saab #include "opt_compat.h"
348983cfbfSMike Smith 
358983cfbfSMike Smith #include <sys/param.h>
368983cfbfSMike Smith #include <sys/systm.h>
378983cfbfSMike Smith #include <sys/malloc.h>
388983cfbfSMike Smith #include <sys/module.h>
398983cfbfSMike Smith #include <sys/linker.h>
408983cfbfSMike Smith #include <sys/fcntl.h>
418983cfbfSMike Smith #include <sys/conf.h>
428983cfbfSMike Smith #include <sys/kernel.h>
438002488bSRobert Watson #include <sys/proc.h>
448983cfbfSMike Smith #include <sys/queue.h>
458983cfbfSMike Smith #include <sys/types.h>
468983cfbfSMike Smith 
478983cfbfSMike Smith #include <vm/vm.h>
488983cfbfSMike Smith #include <vm/pmap.h>
498983cfbfSMike Smith #include <vm/vm_extern.h>
508983cfbfSMike Smith 
518983cfbfSMike Smith #include <sys/bus.h>
528983cfbfSMike Smith #include <machine/bus.h>
538983cfbfSMike Smith #include <sys/rman.h>
548983cfbfSMike Smith #include <machine/resource.h>
558983cfbfSMike Smith 
568983cfbfSMike Smith #include <sys/pciio.h>
5738d8c994SWarner Losh #include <dev/pci/pcireg.h>
5838d8c994SWarner Losh #include <dev/pci/pcivar.h>
598983cfbfSMike Smith 
608983cfbfSMike Smith #include "pcib_if.h"
618983cfbfSMike Smith #include "pci_if.h"
628983cfbfSMike Smith 
638983cfbfSMike Smith /*
648983cfbfSMike Smith  * This is the user interface to PCI configuration space.
658983cfbfSMike Smith  */
668983cfbfSMike Smith 
67b40ce416SJulian Elischer static d_open_t 	pci_open;
68b40ce416SJulian Elischer static d_close_t	pci_close;
698983cfbfSMike Smith static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
708983cfbfSMike Smith 			       struct pci_conf *match_buf);
71b40ce416SJulian Elischer static d_ioctl_t	pci_ioctl;
728983cfbfSMike Smith 
738983cfbfSMike Smith struct cdevsw pcicdev = {
74dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
75dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
767ac40f5fSPoul-Henning Kamp 	.d_open =	pci_open,
777ac40f5fSPoul-Henning Kamp 	.d_close =	pci_close,
787ac40f5fSPoul-Henning Kamp 	.d_ioctl =	pci_ioctl,
797ac40f5fSPoul-Henning Kamp 	.d_name =	"pci",
808983cfbfSMike Smith };
818983cfbfSMike Smith 
828983cfbfSMike Smith static int
8389c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
848983cfbfSMike Smith {
858002488bSRobert Watson 	int error;
868002488bSRobert Watson 
878002488bSRobert Watson 	if (oflags & FWRITE) {
88a854ed98SJohn Baldwin 		error = securelevel_gt(td->td_ucred, 0);
898002488bSRobert Watson 		if (error)
908002488bSRobert Watson 			return (error);
918983cfbfSMike Smith 	}
928002488bSRobert Watson 
938002488bSRobert Watson 	return (0);
948983cfbfSMike Smith }
958983cfbfSMike Smith 
968983cfbfSMike Smith static int
9789c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
988983cfbfSMike Smith {
998983cfbfSMike Smith 	return 0;
1008983cfbfSMike Smith }
1018983cfbfSMike Smith 
1028983cfbfSMike Smith /*
1038983cfbfSMike Smith  * Match a single pci_conf structure against an array of pci_match_conf
1048983cfbfSMike Smith  * structures.  The first argument, 'matches', is an array of num_matches
1058983cfbfSMike Smith  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
1068983cfbfSMike Smith  * structure that will be compared to every entry in the matches array.
1078983cfbfSMike Smith  * This function returns 1 on failure, 0 on success.
1088983cfbfSMike Smith  */
1098983cfbfSMike Smith static int
1108983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches,
1118983cfbfSMike Smith 	       struct pci_conf *match_buf)
1128983cfbfSMike Smith {
1138983cfbfSMike Smith 	int i;
1148983cfbfSMike Smith 
1158983cfbfSMike Smith 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
1168983cfbfSMike Smith 		return(1);
1178983cfbfSMike Smith 
1188983cfbfSMike Smith 	for (i = 0; i < num_matches; i++) {
1198983cfbfSMike Smith 		/*
1208983cfbfSMike Smith 		 * I'm not sure why someone would do this...but...
1218983cfbfSMike Smith 		 */
1228983cfbfSMike Smith 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
1238983cfbfSMike Smith 			continue;
1248983cfbfSMike Smith 
1258983cfbfSMike Smith 		/*
1268983cfbfSMike Smith 		 * Look at each of the match flags.  If it's set, do the
1278983cfbfSMike Smith 		 * comparison.  If the comparison fails, we don't have a
1288983cfbfSMike Smith 		 * match, go on to the next item if there is one.
1298983cfbfSMike Smith 		 */
13055aaf894SMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
13155aaf894SMarius Strobl 		 && (match_buf->pc_sel.pc_domain !=
13255aaf894SMarius Strobl 		 matches[i].pc_sel.pc_domain))
13355aaf894SMarius Strobl 			continue;
13455aaf894SMarius Strobl 
1358983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
1368983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
1378983cfbfSMike Smith 			continue;
1388983cfbfSMike Smith 
1398983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
1408983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
1418983cfbfSMike Smith 			continue;
1428983cfbfSMike Smith 
1438983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
1448983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
1458983cfbfSMike Smith 			continue;
1468983cfbfSMike Smith 
1478983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
1488983cfbfSMike Smith 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
1498983cfbfSMike Smith 			continue;
1508983cfbfSMike Smith 
1518983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
1528983cfbfSMike Smith 		 && (match_buf->pc_device != matches[i].pc_device))
1538983cfbfSMike Smith 			continue;
1548983cfbfSMike Smith 
1558983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
1568983cfbfSMike Smith 		 && (match_buf->pc_class != matches[i].pc_class))
1578983cfbfSMike Smith 			continue;
1588983cfbfSMike Smith 
1598983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
1608983cfbfSMike Smith 		 && (match_buf->pd_unit != matches[i].pd_unit))
1618983cfbfSMike Smith 			continue;
1628983cfbfSMike Smith 
1638983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
1648983cfbfSMike Smith 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
1658983cfbfSMike Smith 			     sizeof(match_buf->pd_name)) != 0))
1668983cfbfSMike Smith 			continue;
1678983cfbfSMike Smith 
1688983cfbfSMike Smith 		return(0);
1698983cfbfSMike Smith 	}
1708983cfbfSMike Smith 
1718983cfbfSMike Smith 	return(1);
1728983cfbfSMike Smith }
1738983cfbfSMike Smith 
17433d3fffaSMarius Strobl #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
17533d3fffaSMarius Strobl     defined(COMPAT_FREEBSD6)
176b2068c0cSWarner Losh #define PRE7_COMPAT
17733d3fffaSMarius Strobl 
17833d3fffaSMarius Strobl typedef enum {
17933d3fffaSMarius Strobl 	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
18033d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
18133d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
18233d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
18333d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
18433d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
18533d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
18633d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
18733d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
18833d3fffaSMarius Strobl } pci_getconf_flags_old;
18933d3fffaSMarius Strobl 
19033d3fffaSMarius Strobl struct pcisel_old {
19133d3fffaSMarius Strobl 	u_int8_t	pc_bus;		/* bus number */
19233d3fffaSMarius Strobl 	u_int8_t	pc_dev;		/* device on this bus */
19333d3fffaSMarius Strobl 	u_int8_t	pc_func;	/* function on this device */
19433d3fffaSMarius Strobl };
19533d3fffaSMarius Strobl 
19633d3fffaSMarius Strobl struct pci_conf_old {
19733d3fffaSMarius Strobl 	struct pcisel_old pc_sel;	/* bus+slot+function */
19833d3fffaSMarius Strobl 	u_int8_t	pc_hdr;		/* PCI header type */
19933d3fffaSMarius Strobl 	u_int16_t	pc_subvendor;	/* card vendor ID */
20033d3fffaSMarius Strobl 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
20133d3fffaSMarius Strobl 					   card vendor */
20233d3fffaSMarius Strobl 	u_int16_t	pc_vendor;	/* chip vendor ID */
20333d3fffaSMarius Strobl 	u_int16_t	pc_device;	/* chip device ID, assigned by
20433d3fffaSMarius Strobl 					   chip vendor */
20533d3fffaSMarius Strobl 	u_int8_t	pc_class;	/* chip PCI class */
20633d3fffaSMarius Strobl 	u_int8_t	pc_subclass;	/* chip PCI subclass */
20733d3fffaSMarius Strobl 	u_int8_t	pc_progif;	/* chip PCI programming interface */
20833d3fffaSMarius Strobl 	u_int8_t	pc_revid;	/* chip revision ID */
20933d3fffaSMarius Strobl 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
21033d3fffaSMarius Strobl 	u_long		pd_unit;	/* device unit number */
21133d3fffaSMarius Strobl };
21233d3fffaSMarius Strobl 
21333d3fffaSMarius Strobl struct pci_match_conf_old {
21433d3fffaSMarius Strobl 	struct pcisel_old	pc_sel;		/* bus+slot+function */
21533d3fffaSMarius Strobl 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
21633d3fffaSMarius Strobl 	u_long			pd_unit;	/* Unit number */
21733d3fffaSMarius Strobl 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
21833d3fffaSMarius Strobl 	u_int16_t		pc_device;	/* PCI Device ID */
21933d3fffaSMarius Strobl 	u_int8_t		pc_class;	/* PCI class */
220c5860546SMarius Strobl 	pci_getconf_flags_old	flags;		/* Matching expression */
22133d3fffaSMarius Strobl };
22233d3fffaSMarius Strobl 
22333d3fffaSMarius Strobl struct pci_io_old {
22433d3fffaSMarius Strobl 	struct pcisel_old pi_sel;	/* device to operate on */
22533d3fffaSMarius Strobl 	int		pi_reg;		/* configuration register to examine */
22633d3fffaSMarius Strobl 	int		pi_width;	/* width (in bytes) of read or write */
22733d3fffaSMarius Strobl 	u_int32_t	pi_data;	/* data to write or result of read */
22833d3fffaSMarius Strobl };
22933d3fffaSMarius Strobl 
230b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
231b01bf72bSMaxim Sobolev struct pci_conf_old32 {
232b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
233e5280830SGleb Smirnoff 	uint8_t		pc_hdr;		/* PCI header type */
234e5280830SGleb Smirnoff 	uint16_t	pc_subvendor;	/* card vendor ID */
235e5280830SGleb Smirnoff 	uint16_t	pc_subdevice;	/* card device ID, assigned by
236b01bf72bSMaxim Sobolev 					   card vendor */
237e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* chip vendor ID */
238e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* chip device ID, assigned by
239b01bf72bSMaxim Sobolev 					   chip vendor */
240e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* chip PCI class */
241e5280830SGleb Smirnoff 	uint8_t		pc_subclass;	/* chip PCI subclass */
242e5280830SGleb Smirnoff 	uint8_t		pc_progif;	/* chip PCI programming interface */
243e5280830SGleb Smirnoff 	uint8_t		pc_revid;	/* chip revision ID */
244b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
245e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* device unit number (u_long) */
246b01bf72bSMaxim Sobolev };
247b01bf72bSMaxim Sobolev 
248b01bf72bSMaxim Sobolev struct pci_match_conf_old32 {
249b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
250b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
251e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* Unit number (u_long) */
252e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* PCI Vendor ID */
253e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* PCI Device ID */
254e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* PCI class */
255b01bf72bSMaxim Sobolev 	pci_getconf_flags_old flags;	/* Matching expression */
256b01bf72bSMaxim Sobolev };
257b01bf72bSMaxim Sobolev 
258b01bf72bSMaxim Sobolev struct pci_conf_io32 {
259e5280830SGleb Smirnoff 	uint32_t	pat_buf_len;	/* pattern buffer length */
260e5280830SGleb Smirnoff 	uint32_t	num_patterns;	/* number of patterns */
261e5280830SGleb Smirnoff 	uint32_t	patterns;	/* pattern buffer
262e5280830SGleb Smirnoff 					   (struct pci_match_conf_old32 *) */
263e5280830SGleb Smirnoff 	uint32_t	match_buf_len;	/* match buffer length */
264e5280830SGleb Smirnoff 	uint32_t	num_matches;	/* number of matches returned */
265e5280830SGleb Smirnoff 	uint32_t	matches;	/* match buffer
266e5280830SGleb Smirnoff 					   (struct pci_conf_old32 *) */
267e5280830SGleb Smirnoff 	uint32_t	offset;		/* offset into device list */
268e5280830SGleb Smirnoff 	uint32_t	generation;	/* device list generation */
269b01bf72bSMaxim Sobolev 	pci_getconf_status status;	/* request status */
270b01bf72bSMaxim Sobolev };
271b01bf72bSMaxim Sobolev 
272b01bf72bSMaxim Sobolev #define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
273904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
274b01bf72bSMaxim Sobolev 
27533d3fffaSMarius Strobl #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
27633d3fffaSMarius Strobl #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
27733d3fffaSMarius Strobl #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
27833d3fffaSMarius Strobl 
27933d3fffaSMarius Strobl static int	pci_conf_match_old(struct pci_match_conf_old *matches,
28033d3fffaSMarius Strobl 		    int num_matches, struct pci_conf *match_buf);
28133d3fffaSMarius Strobl 
28233d3fffaSMarius Strobl static int
28333d3fffaSMarius Strobl pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
28433d3fffaSMarius Strobl     struct pci_conf *match_buf)
28533d3fffaSMarius Strobl {
28633d3fffaSMarius Strobl 	int i;
28733d3fffaSMarius Strobl 
28833d3fffaSMarius Strobl 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
28933d3fffaSMarius Strobl 		return(1);
29033d3fffaSMarius Strobl 
29133d3fffaSMarius Strobl 	for (i = 0; i < num_matches; i++) {
29233d3fffaSMarius Strobl 		if (match_buf->pc_sel.pc_domain != 0)
29333d3fffaSMarius Strobl 			continue;
29433d3fffaSMarius Strobl 
29533d3fffaSMarius Strobl 		/*
29633d3fffaSMarius Strobl 		 * I'm not sure why someone would do this...but...
29733d3fffaSMarius Strobl 		 */
29833d3fffaSMarius Strobl 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
29933d3fffaSMarius Strobl 			continue;
30033d3fffaSMarius Strobl 
30133d3fffaSMarius Strobl 		/*
30233d3fffaSMarius Strobl 		 * Look at each of the match flags.  If it's set, do the
30333d3fffaSMarius Strobl 		 * comparison.  If the comparison fails, we don't have a
30433d3fffaSMarius Strobl 		 * match, go on to the next item if there is one.
30533d3fffaSMarius Strobl 		 */
30633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
30733d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
30833d3fffaSMarius Strobl 			continue;
30933d3fffaSMarius Strobl 
31033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
31133d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
31233d3fffaSMarius Strobl 			continue;
31333d3fffaSMarius Strobl 
31433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
31533d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
31633d3fffaSMarius Strobl 			continue;
31733d3fffaSMarius Strobl 
31833d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
31933d3fffaSMarius Strobl 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
32033d3fffaSMarius Strobl 			continue;
32133d3fffaSMarius Strobl 
32233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
32333d3fffaSMarius Strobl 		 && (match_buf->pc_device != matches[i].pc_device))
32433d3fffaSMarius Strobl 			continue;
32533d3fffaSMarius Strobl 
32633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
32733d3fffaSMarius Strobl 		 && (match_buf->pc_class != matches[i].pc_class))
32833d3fffaSMarius Strobl 			continue;
32933d3fffaSMarius Strobl 
33033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
33133d3fffaSMarius Strobl 		 && (match_buf->pd_unit != matches[i].pd_unit))
33233d3fffaSMarius Strobl 			continue;
33333d3fffaSMarius Strobl 
33433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
33533d3fffaSMarius Strobl 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
33633d3fffaSMarius Strobl 			     sizeof(match_buf->pd_name)) != 0))
33733d3fffaSMarius Strobl 			continue;
33833d3fffaSMarius Strobl 
33933d3fffaSMarius Strobl 		return(0);
34033d3fffaSMarius Strobl 	}
34133d3fffaSMarius Strobl 
34233d3fffaSMarius Strobl 	return(1);
34333d3fffaSMarius Strobl }
34433d3fffaSMarius Strobl 
345904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
346b01bf72bSMaxim Sobolev static int
347b01bf72bSMaxim Sobolev pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
348b01bf72bSMaxim Sobolev     struct pci_conf *match_buf)
349b01bf72bSMaxim Sobolev {
350b01bf72bSMaxim Sobolev 	int i;
351b01bf72bSMaxim Sobolev 
352b01bf72bSMaxim Sobolev 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
353b01bf72bSMaxim Sobolev 		return(1);
354b01bf72bSMaxim Sobolev 
355b01bf72bSMaxim Sobolev 	for (i = 0; i < num_matches; i++) {
356b01bf72bSMaxim Sobolev 		if (match_buf->pc_sel.pc_domain != 0)
357b01bf72bSMaxim Sobolev 			continue;
358b01bf72bSMaxim Sobolev 
359b01bf72bSMaxim Sobolev 		/*
360b01bf72bSMaxim Sobolev 		 * I'm not sure why someone would do this...but...
361b01bf72bSMaxim Sobolev 		 */
362b01bf72bSMaxim Sobolev 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
363b01bf72bSMaxim Sobolev 			continue;
364b01bf72bSMaxim Sobolev 
365b01bf72bSMaxim Sobolev 		/*
366b01bf72bSMaxim Sobolev 		 * Look at each of the match flags.  If it's set, do the
367b01bf72bSMaxim Sobolev 		 * comparison.  If the comparison fails, we don't have a
368b01bf72bSMaxim Sobolev 		 * match, go on to the next item if there is one.
369b01bf72bSMaxim Sobolev 		 */
370e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
371e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
372b01bf72bSMaxim Sobolev 			continue;
373b01bf72bSMaxim Sobolev 
374e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
375e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
376b01bf72bSMaxim Sobolev 			continue;
377b01bf72bSMaxim Sobolev 
378e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
379e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
380b01bf72bSMaxim Sobolev 			continue;
381b01bf72bSMaxim Sobolev 
382e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
383e5280830SGleb Smirnoff 		    (match_buf->pc_vendor != matches[i].pc_vendor))
384b01bf72bSMaxim Sobolev 			continue;
385b01bf72bSMaxim Sobolev 
386e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
387e5280830SGleb Smirnoff 		    (match_buf->pc_device != matches[i].pc_device))
388b01bf72bSMaxim Sobolev 			continue;
389b01bf72bSMaxim Sobolev 
390e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
391e5280830SGleb Smirnoff 		    (match_buf->pc_class != matches[i].pc_class))
392b01bf72bSMaxim Sobolev 			continue;
393b01bf72bSMaxim Sobolev 
394e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
395e5280830SGleb Smirnoff 		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
396b01bf72bSMaxim Sobolev 			continue;
397b01bf72bSMaxim Sobolev 
398e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
399e5280830SGleb Smirnoff 		    (strncmp(matches[i].pd_name, match_buf->pd_name,
400b01bf72bSMaxim Sobolev 		    sizeof(match_buf->pd_name)) != 0))
401b01bf72bSMaxim Sobolev 			continue;
402b01bf72bSMaxim Sobolev 
403b01bf72bSMaxim Sobolev 		return (0);
404b01bf72bSMaxim Sobolev 	}
405b01bf72bSMaxim Sobolev 
406b01bf72bSMaxim Sobolev 	return (1);
407b01bf72bSMaxim Sobolev }
408904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
409904c3909SGleb Smirnoff #endif	/* PRE7_COMPAT */
41033d3fffaSMarius Strobl 
4118983cfbfSMike Smith static int
41284b755dfSJohn Baldwin pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
41384b755dfSJohn Baldwin {
41484b755dfSJohn Baldwin 	struct pci_vpd_element vpd_element, *vpd_user;
41584b755dfSJohn Baldwin 	struct pcicfg_vpd *vpd;
41684b755dfSJohn Baldwin 	size_t len;
41784b755dfSJohn Baldwin 	int error, i;
41884b755dfSJohn Baldwin 
41984b755dfSJohn Baldwin 	vpd = pci_fetch_vpd_list(dev);
42084b755dfSJohn Baldwin 	if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL)
42184b755dfSJohn Baldwin 		return (ENXIO);
42284b755dfSJohn Baldwin 
42384b755dfSJohn Baldwin 	/*
42484b755dfSJohn Baldwin 	 * Calculate the amount of space needed in the data buffer.  An
42584b755dfSJohn Baldwin 	 * identifier element is always present followed by the read-only
42684b755dfSJohn Baldwin 	 * and read-write keywords.
42784b755dfSJohn Baldwin 	 */
42884b755dfSJohn Baldwin 	len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident);
42984b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_rocnt; i++)
43084b755dfSJohn Baldwin 		len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len;
43184b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_wcnt; i++)
43284b755dfSJohn Baldwin 		len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len;
43384b755dfSJohn Baldwin 
43484b755dfSJohn Baldwin 	if (lvio->plvi_len == 0) {
43584b755dfSJohn Baldwin 		lvio->plvi_len = len;
43684b755dfSJohn Baldwin 		return (0);
43784b755dfSJohn Baldwin 	}
43884b755dfSJohn Baldwin 	if (lvio->plvi_len < len) {
43984b755dfSJohn Baldwin 		lvio->plvi_len = len;
44084b755dfSJohn Baldwin 		return (ENOMEM);
44184b755dfSJohn Baldwin 	}
44284b755dfSJohn Baldwin 
44384b755dfSJohn Baldwin 	/*
44484b755dfSJohn Baldwin 	 * Copyout the identifier string followed by each keyword and
44584b755dfSJohn Baldwin 	 * value.
44684b755dfSJohn Baldwin 	 */
44784b755dfSJohn Baldwin 	vpd_user = lvio->plvi_data;
44884b755dfSJohn Baldwin 	vpd_element.pve_keyword[0] = '\0';
44984b755dfSJohn Baldwin 	vpd_element.pve_keyword[1] = '\0';
45084b755dfSJohn Baldwin 	vpd_element.pve_flags = PVE_FLAG_IDENT;
45184b755dfSJohn Baldwin 	vpd_element.pve_datalen = strlen(vpd->vpd_ident);
45284b755dfSJohn Baldwin 	error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
45384b755dfSJohn Baldwin 	if (error)
45484b755dfSJohn Baldwin 		return (error);
45584b755dfSJohn Baldwin 	error = copyout(vpd->vpd_ident, vpd_user->pve_data,
45684b755dfSJohn Baldwin 	    strlen(vpd->vpd_ident));
45784b755dfSJohn Baldwin 	if (error)
45884b755dfSJohn Baldwin 		return (error);
45984b755dfSJohn Baldwin 	vpd_user = PVE_NEXT(vpd_user);
46084b755dfSJohn Baldwin 	vpd_element.pve_flags = 0;
46184b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_rocnt; i++) {
46284b755dfSJohn Baldwin 		vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
46384b755dfSJohn Baldwin 		vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1];
46484b755dfSJohn Baldwin 		vpd_element.pve_datalen = vpd->vpd_ros[i].len;
46584b755dfSJohn Baldwin 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
46684b755dfSJohn Baldwin 		if (error)
46784b755dfSJohn Baldwin 			return (error);
46884b755dfSJohn Baldwin 		error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data,
46984b755dfSJohn Baldwin 		    vpd->vpd_ros[i].len);
47084b755dfSJohn Baldwin 		if (error)
47184b755dfSJohn Baldwin 			return (error);
47284b755dfSJohn Baldwin 		vpd_user = PVE_NEXT(vpd_user);
47384b755dfSJohn Baldwin 	}
47484b755dfSJohn Baldwin 	vpd_element.pve_flags = PVE_FLAG_RW;
47584b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_wcnt; i++) {
47684b755dfSJohn Baldwin 		vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0];
47784b755dfSJohn Baldwin 		vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1];
47884b755dfSJohn Baldwin 		vpd_element.pve_datalen = vpd->vpd_w[i].len;
47984b755dfSJohn Baldwin 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
48084b755dfSJohn Baldwin 		if (error)
48184b755dfSJohn Baldwin 			return (error);
48284b755dfSJohn Baldwin 		error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data,
48384b755dfSJohn Baldwin 		    vpd->vpd_w[i].len);
48484b755dfSJohn Baldwin 		if (error)
48584b755dfSJohn Baldwin 			return (error);
48684b755dfSJohn Baldwin 		vpd_user = PVE_NEXT(vpd_user);
48784b755dfSJohn Baldwin 	}
48884b755dfSJohn Baldwin 	KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
48984b755dfSJohn Baldwin 	    ("length mismatch"));
49084b755dfSJohn Baldwin 	lvio->plvi_len = len;
49184b755dfSJohn Baldwin 	return (0);
49284b755dfSJohn Baldwin }
49384b755dfSJohn Baldwin 
49484b755dfSJohn Baldwin static int
49589c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
4968983cfbfSMike Smith {
4975060ec97SRyan Stone 	device_t pcidev;
49833d3fffaSMarius Strobl 	void *confdata;
4998983cfbfSMike Smith 	const char *name;
50033d3fffaSMarius Strobl 	struct devlist *devlist_head;
501904c3909SGleb Smirnoff 	struct pci_conf_io *cio = NULL;
50233d3fffaSMarius Strobl 	struct pci_devinfo *dinfo;
50333d3fffaSMarius Strobl 	struct pci_io *io;
504da1e0915SJohn Baldwin 	struct pci_bar_io *bio;
50584b755dfSJohn Baldwin 	struct pci_list_vpd_io *lvio;
50633d3fffaSMarius Strobl 	struct pci_match_conf *pattern_buf;
507a90dd577SJohn Baldwin 	struct pci_map *pm;
50833d3fffaSMarius Strobl 	size_t confsz, iolen, pbufsz;
50933d3fffaSMarius Strobl 	int error, ionum, i, num_patterns;
510b2068c0cSWarner Losh #ifdef PRE7_COMPAT
511b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
512904c3909SGleb Smirnoff 	struct pci_conf_io32 *cio32 = NULL;
513904c3909SGleb Smirnoff 	struct pci_conf_old32 conf_old32;
514fee3029cSDavid Xu 	struct pci_match_conf_old32 *pattern_buf_old32 = NULL;
515b01bf72bSMaxim Sobolev #endif
51633d3fffaSMarius Strobl 	struct pci_conf_old conf_old;
51733d3fffaSMarius Strobl 	struct pci_io iodata;
51833d3fffaSMarius Strobl 	struct pci_io_old *io_old;
519fee3029cSDavid Xu 	struct pci_match_conf_old *pattern_buf_old = NULL;
5208983cfbfSMike Smith 
52133d3fffaSMarius Strobl 	io_old = NULL;
52233d3fffaSMarius Strobl #endif
5238983cfbfSMike Smith 
52484b755dfSJohn Baldwin 	if (!(flag & FWRITE)) {
52584b755dfSJohn Baldwin 		switch (cmd) {
52684b755dfSJohn Baldwin #ifdef PRE7_COMPAT
52784b755dfSJohn Baldwin #ifdef COMPAT_FREEBSD32
52884b755dfSJohn Baldwin 		case PCIOCGETCONF_OLD32:
52984b755dfSJohn Baldwin #endif
53084b755dfSJohn Baldwin 		case PCIOCGETCONF_OLD:
53184b755dfSJohn Baldwin #endif
53284b755dfSJohn Baldwin 		case PCIOCGETCONF:
53384b755dfSJohn Baldwin 		case PCIOCGETBAR:
53484b755dfSJohn Baldwin 		case PCIOCLISTVPD:
53584b755dfSJohn Baldwin 			break;
53684b755dfSJohn Baldwin 		default:
53784b755dfSJohn Baldwin 			return (EPERM);
53884b755dfSJohn Baldwin 		}
53984b755dfSJohn Baldwin 	}
54084b755dfSJohn Baldwin 
5418983cfbfSMike Smith 	switch (cmd) {
542b2068c0cSWarner Losh #ifdef PRE7_COMPAT
543904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
544b01bf72bSMaxim Sobolev 	case PCIOCGETCONF_OLD32:
545b01bf72bSMaxim Sobolev                cio32 = (struct pci_conf_io32 *)data;
546b01bf72bSMaxim Sobolev                cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
547b01bf72bSMaxim Sobolev                cio->pat_buf_len = cio32->pat_buf_len;
548b01bf72bSMaxim Sobolev                cio->num_patterns = cio32->num_patterns;
549b01bf72bSMaxim Sobolev                cio->patterns = (void *)(uintptr_t)cio32->patterns;
550b01bf72bSMaxim Sobolev                cio->match_buf_len = cio32->match_buf_len;
551b01bf72bSMaxim Sobolev                cio->num_matches = cio32->num_matches;
552b01bf72bSMaxim Sobolev                cio->matches = (void *)(uintptr_t)cio32->matches;
553b01bf72bSMaxim Sobolev                cio->offset = cio32->offset;
554b01bf72bSMaxim Sobolev                cio->generation = cio32->generation;
555b01bf72bSMaxim Sobolev                cio->status = cio32->status;
556b01bf72bSMaxim Sobolev                cio32->num_matches = 0;
557904c3909SGleb Smirnoff                break;
558904c3909SGleb Smirnoff #endif
55933d3fffaSMarius Strobl 	case PCIOCGETCONF_OLD:
56033d3fffaSMarius Strobl #endif
5618983cfbfSMike Smith 	case PCIOCGETCONF:
5628983cfbfSMike Smith 		cio = (struct pci_conf_io *)data;
563904c3909SGleb Smirnoff 	}
564904c3909SGleb Smirnoff 
565904c3909SGleb Smirnoff 	switch (cmd) {
566904c3909SGleb Smirnoff #ifdef PRE7_COMPAT
567904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
568904c3909SGleb Smirnoff 	case PCIOCGETCONF_OLD32:
569904c3909SGleb Smirnoff #endif
570904c3909SGleb Smirnoff 	case PCIOCGETCONF_OLD:
571904c3909SGleb Smirnoff #endif
572904c3909SGleb Smirnoff 	case PCIOCGETCONF:
5738983cfbfSMike Smith 
57433d3fffaSMarius Strobl 		pattern_buf = NULL;
5758983cfbfSMike Smith 		num_patterns = 0;
5768983cfbfSMike Smith 		dinfo = NULL;
5778983cfbfSMike Smith 
57833d3fffaSMarius Strobl 		cio->num_matches = 0;
57933d3fffaSMarius Strobl 
5808983cfbfSMike Smith 		/*
5818983cfbfSMike Smith 		 * If the user specified an offset into the device list,
5828983cfbfSMike Smith 		 * but the list has changed since they last called this
5838983cfbfSMike Smith 		 * ioctl, tell them that the list has changed.  They will
5848983cfbfSMike Smith 		 * have to get the list from the beginning.
5858983cfbfSMike Smith 		 */
5868983cfbfSMike Smith 		if ((cio->offset != 0)
5878983cfbfSMike Smith 		 && (cio->generation != pci_generation)){
5888983cfbfSMike Smith 			cio->status = PCI_GETCONF_LIST_CHANGED;
5898983cfbfSMike Smith 			error = 0;
590b01bf72bSMaxim Sobolev 			goto getconfexit;
5918983cfbfSMike Smith 		}
5928983cfbfSMike Smith 
5938983cfbfSMike Smith 		/*
5948983cfbfSMike Smith 		 * Check to see whether the user has asked for an offset
5958983cfbfSMike Smith 		 * past the end of our list.
5968983cfbfSMike Smith 		 */
5978983cfbfSMike Smith 		if (cio->offset >= pci_numdevs) {
5988983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
5998983cfbfSMike Smith 			error = 0;
600b01bf72bSMaxim Sobolev 			goto getconfexit;
6018983cfbfSMike Smith 		}
6028983cfbfSMike Smith 
6038983cfbfSMike Smith 		/* get the head of the device queue */
6048983cfbfSMike Smith 		devlist_head = &pci_devq;
6058983cfbfSMike Smith 
6068983cfbfSMike Smith 		/*
6078983cfbfSMike Smith 		 * Determine how much room we have for pci_conf structures.
6088983cfbfSMike Smith 		 * Round the user's buffer size down to the nearest
6098983cfbfSMike Smith 		 * multiple of sizeof(struct pci_conf) in case the user
6108983cfbfSMike Smith 		 * didn't specify a multiple of that size.
6118983cfbfSMike Smith 		 */
612b2068c0cSWarner Losh #ifdef PRE7_COMPAT
613b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
614b01bf72bSMaxim Sobolev 		if (cmd == PCIOCGETCONF_OLD32)
615b01bf72bSMaxim Sobolev 			confsz = sizeof(struct pci_conf_old32);
616b01bf72bSMaxim Sobolev 		else
617b01bf72bSMaxim Sobolev #endif
61833d3fffaSMarius Strobl 		if (cmd == PCIOCGETCONF_OLD)
61933d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf_old);
62033d3fffaSMarius Strobl 		else
62133d3fffaSMarius Strobl #endif
62233d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf);
62333d3fffaSMarius Strobl 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
62433d3fffaSMarius Strobl 		    pci_numdevs * confsz);
6258983cfbfSMike Smith 
6268983cfbfSMike Smith 		/*
6278983cfbfSMike Smith 		 * Since we know that iolen is a multiple of the size of
6288983cfbfSMike Smith 		 * the pciconf union, it's okay to do this.
6298983cfbfSMike Smith 		 */
63033d3fffaSMarius Strobl 		ionum = iolen / confsz;
6318983cfbfSMike Smith 
6328983cfbfSMike Smith 		/*
6338983cfbfSMike Smith 		 * If this test is true, the user wants the pci_conf
6348983cfbfSMike Smith 		 * structures returned to match the supplied entries.
6358983cfbfSMike Smith 		 */
636e3f932deSJohn-Mark Gurney 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
6378983cfbfSMike Smith 		 && (cio->pat_buf_len > 0)) {
6388983cfbfSMike Smith 			/*
6398983cfbfSMike Smith 			 * pat_buf_len needs to be:
6408983cfbfSMike Smith 			 * num_patterns * sizeof(struct pci_match_conf)
6418983cfbfSMike Smith 			 * While it is certainly possible the user just
6428983cfbfSMike Smith 			 * allocated a large buffer, but set the number of
6438983cfbfSMike Smith 			 * matches correctly, it is far more likely that
6448983cfbfSMike Smith 			 * their kernel doesn't match the userland utility
6458983cfbfSMike Smith 			 * they're using.  It's also possible that the user
6468983cfbfSMike Smith 			 * forgot to initialize some variables.  Yes, this
6478983cfbfSMike Smith 			 * may be overly picky, but I hazard to guess that
6488983cfbfSMike Smith 			 * it's far more likely to just catch folks that
6498983cfbfSMike Smith 			 * updated their kernel but not their userland.
6508983cfbfSMike Smith 			 */
651b2068c0cSWarner Losh #ifdef PRE7_COMPAT
652b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
653b01bf72bSMaxim Sobolev 			if (cmd == PCIOCGETCONF_OLD32)
654b01bf72bSMaxim Sobolev 				pbufsz = sizeof(struct pci_match_conf_old32);
655b01bf72bSMaxim Sobolev 			else
656b01bf72bSMaxim Sobolev #endif
65733d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD)
65833d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf_old);
65933d3fffaSMarius Strobl 			else
66033d3fffaSMarius Strobl #endif
66133d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf);
66233d3fffaSMarius Strobl 			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
66333d3fffaSMarius Strobl 				/* The user made a mistake, return an error. */
6648983cfbfSMike Smith 				cio->status = PCI_GETCONF_ERROR;
6658983cfbfSMike Smith 				error = EINVAL;
666b01bf72bSMaxim Sobolev 				goto getconfexit;
6678983cfbfSMike Smith 			}
6688983cfbfSMike Smith 
6698983cfbfSMike Smith 			/*
6708983cfbfSMike Smith 			 * Allocate a buffer to hold the patterns.
6718983cfbfSMike Smith 			 */
672b2068c0cSWarner Losh #ifdef PRE7_COMPAT
673904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
674b01bf72bSMaxim Sobolev 			if (cmd == PCIOCGETCONF_OLD32) {
675b01bf72bSMaxim Sobolev 				pattern_buf_old32 = malloc(cio->pat_buf_len,
676b01bf72bSMaxim Sobolev 				    M_TEMP, M_WAITOK);
677b01bf72bSMaxim Sobolev 				error = copyin(cio->patterns,
678b01bf72bSMaxim Sobolev 				    pattern_buf_old32, cio->pat_buf_len);
679b01bf72bSMaxim Sobolev 			} else
680904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */
68133d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD) {
68233d3fffaSMarius Strobl 				pattern_buf_old = malloc(cio->pat_buf_len,
68333d3fffaSMarius Strobl 				    M_TEMP, M_WAITOK);
68433d3fffaSMarius Strobl 				error = copyin(cio->patterns,
68533d3fffaSMarius Strobl 				    pattern_buf_old, cio->pat_buf_len);
68633d3fffaSMarius Strobl 			} else
687904c3909SGleb Smirnoff #endif /* PRE7_COMPAT */
68833d3fffaSMarius Strobl 			{
6898983cfbfSMike Smith 				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
690a163d034SWarner Losh 				    M_WAITOK);
6918983cfbfSMike Smith 				error = copyin(cio->patterns, pattern_buf,
6928983cfbfSMike Smith 				    cio->pat_buf_len);
69333d3fffaSMarius Strobl 			}
694d08239c1SJohn-Mark Gurney 			if (error != 0) {
695d08239c1SJohn-Mark Gurney 				error = EINVAL;
696d08239c1SJohn-Mark Gurney 				goto getconfexit;
697d08239c1SJohn-Mark Gurney 			}
6988983cfbfSMike Smith 			num_patterns = cio->num_patterns;
6998983cfbfSMike Smith 		} else if ((cio->num_patterns > 0)
7008983cfbfSMike Smith 			|| (cio->pat_buf_len > 0)) {
7018983cfbfSMike Smith 			/*
7028983cfbfSMike Smith 			 * The user made a mistake, spit out an error.
7038983cfbfSMike Smith 			 */
7048983cfbfSMike Smith 			cio->status = PCI_GETCONF_ERROR;
7058983cfbfSMike Smith 			error = EINVAL;
706b01bf72bSMaxim Sobolev                        goto getconfexit;
70733d3fffaSMarius Strobl 		}
7088983cfbfSMike Smith 
7098983cfbfSMike Smith 		/*
7108983cfbfSMike Smith 		 * Go through the list of devices and copy out the devices
7118983cfbfSMike Smith 		 * that match the user's criteria.
7128983cfbfSMike Smith 		 */
71310012d53SJohn Baldwin 		for (cio->num_matches = 0, i = 0,
7148983cfbfSMike Smith 				 dinfo = STAILQ_FIRST(devlist_head);
71510012d53SJohn Baldwin 		     dinfo != NULL;
7168983cfbfSMike Smith 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
7178983cfbfSMike Smith 
7188983cfbfSMike Smith 			if (i < cio->offset)
7198983cfbfSMike Smith 				continue;
7208983cfbfSMike Smith 
7218983cfbfSMike Smith 			/* Populate pd_name and pd_unit */
7228983cfbfSMike Smith 			name = NULL;
7230678f786SJohn Baldwin 			if (dinfo->cfg.dev)
7248983cfbfSMike Smith 				name = device_get_name(dinfo->cfg.dev);
7258983cfbfSMike Smith 			if (name) {
7268983cfbfSMike Smith 				strncpy(dinfo->conf.pd_name, name,
7278983cfbfSMike Smith 					sizeof(dinfo->conf.pd_name));
7288983cfbfSMike Smith 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
7298983cfbfSMike Smith 				dinfo->conf.pd_unit =
7308983cfbfSMike Smith 					device_get_unit(dinfo->cfg.dev);
7310678f786SJohn Baldwin 			} else {
7320678f786SJohn Baldwin 				dinfo->conf.pd_name[0] = '\0';
7330678f786SJohn Baldwin 				dinfo->conf.pd_unit = 0;
7348983cfbfSMike Smith 			}
7358983cfbfSMike Smith 
736b2068c0cSWarner Losh #ifdef PRE7_COMPAT
737904c3909SGleb Smirnoff 			if (
738904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
739904c3909SGleb Smirnoff 			    (cmd == PCIOCGETCONF_OLD32 &&
740b01bf72bSMaxim Sobolev 			    (pattern_buf_old32 == NULL ||
741b01bf72bSMaxim Sobolev 			    pci_conf_match_old32(pattern_buf_old32,
742b01bf72bSMaxim Sobolev 			    num_patterns, &dinfo->conf) == 0)) ||
743904c3909SGleb Smirnoff #endif
744b01bf72bSMaxim Sobolev 			    (cmd == PCIOCGETCONF_OLD &&
74533d3fffaSMarius Strobl 			    (pattern_buf_old == NULL ||
74633d3fffaSMarius Strobl 			    pci_conf_match_old(pattern_buf_old, num_patterns,
74733d3fffaSMarius Strobl 			    &dinfo->conf) == 0)) ||
74833d3fffaSMarius Strobl 			    (cmd == PCIOCGETCONF &&
74933d3fffaSMarius Strobl 			    (pattern_buf == NULL ||
75033d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
75133d3fffaSMarius Strobl 			    &dinfo->conf) == 0))) {
75233d3fffaSMarius Strobl #else
75333d3fffaSMarius Strobl 			if (pattern_buf == NULL ||
75433d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
75533d3fffaSMarius Strobl 			    &dinfo->conf) == 0) {
75633d3fffaSMarius Strobl #endif
7578983cfbfSMike Smith 				/*
7588983cfbfSMike Smith 				 * If we've filled up the user's buffer,
7598983cfbfSMike Smith 				 * break out at this point.  Since we've
7608983cfbfSMike Smith 				 * got a match here, we'll pick right back
7618983cfbfSMike Smith 				 * up at the matching entry.  We can also
7628983cfbfSMike Smith 				 * tell the user that there are more matches
7638983cfbfSMike Smith 				 * left.
7648983cfbfSMike Smith 				 */
765*fadd3f8aSConrad Meyer 				if (cio->num_matches >= ionum) {
766*fadd3f8aSConrad Meyer 					error = 0;
7678983cfbfSMike Smith 					break;
768*fadd3f8aSConrad Meyer 				}
7698983cfbfSMike Smith 
770b2068c0cSWarner Losh #ifdef PRE7_COMPAT
771904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
772b01bf72bSMaxim Sobolev 				if (cmd == PCIOCGETCONF_OLD32) {
773b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_bus =
774b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_bus;
775b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_dev =
776b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_dev;
777b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_func =
778b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_func;
779b01bf72bSMaxim Sobolev 					conf_old32.pc_hdr = dinfo->conf.pc_hdr;
780b01bf72bSMaxim Sobolev 					conf_old32.pc_subvendor =
781b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subvendor;
782b01bf72bSMaxim Sobolev 					conf_old32.pc_subdevice =
783b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subdevice;
784b01bf72bSMaxim Sobolev 					conf_old32.pc_vendor =
785b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_vendor;
786b01bf72bSMaxim Sobolev 					conf_old32.pc_device =
787b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_device;
788b01bf72bSMaxim Sobolev 					conf_old32.pc_class =
789b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_class;
790b01bf72bSMaxim Sobolev 					conf_old32.pc_subclass =
791b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subclass;
792b01bf72bSMaxim Sobolev 					conf_old32.pc_progif =
793b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_progif;
794b01bf72bSMaxim Sobolev 					conf_old32.pc_revid =
795b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_revid;
796b01bf72bSMaxim Sobolev 					strncpy(conf_old32.pd_name,
797b01bf72bSMaxim Sobolev 					    dinfo->conf.pd_name,
798b01bf72bSMaxim Sobolev 					    sizeof(conf_old32.pd_name));
799b01bf72bSMaxim Sobolev 					conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
800b01bf72bSMaxim Sobolev 					conf_old32.pd_unit =
801e5280830SGleb Smirnoff 					    (uint32_t)dinfo->conf.pd_unit;
802b01bf72bSMaxim Sobolev 					confdata = &conf_old32;
803b01bf72bSMaxim Sobolev 				} else
804904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */
80533d3fffaSMarius Strobl 				if (cmd == PCIOCGETCONF_OLD) {
80633d3fffaSMarius Strobl 					conf_old.pc_sel.pc_bus =
80733d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_bus;
80833d3fffaSMarius Strobl 					conf_old.pc_sel.pc_dev =
80933d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_dev;
81033d3fffaSMarius Strobl 					conf_old.pc_sel.pc_func =
81133d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_func;
81233d3fffaSMarius Strobl 					conf_old.pc_hdr = dinfo->conf.pc_hdr;
81333d3fffaSMarius Strobl 					conf_old.pc_subvendor =
81433d3fffaSMarius Strobl 					    dinfo->conf.pc_subvendor;
81533d3fffaSMarius Strobl 					conf_old.pc_subdevice =
81633d3fffaSMarius Strobl 					    dinfo->conf.pc_subdevice;
81733d3fffaSMarius Strobl 					conf_old.pc_vendor =
81833d3fffaSMarius Strobl 					    dinfo->conf.pc_vendor;
81933d3fffaSMarius Strobl 					conf_old.pc_device =
82033d3fffaSMarius Strobl 					    dinfo->conf.pc_device;
82133d3fffaSMarius Strobl 					conf_old.pc_class =
82233d3fffaSMarius Strobl 					    dinfo->conf.pc_class;
82333d3fffaSMarius Strobl 					conf_old.pc_subclass =
82433d3fffaSMarius Strobl 					    dinfo->conf.pc_subclass;
82533d3fffaSMarius Strobl 					conf_old.pc_progif =
82633d3fffaSMarius Strobl 					    dinfo->conf.pc_progif;
82733d3fffaSMarius Strobl 					conf_old.pc_revid =
82833d3fffaSMarius Strobl 					    dinfo->conf.pc_revid;
829c5860546SMarius Strobl 					strncpy(conf_old.pd_name,
830c5860546SMarius Strobl 					    dinfo->conf.pd_name,
83133d3fffaSMarius Strobl 					    sizeof(conf_old.pd_name));
832c5860546SMarius Strobl 					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
83333d3fffaSMarius Strobl 					conf_old.pd_unit =
83433d3fffaSMarius Strobl 					    dinfo->conf.pd_unit;
83533d3fffaSMarius Strobl 					confdata = &conf_old;
83633d3fffaSMarius Strobl 				} else
837904c3909SGleb Smirnoff #endif /* PRE7_COMPAT */
83833d3fffaSMarius Strobl 					confdata = &dinfo->conf;
83910012d53SJohn Baldwin 				error = copyout(confdata,
840c5860546SMarius Strobl 				    (caddr_t)cio->matches +
84110012d53SJohn Baldwin 				    confsz * cio->num_matches, confsz);
84210012d53SJohn Baldwin 				if (error)
84310012d53SJohn Baldwin 					break;
84433d3fffaSMarius Strobl 				cio->num_matches++;
8458983cfbfSMike Smith 			}
846d08239c1SJohn-Mark Gurney 		}
8478983cfbfSMike Smith 
8488983cfbfSMike Smith 		/*
8498983cfbfSMike Smith 		 * Set the pointer into the list, so if the user is getting
8508983cfbfSMike Smith 		 * n records at a time, where n < pci_numdevs,
8518983cfbfSMike Smith 		 */
8528983cfbfSMike Smith 		cio->offset = i;
8538983cfbfSMike Smith 
8548983cfbfSMike Smith 		/*
8558983cfbfSMike Smith 		 * Set the generation, the user will need this if they make
8568983cfbfSMike Smith 		 * another ioctl call with offset != 0.
8578983cfbfSMike Smith 		 */
8588983cfbfSMike Smith 		cio->generation = pci_generation;
8598983cfbfSMike Smith 
8608983cfbfSMike Smith 		/*
8618983cfbfSMike Smith 		 * If this is the last device, inform the user so he won't
8628983cfbfSMike Smith 		 * bother asking for more devices.  If dinfo isn't NULL, we
8638983cfbfSMike Smith 		 * know that there are more matches in the list because of
8648983cfbfSMike Smith 		 * the way the traversal is done.
8658983cfbfSMike Smith 		 */
8668983cfbfSMike Smith 		if (dinfo == NULL)
8678983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
8688983cfbfSMike Smith 		else
8698983cfbfSMike Smith 			cio->status = PCI_GETCONF_MORE_DEVS;
8708983cfbfSMike Smith 
871d08239c1SJohn-Mark Gurney getconfexit:
872904c3909SGleb Smirnoff #ifdef PRE7_COMPAT
873b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
874b01bf72bSMaxim Sobolev 		if (cmd == PCIOCGETCONF_OLD32) {
875b01bf72bSMaxim Sobolev 			cio32->status = cio->status;
876b01bf72bSMaxim Sobolev 			cio32->generation = cio->generation;
877b01bf72bSMaxim Sobolev 			cio32->offset = cio->offset;
878b01bf72bSMaxim Sobolev 			cio32->num_matches = cio->num_matches;
879b01bf72bSMaxim Sobolev 			free(cio, M_TEMP);
880b01bf72bSMaxim Sobolev 		}
881b01bf72bSMaxim Sobolev 		if (pattern_buf_old32 != NULL)
882b01bf72bSMaxim Sobolev 			free(pattern_buf_old32, M_TEMP);
883904c3909SGleb Smirnoff #endif
88433d3fffaSMarius Strobl 		if (pattern_buf_old != NULL)
88533d3fffaSMarius Strobl 			free(pattern_buf_old, M_TEMP);
88633d3fffaSMarius Strobl #endif
887904c3909SGleb Smirnoff 		if (pattern_buf != NULL)
888904c3909SGleb Smirnoff 			free(pattern_buf, M_TEMP);
8898983cfbfSMike Smith 
8908983cfbfSMike Smith 		break;
8918983cfbfSMike Smith 
892b2068c0cSWarner Losh #ifdef PRE7_COMPAT
89333d3fffaSMarius Strobl 	case PCIOCREAD_OLD:
89433d3fffaSMarius Strobl 	case PCIOCWRITE_OLD:
89533d3fffaSMarius Strobl 		io_old = (struct pci_io_old *)data;
89633d3fffaSMarius Strobl 		iodata.pi_sel.pc_domain = 0;
89733d3fffaSMarius Strobl 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
89833d3fffaSMarius Strobl 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
89933d3fffaSMarius Strobl 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
90033d3fffaSMarius Strobl 		iodata.pi_reg = io_old->pi_reg;
90133d3fffaSMarius Strobl 		iodata.pi_width = io_old->pi_width;
90233d3fffaSMarius Strobl 		iodata.pi_data = io_old->pi_data;
90333d3fffaSMarius Strobl 		data = (caddr_t)&iodata;
90433d3fffaSMarius Strobl 		/* FALLTHROUGH */
90533d3fffaSMarius Strobl #endif
90666f314b5SStefan Eßer 	case PCIOCREAD:
9078983cfbfSMike Smith 	case PCIOCWRITE:
9088983cfbfSMike Smith 		io = (struct pci_io *)data;
9098983cfbfSMike Smith 		switch(io->pi_width) {
9108983cfbfSMike Smith 		case 4:
9118983cfbfSMike Smith 		case 2:
9128983cfbfSMike Smith 		case 1:
913d16d35fdSAndriy Gapon 			/* Make sure register is not negative and aligned. */
91433d3fffaSMarius Strobl 			if (io->pi_reg < 0 ||
91533d3fffaSMarius Strobl 			    io->pi_reg & (io->pi_width - 1)) {
91666f314b5SStefan Eßer 				error = EINVAL;
91740ed3f47SRuslan Ermilov 				break;
91840ed3f47SRuslan Ermilov 			}
9198983cfbfSMike Smith 			/*
9208983cfbfSMike Smith 			 * Assume that the user-level bus number is
92137ce43b7SBruce M Simpson 			 * in fact the physical PCI bus number.
92237ce43b7SBruce M Simpson 			 * Look up the grandparent, i.e. the bridge device,
92337ce43b7SBruce M Simpson 			 * so that we can issue configuration space cycles.
9248983cfbfSMike Smith 			 */
92555aaf894SMarius Strobl 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
92655aaf894SMarius Strobl 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
92755aaf894SMarius Strobl 			    io->pi_sel.pc_func);
92837ce43b7SBruce M Simpson 			if (pcidev) {
929b2068c0cSWarner Losh #ifdef PRE7_COMPAT
93033d3fffaSMarius Strobl 				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
93133d3fffaSMarius Strobl #else
93266f314b5SStefan Eßer 				if (cmd == PCIOCWRITE)
93333d3fffaSMarius Strobl #endif
9345060ec97SRyan Stone 					pci_write_config(pcidev,
9358983cfbfSMike Smith 							  io->pi_reg,
9368983cfbfSMike Smith 							  io->pi_data,
9378983cfbfSMike Smith 							  io->pi_width);
938b2068c0cSWarner Losh #ifdef PRE7_COMPAT
93933d3fffaSMarius Strobl 				else if (cmd == PCIOCREAD_OLD)
94033d3fffaSMarius Strobl 					io_old->pi_data =
9415060ec97SRyan Stone 						pci_read_config(pcidev,
94233d3fffaSMarius Strobl 							  io->pi_reg,
94333d3fffaSMarius Strobl 							  io->pi_width);
94433d3fffaSMarius Strobl #endif
94566f314b5SStefan Eßer 				else
94666f314b5SStefan Eßer 					io->pi_data =
9475060ec97SRyan Stone 						pci_read_config(pcidev,
94866f314b5SStefan Eßer 							  io->pi_reg,
94966f314b5SStefan Eßer 							  io->pi_width);
9508983cfbfSMike Smith 				error = 0;
9518983cfbfSMike Smith 			} else {
9528910aa92SPaul Saab #ifdef COMPAT_FREEBSD4
95333d3fffaSMarius Strobl 				if (cmd == PCIOCREAD_OLD) {
95433d3fffaSMarius Strobl 					io_old->pi_data = -1;
9558910aa92SPaul Saab 					error = 0;
9568910aa92SPaul Saab 				} else
9578910aa92SPaul Saab #endif
9588983cfbfSMike Smith 					error = ENODEV;
9598983cfbfSMike Smith 			}
9608983cfbfSMike Smith 			break;
9618983cfbfSMike Smith 		default:
962d08239c1SJohn-Mark Gurney 			error = EINVAL;
9638983cfbfSMike Smith 			break;
9648983cfbfSMike Smith 		}
9658983cfbfSMike Smith 		break;
9668983cfbfSMike Smith 
967da1e0915SJohn Baldwin 	case PCIOCGETBAR:
968da1e0915SJohn Baldwin 		bio = (struct pci_bar_io *)data;
969da1e0915SJohn Baldwin 
970da1e0915SJohn Baldwin 		/*
971da1e0915SJohn Baldwin 		 * Assume that the user-level bus number is
972da1e0915SJohn Baldwin 		 * in fact the physical PCI bus number.
973da1e0915SJohn Baldwin 		 */
974da1e0915SJohn Baldwin 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
975da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
976da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_func);
977da1e0915SJohn Baldwin 		if (pcidev == NULL) {
978da1e0915SJohn Baldwin 			error = ENODEV;
979da1e0915SJohn Baldwin 			break;
980da1e0915SJohn Baldwin 		}
981a90dd577SJohn Baldwin 		pm = pci_find_bar(pcidev, bio->pbi_reg);
982a90dd577SJohn Baldwin 		if (pm == NULL) {
983da1e0915SJohn Baldwin 			error = EINVAL;
984da1e0915SJohn Baldwin 			break;
985da1e0915SJohn Baldwin 		}
986a90dd577SJohn Baldwin 		bio->pbi_base = pm->pm_value;
987a90dd577SJohn Baldwin 		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
988a90dd577SJohn Baldwin 		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
989da1e0915SJohn Baldwin 		error = 0;
990da1e0915SJohn Baldwin 		break;
991762aad81SNeel Natu 	case PCIOCATTACHED:
992762aad81SNeel Natu 		error = 0;
993762aad81SNeel Natu 		io = (struct pci_io *)data;
994762aad81SNeel Natu 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
995762aad81SNeel Natu 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
996762aad81SNeel Natu 		if (pcidev != NULL)
997762aad81SNeel Natu 			io->pi_data = device_is_attached(pcidev);
998762aad81SNeel Natu 		else
999762aad81SNeel Natu 			error = ENODEV;
1000762aad81SNeel Natu 		break;
100184b755dfSJohn Baldwin 	case PCIOCLISTVPD:
100284b755dfSJohn Baldwin 		lvio = (struct pci_list_vpd_io *)data;
100384b755dfSJohn Baldwin 
100484b755dfSJohn Baldwin 		/*
100584b755dfSJohn Baldwin 		 * Assume that the user-level bus number is
100684b755dfSJohn Baldwin 		 * in fact the physical PCI bus number.
100784b755dfSJohn Baldwin 		 */
100884b755dfSJohn Baldwin 		pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain,
100984b755dfSJohn Baldwin 		    lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev,
101084b755dfSJohn Baldwin 		    lvio->plvi_sel.pc_func);
101184b755dfSJohn Baldwin 		if (pcidev == NULL) {
101284b755dfSJohn Baldwin 			error = ENODEV;
101384b755dfSJohn Baldwin 			break;
101484b755dfSJohn Baldwin 		}
101584b755dfSJohn Baldwin 		error = pci_list_vpd(pcidev, lvio);
101684b755dfSJohn Baldwin 		break;
10178983cfbfSMike Smith 	default:
10188983cfbfSMike Smith 		error = ENOTTY;
10198983cfbfSMike Smith 		break;
10208983cfbfSMike Smith 	}
10218983cfbfSMike Smith 
10228983cfbfSMike Smith 	return (error);
10238983cfbfSMike Smith }
1024