xref: /freebsd/sys/dev/pci/pci_user.c (revision e5280830c4837cd9e6d41a06ba556f5eac9a7a89)
1aad970f1SDavid E. O'Brien /*-
28983cfbfSMike Smith  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
38983cfbfSMike Smith  * All rights reserved.
48983cfbfSMike Smith  *
58983cfbfSMike Smith  * Redistribution and use in source and binary forms, with or without
68983cfbfSMike Smith  * modification, are permitted provided that the following conditions
78983cfbfSMike Smith  * are met:
88983cfbfSMike Smith  * 1. Redistributions of source code must retain the above copyright
98983cfbfSMike Smith  *    notice unmodified, this list of conditions, and the following
108983cfbfSMike Smith  *    disclaimer.
118983cfbfSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
128983cfbfSMike Smith  *    notice, this list of conditions and the following disclaimer in the
138983cfbfSMike Smith  *    documentation and/or other materials provided with the distribution.
148983cfbfSMike Smith  *
158983cfbfSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168983cfbfSMike Smith  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178983cfbfSMike Smith  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188983cfbfSMike Smith  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198983cfbfSMike Smith  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
208983cfbfSMike Smith  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
218983cfbfSMike Smith  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
228983cfbfSMike Smith  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238983cfbfSMike Smith  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
248983cfbfSMike Smith  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258983cfbfSMike Smith  */
268983cfbfSMike Smith 
27aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
28aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
29aad970f1SDavid E. O'Brien 
308983cfbfSMike Smith #include "opt_bus.h"	/* XXX trim includes */
318910aa92SPaul Saab #include "opt_compat.h"
328983cfbfSMike Smith 
338983cfbfSMike Smith #include <sys/param.h>
348983cfbfSMike Smith #include <sys/systm.h>
358983cfbfSMike Smith #include <sys/malloc.h>
368983cfbfSMike Smith #include <sys/module.h>
378983cfbfSMike Smith #include <sys/linker.h>
388983cfbfSMike Smith #include <sys/fcntl.h>
398983cfbfSMike Smith #include <sys/conf.h>
408983cfbfSMike Smith #include <sys/kernel.h>
418002488bSRobert Watson #include <sys/proc.h>
428983cfbfSMike Smith #include <sys/queue.h>
438983cfbfSMike Smith #include <sys/types.h>
448983cfbfSMike Smith 
458983cfbfSMike Smith #include <vm/vm.h>
468983cfbfSMike Smith #include <vm/pmap.h>
478983cfbfSMike Smith #include <vm/vm_extern.h>
488983cfbfSMike Smith 
498983cfbfSMike Smith #include <sys/bus.h>
508983cfbfSMike Smith #include <machine/bus.h>
518983cfbfSMike Smith #include <sys/rman.h>
528983cfbfSMike Smith #include <machine/resource.h>
538983cfbfSMike Smith 
548983cfbfSMike Smith #include <sys/pciio.h>
5538d8c994SWarner Losh #include <dev/pci/pcireg.h>
5638d8c994SWarner Losh #include <dev/pci/pcivar.h>
578983cfbfSMike Smith 
588983cfbfSMike Smith #include "pcib_if.h"
598983cfbfSMike Smith #include "pci_if.h"
608983cfbfSMike Smith 
618983cfbfSMike Smith /*
628983cfbfSMike Smith  * This is the user interface to PCI configuration space.
638983cfbfSMike Smith  */
648983cfbfSMike Smith 
65b40ce416SJulian Elischer static d_open_t 	pci_open;
66b40ce416SJulian Elischer static d_close_t	pci_close;
678983cfbfSMike Smith static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
688983cfbfSMike Smith 			       struct pci_conf *match_buf);
69b40ce416SJulian Elischer static d_ioctl_t	pci_ioctl;
708983cfbfSMike Smith 
718983cfbfSMike Smith struct cdevsw pcicdev = {
72dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
73dc08ffecSPoul-Henning Kamp 	.d_flags =	D_NEEDGIANT,
747ac40f5fSPoul-Henning Kamp 	.d_open =	pci_open,
757ac40f5fSPoul-Henning Kamp 	.d_close =	pci_close,
767ac40f5fSPoul-Henning Kamp 	.d_ioctl =	pci_ioctl,
777ac40f5fSPoul-Henning Kamp 	.d_name =	"pci",
788983cfbfSMike Smith };
798983cfbfSMike Smith 
808983cfbfSMike Smith static int
8189c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
828983cfbfSMike Smith {
838002488bSRobert Watson 	int error;
848002488bSRobert Watson 
858002488bSRobert Watson 	if (oflags & FWRITE) {
86a854ed98SJohn Baldwin 		error = securelevel_gt(td->td_ucred, 0);
878002488bSRobert Watson 		if (error)
888002488bSRobert Watson 			return (error);
898983cfbfSMike Smith 	}
908002488bSRobert Watson 
918002488bSRobert Watson 	return (0);
928983cfbfSMike Smith }
938983cfbfSMike Smith 
948983cfbfSMike Smith static int
9589c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
968983cfbfSMike Smith {
978983cfbfSMike Smith 	return 0;
988983cfbfSMike Smith }
998983cfbfSMike Smith 
1008983cfbfSMike Smith /*
1018983cfbfSMike Smith  * Match a single pci_conf structure against an array of pci_match_conf
1028983cfbfSMike Smith  * structures.  The first argument, 'matches', is an array of num_matches
1038983cfbfSMike Smith  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
1048983cfbfSMike Smith  * structure that will be compared to every entry in the matches array.
1058983cfbfSMike Smith  * This function returns 1 on failure, 0 on success.
1068983cfbfSMike Smith  */
1078983cfbfSMike Smith static int
1088983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches,
1098983cfbfSMike Smith 	       struct pci_conf *match_buf)
1108983cfbfSMike Smith {
1118983cfbfSMike Smith 	int i;
1128983cfbfSMike Smith 
1138983cfbfSMike Smith 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
1148983cfbfSMike Smith 		return(1);
1158983cfbfSMike Smith 
1168983cfbfSMike Smith 	for (i = 0; i < num_matches; i++) {
1178983cfbfSMike Smith 		/*
1188983cfbfSMike Smith 		 * I'm not sure why someone would do this...but...
1198983cfbfSMike Smith 		 */
1208983cfbfSMike Smith 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
1218983cfbfSMike Smith 			continue;
1228983cfbfSMike Smith 
1238983cfbfSMike Smith 		/*
1248983cfbfSMike Smith 		 * Look at each of the match flags.  If it's set, do the
1258983cfbfSMike Smith 		 * comparison.  If the comparison fails, we don't have a
1268983cfbfSMike Smith 		 * match, go on to the next item if there is one.
1278983cfbfSMike Smith 		 */
12855aaf894SMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
12955aaf894SMarius Strobl 		 && (match_buf->pc_sel.pc_domain !=
13055aaf894SMarius Strobl 		 matches[i].pc_sel.pc_domain))
13155aaf894SMarius Strobl 			continue;
13255aaf894SMarius Strobl 
1338983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
1348983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
1358983cfbfSMike Smith 			continue;
1368983cfbfSMike Smith 
1378983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
1388983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
1398983cfbfSMike Smith 			continue;
1408983cfbfSMike Smith 
1418983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
1428983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
1438983cfbfSMike Smith 			continue;
1448983cfbfSMike Smith 
1458983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
1468983cfbfSMike Smith 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
1478983cfbfSMike Smith 			continue;
1488983cfbfSMike Smith 
1498983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
1508983cfbfSMike Smith 		 && (match_buf->pc_device != matches[i].pc_device))
1518983cfbfSMike Smith 			continue;
1528983cfbfSMike Smith 
1538983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
1548983cfbfSMike Smith 		 && (match_buf->pc_class != matches[i].pc_class))
1558983cfbfSMike Smith 			continue;
1568983cfbfSMike Smith 
1578983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
1588983cfbfSMike Smith 		 && (match_buf->pd_unit != matches[i].pd_unit))
1598983cfbfSMike Smith 			continue;
1608983cfbfSMike Smith 
1618983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
1628983cfbfSMike Smith 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
1638983cfbfSMike Smith 			     sizeof(match_buf->pd_name)) != 0))
1648983cfbfSMike Smith 			continue;
1658983cfbfSMike Smith 
1668983cfbfSMike Smith 		return(0);
1678983cfbfSMike Smith 	}
1688983cfbfSMike Smith 
1698983cfbfSMike Smith 	return(1);
1708983cfbfSMike Smith }
1718983cfbfSMike Smith 
17233d3fffaSMarius Strobl #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
17333d3fffaSMarius Strobl     defined(COMPAT_FREEBSD6)
174b2068c0cSWarner Losh #define PRE7_COMPAT
17533d3fffaSMarius Strobl 
17633d3fffaSMarius Strobl typedef enum {
17733d3fffaSMarius Strobl 	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
17833d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
17933d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
18033d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
18133d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
18233d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
18333d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
18433d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
18533d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
18633d3fffaSMarius Strobl } pci_getconf_flags_old;
18733d3fffaSMarius Strobl 
18833d3fffaSMarius Strobl struct pcisel_old {
18933d3fffaSMarius Strobl 	u_int8_t	pc_bus;		/* bus number */
19033d3fffaSMarius Strobl 	u_int8_t	pc_dev;		/* device on this bus */
19133d3fffaSMarius Strobl 	u_int8_t	pc_func;	/* function on this device */
19233d3fffaSMarius Strobl };
19333d3fffaSMarius Strobl 
19433d3fffaSMarius Strobl struct pci_conf_old {
19533d3fffaSMarius Strobl 	struct pcisel_old pc_sel;	/* bus+slot+function */
19633d3fffaSMarius Strobl 	u_int8_t	pc_hdr;		/* PCI header type */
19733d3fffaSMarius Strobl 	u_int16_t	pc_subvendor;	/* card vendor ID */
19833d3fffaSMarius Strobl 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
19933d3fffaSMarius Strobl 					   card vendor */
20033d3fffaSMarius Strobl 	u_int16_t	pc_vendor;	/* chip vendor ID */
20133d3fffaSMarius Strobl 	u_int16_t	pc_device;	/* chip device ID, assigned by
20233d3fffaSMarius Strobl 					   chip vendor */
20333d3fffaSMarius Strobl 	u_int8_t	pc_class;	/* chip PCI class */
20433d3fffaSMarius Strobl 	u_int8_t	pc_subclass;	/* chip PCI subclass */
20533d3fffaSMarius Strobl 	u_int8_t	pc_progif;	/* chip PCI programming interface */
20633d3fffaSMarius Strobl 	u_int8_t	pc_revid;	/* chip revision ID */
20733d3fffaSMarius Strobl 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
20833d3fffaSMarius Strobl 	u_long		pd_unit;	/* device unit number */
20933d3fffaSMarius Strobl };
21033d3fffaSMarius Strobl 
21133d3fffaSMarius Strobl struct pci_match_conf_old {
21233d3fffaSMarius Strobl 	struct pcisel_old	pc_sel;		/* bus+slot+function */
21333d3fffaSMarius Strobl 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
21433d3fffaSMarius Strobl 	u_long			pd_unit;	/* Unit number */
21533d3fffaSMarius Strobl 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
21633d3fffaSMarius Strobl 	u_int16_t		pc_device;	/* PCI Device ID */
21733d3fffaSMarius Strobl 	u_int8_t		pc_class;	/* PCI class */
218c5860546SMarius Strobl 	pci_getconf_flags_old	flags;		/* Matching expression */
21933d3fffaSMarius Strobl };
22033d3fffaSMarius Strobl 
22133d3fffaSMarius Strobl struct pci_io_old {
22233d3fffaSMarius Strobl 	struct pcisel_old pi_sel;	/* device to operate on */
22333d3fffaSMarius Strobl 	int		pi_reg;		/* configuration register to examine */
22433d3fffaSMarius Strobl 	int		pi_width;	/* width (in bytes) of read or write */
22533d3fffaSMarius Strobl 	u_int32_t	pi_data;	/* data to write or result of read */
22633d3fffaSMarius Strobl };
22733d3fffaSMarius Strobl 
228b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
229b01bf72bSMaxim Sobolev struct pci_conf_old32 {
230b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
231*e5280830SGleb Smirnoff 	uint8_t		pc_hdr;		/* PCI header type */
232*e5280830SGleb Smirnoff 	uint16_t	pc_subvendor;	/* card vendor ID */
233*e5280830SGleb Smirnoff 	uint16_t	pc_subdevice;	/* card device ID, assigned by
234b01bf72bSMaxim Sobolev 					   card vendor */
235*e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* chip vendor ID */
236*e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* chip device ID, assigned by
237b01bf72bSMaxim Sobolev 					   chip vendor */
238*e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* chip PCI class */
239*e5280830SGleb Smirnoff 	uint8_t		pc_subclass;	/* chip PCI subclass */
240*e5280830SGleb Smirnoff 	uint8_t		pc_progif;	/* chip PCI programming interface */
241*e5280830SGleb Smirnoff 	uint8_t		pc_revid;	/* chip revision ID */
242b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
243*e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* device unit number (u_long) */
244b01bf72bSMaxim Sobolev };
245b01bf72bSMaxim Sobolev 
246b01bf72bSMaxim Sobolev struct pci_match_conf_old32 {
247b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
248b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
249*e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* Unit number (u_long) */
250*e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* PCI Vendor ID */
251*e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* PCI Device ID */
252*e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* PCI class */
253b01bf72bSMaxim Sobolev 	pci_getconf_flags_old flags;	/* Matching expression */
254b01bf72bSMaxim Sobolev };
255b01bf72bSMaxim Sobolev 
256b01bf72bSMaxim Sobolev struct pci_conf_io32 {
257*e5280830SGleb Smirnoff 	uint32_t	pat_buf_len;	/* pattern buffer length */
258*e5280830SGleb Smirnoff 	uint32_t	num_patterns;	/* number of patterns */
259*e5280830SGleb Smirnoff 	uint32_t	patterns;	/* pattern buffer
260*e5280830SGleb Smirnoff 					   (struct pci_match_conf_old32 *) */
261*e5280830SGleb Smirnoff 	uint32_t	match_buf_len;	/* match buffer length */
262*e5280830SGleb Smirnoff 	uint32_t	num_matches;	/* number of matches returned */
263*e5280830SGleb Smirnoff 	uint32_t	matches;	/* match buffer
264*e5280830SGleb Smirnoff 					   (struct pci_conf_old32 *) */
265*e5280830SGleb Smirnoff 	uint32_t	offset;		/* offset into device list */
266*e5280830SGleb Smirnoff 	uint32_t	generation;	/* device list generation */
267b01bf72bSMaxim Sobolev 	pci_getconf_status status;	/* request status */
268b01bf72bSMaxim Sobolev };
269b01bf72bSMaxim Sobolev 
270b01bf72bSMaxim Sobolev #define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
271904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
272b01bf72bSMaxim Sobolev 
27333d3fffaSMarius Strobl #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
27433d3fffaSMarius Strobl #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
27533d3fffaSMarius Strobl #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
27633d3fffaSMarius Strobl 
27733d3fffaSMarius Strobl static int	pci_conf_match_old(struct pci_match_conf_old *matches,
27833d3fffaSMarius Strobl 		    int num_matches, struct pci_conf *match_buf);
27933d3fffaSMarius Strobl 
28033d3fffaSMarius Strobl static int
28133d3fffaSMarius Strobl pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
28233d3fffaSMarius Strobl     struct pci_conf *match_buf)
28333d3fffaSMarius Strobl {
28433d3fffaSMarius Strobl 	int i;
28533d3fffaSMarius Strobl 
28633d3fffaSMarius Strobl 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
28733d3fffaSMarius Strobl 		return(1);
28833d3fffaSMarius Strobl 
28933d3fffaSMarius Strobl 	for (i = 0; i < num_matches; i++) {
29033d3fffaSMarius Strobl 		if (match_buf->pc_sel.pc_domain != 0)
29133d3fffaSMarius Strobl 			continue;
29233d3fffaSMarius Strobl 
29333d3fffaSMarius Strobl 		/*
29433d3fffaSMarius Strobl 		 * I'm not sure why someone would do this...but...
29533d3fffaSMarius Strobl 		 */
29633d3fffaSMarius Strobl 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
29733d3fffaSMarius Strobl 			continue;
29833d3fffaSMarius Strobl 
29933d3fffaSMarius Strobl 		/*
30033d3fffaSMarius Strobl 		 * Look at each of the match flags.  If it's set, do the
30133d3fffaSMarius Strobl 		 * comparison.  If the comparison fails, we don't have a
30233d3fffaSMarius Strobl 		 * match, go on to the next item if there is one.
30333d3fffaSMarius Strobl 		 */
30433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
30533d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
30633d3fffaSMarius Strobl 			continue;
30733d3fffaSMarius Strobl 
30833d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
30933d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
31033d3fffaSMarius Strobl 			continue;
31133d3fffaSMarius Strobl 
31233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
31333d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
31433d3fffaSMarius Strobl 			continue;
31533d3fffaSMarius Strobl 
31633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
31733d3fffaSMarius Strobl 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
31833d3fffaSMarius Strobl 			continue;
31933d3fffaSMarius Strobl 
32033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
32133d3fffaSMarius Strobl 		 && (match_buf->pc_device != matches[i].pc_device))
32233d3fffaSMarius Strobl 			continue;
32333d3fffaSMarius Strobl 
32433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
32533d3fffaSMarius Strobl 		 && (match_buf->pc_class != matches[i].pc_class))
32633d3fffaSMarius Strobl 			continue;
32733d3fffaSMarius Strobl 
32833d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
32933d3fffaSMarius Strobl 		 && (match_buf->pd_unit != matches[i].pd_unit))
33033d3fffaSMarius Strobl 			continue;
33133d3fffaSMarius Strobl 
33233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
33333d3fffaSMarius Strobl 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
33433d3fffaSMarius Strobl 			     sizeof(match_buf->pd_name)) != 0))
33533d3fffaSMarius Strobl 			continue;
33633d3fffaSMarius Strobl 
33733d3fffaSMarius Strobl 		return(0);
33833d3fffaSMarius Strobl 	}
33933d3fffaSMarius Strobl 
34033d3fffaSMarius Strobl 	return(1);
34133d3fffaSMarius Strobl }
34233d3fffaSMarius Strobl 
343904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
344b01bf72bSMaxim Sobolev static int
345b01bf72bSMaxim Sobolev pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
346b01bf72bSMaxim Sobolev     struct pci_conf *match_buf)
347b01bf72bSMaxim Sobolev {
348b01bf72bSMaxim Sobolev 	int i;
349b01bf72bSMaxim Sobolev 
350b01bf72bSMaxim Sobolev 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
351b01bf72bSMaxim Sobolev 		return(1);
352b01bf72bSMaxim Sobolev 
353b01bf72bSMaxim Sobolev 	for (i = 0; i < num_matches; i++) {
354b01bf72bSMaxim Sobolev 		if (match_buf->pc_sel.pc_domain != 0)
355b01bf72bSMaxim Sobolev 			continue;
356b01bf72bSMaxim Sobolev 
357b01bf72bSMaxim Sobolev 		/*
358b01bf72bSMaxim Sobolev 		 * I'm not sure why someone would do this...but...
359b01bf72bSMaxim Sobolev 		 */
360b01bf72bSMaxim Sobolev 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
361b01bf72bSMaxim Sobolev 			continue;
362b01bf72bSMaxim Sobolev 
363b01bf72bSMaxim Sobolev 		/*
364b01bf72bSMaxim Sobolev 		 * Look at each of the match flags.  If it's set, do the
365b01bf72bSMaxim Sobolev 		 * comparison.  If the comparison fails, we don't have a
366b01bf72bSMaxim Sobolev 		 * match, go on to the next item if there is one.
367b01bf72bSMaxim Sobolev 		 */
368*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
369*e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
370b01bf72bSMaxim Sobolev 			continue;
371b01bf72bSMaxim Sobolev 
372*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
373*e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
374b01bf72bSMaxim Sobolev 			continue;
375b01bf72bSMaxim Sobolev 
376*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
377*e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
378b01bf72bSMaxim Sobolev 			continue;
379b01bf72bSMaxim Sobolev 
380*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
381*e5280830SGleb Smirnoff 		    (match_buf->pc_vendor != matches[i].pc_vendor))
382b01bf72bSMaxim Sobolev 			continue;
383b01bf72bSMaxim Sobolev 
384*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
385*e5280830SGleb Smirnoff 		    (match_buf->pc_device != matches[i].pc_device))
386b01bf72bSMaxim Sobolev 			continue;
387b01bf72bSMaxim Sobolev 
388*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
389*e5280830SGleb Smirnoff 		    (match_buf->pc_class != matches[i].pc_class))
390b01bf72bSMaxim Sobolev 			continue;
391b01bf72bSMaxim Sobolev 
392*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
393*e5280830SGleb Smirnoff 		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
394b01bf72bSMaxim Sobolev 			continue;
395b01bf72bSMaxim Sobolev 
396*e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
397*e5280830SGleb Smirnoff 		    (strncmp(matches[i].pd_name, match_buf->pd_name,
398b01bf72bSMaxim Sobolev 		    sizeof(match_buf->pd_name)) != 0))
399b01bf72bSMaxim Sobolev 			continue;
400b01bf72bSMaxim Sobolev 
401b01bf72bSMaxim Sobolev 		return (0);
402b01bf72bSMaxim Sobolev 	}
403b01bf72bSMaxim Sobolev 
404b01bf72bSMaxim Sobolev 	return (1);
405b01bf72bSMaxim Sobolev }
406904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
407904c3909SGleb Smirnoff #endif	/* PRE7_COMPAT */
40833d3fffaSMarius Strobl 
4098983cfbfSMike Smith static int
41089c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
4118983cfbfSMike Smith {
41233d3fffaSMarius Strobl 	device_t pcidev, brdev;
41333d3fffaSMarius Strobl 	void *confdata;
4148983cfbfSMike Smith 	const char *name;
41533d3fffaSMarius Strobl 	struct devlist *devlist_head;
416904c3909SGleb Smirnoff 	struct pci_conf_io *cio = NULL;
41733d3fffaSMarius Strobl 	struct pci_devinfo *dinfo;
41833d3fffaSMarius Strobl 	struct pci_io *io;
419da1e0915SJohn Baldwin 	struct pci_bar_io *bio;
42033d3fffaSMarius Strobl 	struct pci_match_conf *pattern_buf;
421a90dd577SJohn Baldwin 	struct pci_map *pm;
42233d3fffaSMarius Strobl 	size_t confsz, iolen, pbufsz;
42333d3fffaSMarius Strobl 	int error, ionum, i, num_patterns;
424b2068c0cSWarner Losh #ifdef PRE7_COMPAT
425b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
426904c3909SGleb Smirnoff 	struct pci_conf_io32 *cio32 = NULL;
427904c3909SGleb Smirnoff 	struct pci_conf_old32 conf_old32;
428904c3909SGleb Smirnoff 	struct pci_match_conf_old32 *pattern_buf_old32;
429b01bf72bSMaxim Sobolev #endif
43033d3fffaSMarius Strobl 	struct pci_conf_old conf_old;
43133d3fffaSMarius Strobl 	struct pci_io iodata;
43233d3fffaSMarius Strobl 	struct pci_io_old *io_old;
43333d3fffaSMarius Strobl 	struct pci_match_conf_old *pattern_buf_old;
4348983cfbfSMike Smith 
43533d3fffaSMarius Strobl 	io_old = NULL;
43633d3fffaSMarius Strobl 
437da1e0915SJohn Baldwin 	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
438da1e0915SJohn Baldwin 	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
43933d3fffaSMarius Strobl 		return EPERM;
44033d3fffaSMarius Strobl #else
441da1e0915SJohn Baldwin 	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
4428983cfbfSMike Smith 		return EPERM;
44333d3fffaSMarius Strobl #endif
4448983cfbfSMike Smith 
4458983cfbfSMike Smith 	switch(cmd) {
446b2068c0cSWarner Losh #ifdef PRE7_COMPAT
447904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
448b01bf72bSMaxim Sobolev        case PCIOCGETCONF_OLD32:
449b01bf72bSMaxim Sobolev                cio32 = (struct pci_conf_io32 *)data;
450b01bf72bSMaxim Sobolev                cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
451b01bf72bSMaxim Sobolev                cio->pat_buf_len = cio32->pat_buf_len;
452b01bf72bSMaxim Sobolev                cio->num_patterns = cio32->num_patterns;
453b01bf72bSMaxim Sobolev                cio->patterns = (void *)(uintptr_t)cio32->patterns;
454b01bf72bSMaxim Sobolev                cio->match_buf_len = cio32->match_buf_len;
455b01bf72bSMaxim Sobolev                cio->num_matches = cio32->num_matches;
456b01bf72bSMaxim Sobolev                cio->matches = (void *)(uintptr_t)cio32->matches;
457b01bf72bSMaxim Sobolev                cio->offset = cio32->offset;
458b01bf72bSMaxim Sobolev                cio->generation = cio32->generation;
459b01bf72bSMaxim Sobolev                cio->status = cio32->status;
460b01bf72bSMaxim Sobolev                cio32->num_matches = 0;
461904c3909SGleb Smirnoff                break;
462904c3909SGleb Smirnoff #endif
46333d3fffaSMarius Strobl 	case PCIOCGETCONF_OLD:
46433d3fffaSMarius Strobl #endif
4658983cfbfSMike Smith 	case PCIOCGETCONF:
4668983cfbfSMike Smith 		cio = (struct pci_conf_io *)data;
467904c3909SGleb Smirnoff 	}
468904c3909SGleb Smirnoff 
469904c3909SGleb Smirnoff 	switch(cmd) {
470904c3909SGleb Smirnoff #ifdef PRE7_COMPAT
471904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
472904c3909SGleb Smirnoff 	case PCIOCGETCONF_OLD32:
473904c3909SGleb Smirnoff 		pattern_buf_old32 = NULL;
474904c3909SGleb Smirnoff #endif
475904c3909SGleb Smirnoff 	case PCIOCGETCONF_OLD:
476904c3909SGleb Smirnoff 		pattern_buf_old = NULL;
477904c3909SGleb Smirnoff #endif
478904c3909SGleb Smirnoff 	case PCIOCGETCONF:
4798983cfbfSMike Smith 
48033d3fffaSMarius Strobl 		pattern_buf = NULL;
4818983cfbfSMike Smith 		num_patterns = 0;
4828983cfbfSMike Smith 		dinfo = NULL;
4838983cfbfSMike Smith 
48433d3fffaSMarius Strobl 		cio->num_matches = 0;
48533d3fffaSMarius Strobl 
4868983cfbfSMike Smith 		/*
4878983cfbfSMike Smith 		 * If the user specified an offset into the device list,
4888983cfbfSMike Smith 		 * but the list has changed since they last called this
4898983cfbfSMike Smith 		 * ioctl, tell them that the list has changed.  They will
4908983cfbfSMike Smith 		 * have to get the list from the beginning.
4918983cfbfSMike Smith 		 */
4928983cfbfSMike Smith 		if ((cio->offset != 0)
4938983cfbfSMike Smith 		 && (cio->generation != pci_generation)){
4948983cfbfSMike Smith 			cio->status = PCI_GETCONF_LIST_CHANGED;
4958983cfbfSMike Smith 			error = 0;
496b01bf72bSMaxim Sobolev 			goto getconfexit;
4978983cfbfSMike Smith 		}
4988983cfbfSMike Smith 
4998983cfbfSMike Smith 		/*
5008983cfbfSMike Smith 		 * Check to see whether the user has asked for an offset
5018983cfbfSMike Smith 		 * past the end of our list.
5028983cfbfSMike Smith 		 */
5038983cfbfSMike Smith 		if (cio->offset >= pci_numdevs) {
5048983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
5058983cfbfSMike Smith 			error = 0;
506b01bf72bSMaxim Sobolev 			goto getconfexit;
5078983cfbfSMike Smith 		}
5088983cfbfSMike Smith 
5098983cfbfSMike Smith 		/* get the head of the device queue */
5108983cfbfSMike Smith 		devlist_head = &pci_devq;
5118983cfbfSMike Smith 
5128983cfbfSMike Smith 		/*
5138983cfbfSMike Smith 		 * Determine how much room we have for pci_conf structures.
5148983cfbfSMike Smith 		 * Round the user's buffer size down to the nearest
5158983cfbfSMike Smith 		 * multiple of sizeof(struct pci_conf) in case the user
5168983cfbfSMike Smith 		 * didn't specify a multiple of that size.
5178983cfbfSMike Smith 		 */
518b2068c0cSWarner Losh #ifdef PRE7_COMPAT
519b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
520b01bf72bSMaxim Sobolev 		if (cmd == PCIOCGETCONF_OLD32)
521b01bf72bSMaxim Sobolev 			confsz = sizeof(struct pci_conf_old32);
522b01bf72bSMaxim Sobolev 		else
523b01bf72bSMaxim Sobolev #endif
52433d3fffaSMarius Strobl 		if (cmd == PCIOCGETCONF_OLD)
52533d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf_old);
52633d3fffaSMarius Strobl 		else
52733d3fffaSMarius Strobl #endif
52833d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf);
52933d3fffaSMarius Strobl 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
53033d3fffaSMarius Strobl 		    pci_numdevs * confsz);
5318983cfbfSMike Smith 
5328983cfbfSMike Smith 		/*
5338983cfbfSMike Smith 		 * Since we know that iolen is a multiple of the size of
5348983cfbfSMike Smith 		 * the pciconf union, it's okay to do this.
5358983cfbfSMike Smith 		 */
53633d3fffaSMarius Strobl 		ionum = iolen / confsz;
5378983cfbfSMike Smith 
5388983cfbfSMike Smith 		/*
5398983cfbfSMike Smith 		 * If this test is true, the user wants the pci_conf
5408983cfbfSMike Smith 		 * structures returned to match the supplied entries.
5418983cfbfSMike Smith 		 */
542e3f932deSJohn-Mark Gurney 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
5438983cfbfSMike Smith 		 && (cio->pat_buf_len > 0)) {
5448983cfbfSMike Smith 			/*
5458983cfbfSMike Smith 			 * pat_buf_len needs to be:
5468983cfbfSMike Smith 			 * num_patterns * sizeof(struct pci_match_conf)
5478983cfbfSMike Smith 			 * While it is certainly possible the user just
5488983cfbfSMike Smith 			 * allocated a large buffer, but set the number of
5498983cfbfSMike Smith 			 * matches correctly, it is far more likely that
5508983cfbfSMike Smith 			 * their kernel doesn't match the userland utility
5518983cfbfSMike Smith 			 * they're using.  It's also possible that the user
5528983cfbfSMike Smith 			 * forgot to initialize some variables.  Yes, this
5538983cfbfSMike Smith 			 * may be overly picky, but I hazard to guess that
5548983cfbfSMike Smith 			 * it's far more likely to just catch folks that
5558983cfbfSMike Smith 			 * updated their kernel but not their userland.
5568983cfbfSMike Smith 			 */
557b2068c0cSWarner Losh #ifdef PRE7_COMPAT
558b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
559b01bf72bSMaxim Sobolev 			if (cmd == PCIOCGETCONF_OLD32)
560b01bf72bSMaxim Sobolev 				pbufsz = sizeof(struct pci_match_conf_old32);
561b01bf72bSMaxim Sobolev 			else
562b01bf72bSMaxim Sobolev #endif
56333d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD)
56433d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf_old);
56533d3fffaSMarius Strobl 			else
56633d3fffaSMarius Strobl #endif
56733d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf);
56833d3fffaSMarius Strobl 			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
56933d3fffaSMarius Strobl 				/* The user made a mistake, return an error. */
5708983cfbfSMike Smith 				cio->status = PCI_GETCONF_ERROR;
5718983cfbfSMike Smith 				error = EINVAL;
572b01bf72bSMaxim Sobolev 				goto getconfexit;
5738983cfbfSMike Smith 			}
5748983cfbfSMike Smith 
5758983cfbfSMike Smith 			/*
5768983cfbfSMike Smith 			 * Allocate a buffer to hold the patterns.
5778983cfbfSMike Smith 			 */
578b2068c0cSWarner Losh #ifdef PRE7_COMPAT
579904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
580b01bf72bSMaxim Sobolev 			if (cmd == PCIOCGETCONF_OLD32) {
581b01bf72bSMaxim Sobolev 				pattern_buf_old32 = malloc(cio->pat_buf_len,
582b01bf72bSMaxim Sobolev 				    M_TEMP, M_WAITOK);
583b01bf72bSMaxim Sobolev 				error = copyin(cio->patterns,
584b01bf72bSMaxim Sobolev 				    pattern_buf_old32, cio->pat_buf_len);
585b01bf72bSMaxim Sobolev 			} else
586904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */
58733d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD) {
58833d3fffaSMarius Strobl 				pattern_buf_old = malloc(cio->pat_buf_len,
58933d3fffaSMarius Strobl 				    M_TEMP, M_WAITOK);
59033d3fffaSMarius Strobl 				error = copyin(cio->patterns,
59133d3fffaSMarius Strobl 				    pattern_buf_old, cio->pat_buf_len);
59233d3fffaSMarius Strobl 			} else
593904c3909SGleb Smirnoff #endif /* PRE7_COMPAT */
59433d3fffaSMarius Strobl 			{
5958983cfbfSMike Smith 				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
596a163d034SWarner Losh 				    M_WAITOK);
5978983cfbfSMike Smith 				error = copyin(cio->patterns, pattern_buf,
5988983cfbfSMike Smith 				    cio->pat_buf_len);
59933d3fffaSMarius Strobl 			}
600d08239c1SJohn-Mark Gurney 			if (error != 0) {
601d08239c1SJohn-Mark Gurney 				error = EINVAL;
602d08239c1SJohn-Mark Gurney 				goto getconfexit;
603d08239c1SJohn-Mark Gurney 			}
6048983cfbfSMike Smith 			num_patterns = cio->num_patterns;
6058983cfbfSMike Smith 		} else if ((cio->num_patterns > 0)
6068983cfbfSMike Smith 			|| (cio->pat_buf_len > 0)) {
6078983cfbfSMike Smith 			/*
6088983cfbfSMike Smith 			 * The user made a mistake, spit out an error.
6098983cfbfSMike Smith 			 */
6108983cfbfSMike Smith 			cio->status = PCI_GETCONF_ERROR;
6118983cfbfSMike Smith 			error = EINVAL;
612b01bf72bSMaxim Sobolev                        goto getconfexit;
61333d3fffaSMarius Strobl 		}
6148983cfbfSMike Smith 
6158983cfbfSMike Smith 		/*
6168983cfbfSMike Smith 		 * Go through the list of devices and copy out the devices
6178983cfbfSMike Smith 		 * that match the user's criteria.
6188983cfbfSMike Smith 		 */
6198983cfbfSMike Smith 		for (cio->num_matches = 0, error = 0, i = 0,
6208983cfbfSMike Smith 		     dinfo = STAILQ_FIRST(devlist_head);
6218983cfbfSMike Smith 		     (dinfo != NULL) && (cio->num_matches < ionum)
622d08239c1SJohn-Mark Gurney 		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
6238983cfbfSMike Smith 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
6248983cfbfSMike Smith 
6258983cfbfSMike Smith 			if (i < cio->offset)
6268983cfbfSMike Smith 				continue;
6278983cfbfSMike Smith 
6288983cfbfSMike Smith 			/* Populate pd_name and pd_unit */
6298983cfbfSMike Smith 			name = NULL;
6300678f786SJohn Baldwin 			if (dinfo->cfg.dev)
6318983cfbfSMike Smith 				name = device_get_name(dinfo->cfg.dev);
6328983cfbfSMike Smith 			if (name) {
6338983cfbfSMike Smith 				strncpy(dinfo->conf.pd_name, name,
6348983cfbfSMike Smith 					sizeof(dinfo->conf.pd_name));
6358983cfbfSMike Smith 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
6368983cfbfSMike Smith 				dinfo->conf.pd_unit =
6378983cfbfSMike Smith 					device_get_unit(dinfo->cfg.dev);
6380678f786SJohn Baldwin 			} else {
6390678f786SJohn Baldwin 				dinfo->conf.pd_name[0] = '\0';
6400678f786SJohn Baldwin 				dinfo->conf.pd_unit = 0;
6418983cfbfSMike Smith 			}
6428983cfbfSMike Smith 
643b2068c0cSWarner Losh #ifdef PRE7_COMPAT
644904c3909SGleb Smirnoff 			if (
645904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
646904c3909SGleb Smirnoff 			    (cmd == PCIOCGETCONF_OLD32 &&
647b01bf72bSMaxim Sobolev 			    (pattern_buf_old32 == NULL ||
648b01bf72bSMaxim Sobolev 			    pci_conf_match_old32(pattern_buf_old32,
649b01bf72bSMaxim Sobolev 			    num_patterns, &dinfo->conf) == 0)) ||
650904c3909SGleb Smirnoff #endif
651b01bf72bSMaxim Sobolev 			    (cmd == PCIOCGETCONF_OLD &&
65233d3fffaSMarius Strobl 			    (pattern_buf_old == NULL ||
65333d3fffaSMarius Strobl 			    pci_conf_match_old(pattern_buf_old, num_patterns,
65433d3fffaSMarius Strobl 			    &dinfo->conf) == 0)) ||
65533d3fffaSMarius Strobl 			    (cmd == PCIOCGETCONF &&
65633d3fffaSMarius Strobl 			    (pattern_buf == NULL ||
65733d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
65833d3fffaSMarius Strobl 			    &dinfo->conf) == 0))) {
65933d3fffaSMarius Strobl #else
66033d3fffaSMarius Strobl 			if (pattern_buf == NULL ||
66133d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
66233d3fffaSMarius Strobl 			    &dinfo->conf) == 0) {
66333d3fffaSMarius Strobl #endif
6648983cfbfSMike Smith 				/*
6658983cfbfSMike Smith 				 * If we've filled up the user's buffer,
6668983cfbfSMike Smith 				 * break out at this point.  Since we've
6678983cfbfSMike Smith 				 * got a match here, we'll pick right back
6688983cfbfSMike Smith 				 * up at the matching entry.  We can also
6698983cfbfSMike Smith 				 * tell the user that there are more matches
6708983cfbfSMike Smith 				 * left.
6718983cfbfSMike Smith 				 */
6728983cfbfSMike Smith 				if (cio->num_matches >= ionum)
6738983cfbfSMike Smith 					break;
6748983cfbfSMike Smith 
675b2068c0cSWarner Losh #ifdef PRE7_COMPAT
676904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
677b01bf72bSMaxim Sobolev 				if (cmd == PCIOCGETCONF_OLD32) {
678b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_bus =
679b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_bus;
680b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_dev =
681b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_dev;
682b01bf72bSMaxim Sobolev 					conf_old32.pc_sel.pc_func =
683b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_sel.pc_func;
684b01bf72bSMaxim Sobolev 					conf_old32.pc_hdr = dinfo->conf.pc_hdr;
685b01bf72bSMaxim Sobolev 					conf_old32.pc_subvendor =
686b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subvendor;
687b01bf72bSMaxim Sobolev 					conf_old32.pc_subdevice =
688b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subdevice;
689b01bf72bSMaxim Sobolev 					conf_old32.pc_vendor =
690b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_vendor;
691b01bf72bSMaxim Sobolev 					conf_old32.pc_device =
692b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_device;
693b01bf72bSMaxim Sobolev 					conf_old32.pc_class =
694b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_class;
695b01bf72bSMaxim Sobolev 					conf_old32.pc_subclass =
696b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_subclass;
697b01bf72bSMaxim Sobolev 					conf_old32.pc_progif =
698b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_progif;
699b01bf72bSMaxim Sobolev 					conf_old32.pc_revid =
700b01bf72bSMaxim Sobolev 					    dinfo->conf.pc_revid;
701b01bf72bSMaxim Sobolev 					strncpy(conf_old32.pd_name,
702b01bf72bSMaxim Sobolev 					    dinfo->conf.pd_name,
703b01bf72bSMaxim Sobolev 					    sizeof(conf_old32.pd_name));
704b01bf72bSMaxim Sobolev 					conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
705b01bf72bSMaxim Sobolev 					conf_old32.pd_unit =
706*e5280830SGleb Smirnoff 					    (uint32_t)dinfo->conf.pd_unit;
707b01bf72bSMaxim Sobolev 					confdata = &conf_old32;
708b01bf72bSMaxim Sobolev 				} else
709904c3909SGleb Smirnoff #endif /* COMPAT_FREEBSD32 */
71033d3fffaSMarius Strobl 				if (cmd == PCIOCGETCONF_OLD) {
71133d3fffaSMarius Strobl 					conf_old.pc_sel.pc_bus =
71233d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_bus;
71333d3fffaSMarius Strobl 					conf_old.pc_sel.pc_dev =
71433d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_dev;
71533d3fffaSMarius Strobl 					conf_old.pc_sel.pc_func =
71633d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_func;
71733d3fffaSMarius Strobl 					conf_old.pc_hdr = dinfo->conf.pc_hdr;
71833d3fffaSMarius Strobl 					conf_old.pc_subvendor =
71933d3fffaSMarius Strobl 					    dinfo->conf.pc_subvendor;
72033d3fffaSMarius Strobl 					conf_old.pc_subdevice =
72133d3fffaSMarius Strobl 					    dinfo->conf.pc_subdevice;
72233d3fffaSMarius Strobl 					conf_old.pc_vendor =
72333d3fffaSMarius Strobl 					    dinfo->conf.pc_vendor;
72433d3fffaSMarius Strobl 					conf_old.pc_device =
72533d3fffaSMarius Strobl 					    dinfo->conf.pc_device;
72633d3fffaSMarius Strobl 					conf_old.pc_class =
72733d3fffaSMarius Strobl 					    dinfo->conf.pc_class;
72833d3fffaSMarius Strobl 					conf_old.pc_subclass =
72933d3fffaSMarius Strobl 					    dinfo->conf.pc_subclass;
73033d3fffaSMarius Strobl 					conf_old.pc_progif =
73133d3fffaSMarius Strobl 					    dinfo->conf.pc_progif;
73233d3fffaSMarius Strobl 					conf_old.pc_revid =
73333d3fffaSMarius Strobl 					    dinfo->conf.pc_revid;
734c5860546SMarius Strobl 					strncpy(conf_old.pd_name,
735c5860546SMarius Strobl 					    dinfo->conf.pd_name,
73633d3fffaSMarius Strobl 					    sizeof(conf_old.pd_name));
737c5860546SMarius Strobl 					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
73833d3fffaSMarius Strobl 					conf_old.pd_unit =
73933d3fffaSMarius Strobl 					    dinfo->conf.pd_unit;
74033d3fffaSMarius Strobl 					confdata = &conf_old;
74133d3fffaSMarius Strobl 				} else
742904c3909SGleb Smirnoff #endif /* PRE7_COMPAT */
74333d3fffaSMarius Strobl 					confdata = &dinfo->conf;
74433d3fffaSMarius Strobl 				/* Only if we can copy it out do we count it. */
74533d3fffaSMarius Strobl 				if (!(error = copyout(confdata,
746c5860546SMarius Strobl 				    (caddr_t)cio->matches +
747c5860546SMarius Strobl 				    confsz * cio->num_matches, confsz)))
74833d3fffaSMarius Strobl 					cio->num_matches++;
7498983cfbfSMike Smith 			}
750d08239c1SJohn-Mark Gurney 		}
7518983cfbfSMike Smith 
7528983cfbfSMike Smith 		/*
7538983cfbfSMike Smith 		 * Set the pointer into the list, so if the user is getting
7548983cfbfSMike Smith 		 * n records at a time, where n < pci_numdevs,
7558983cfbfSMike Smith 		 */
7568983cfbfSMike Smith 		cio->offset = i;
7578983cfbfSMike Smith 
7588983cfbfSMike Smith 		/*
7598983cfbfSMike Smith 		 * Set the generation, the user will need this if they make
7608983cfbfSMike Smith 		 * another ioctl call with offset != 0.
7618983cfbfSMike Smith 		 */
7628983cfbfSMike Smith 		cio->generation = pci_generation;
7638983cfbfSMike Smith 
7648983cfbfSMike Smith 		/*
7658983cfbfSMike Smith 		 * If this is the last device, inform the user so he won't
7668983cfbfSMike Smith 		 * bother asking for more devices.  If dinfo isn't NULL, we
7678983cfbfSMike Smith 		 * know that there are more matches in the list because of
7688983cfbfSMike Smith 		 * the way the traversal is done.
7698983cfbfSMike Smith 		 */
7708983cfbfSMike Smith 		if (dinfo == NULL)
7718983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
7728983cfbfSMike Smith 		else
7738983cfbfSMike Smith 			cio->status = PCI_GETCONF_MORE_DEVS;
7748983cfbfSMike Smith 
775d08239c1SJohn-Mark Gurney getconfexit:
776904c3909SGleb Smirnoff #ifdef PRE7_COMPAT
777b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
778b01bf72bSMaxim Sobolev 		if (cmd == PCIOCGETCONF_OLD32) {
779b01bf72bSMaxim Sobolev 			cio32->status = cio->status;
780b01bf72bSMaxim Sobolev 			cio32->generation = cio->generation;
781b01bf72bSMaxim Sobolev 			cio32->offset = cio->offset;
782b01bf72bSMaxim Sobolev 			cio32->num_matches = cio->num_matches;
783b01bf72bSMaxim Sobolev 			free(cio, M_TEMP);
784b01bf72bSMaxim Sobolev 		}
785b01bf72bSMaxim Sobolev 		if (pattern_buf_old32 != NULL)
786b01bf72bSMaxim Sobolev 			free(pattern_buf_old32, M_TEMP);
787904c3909SGleb Smirnoff #endif
78833d3fffaSMarius Strobl 		if (pattern_buf_old != NULL)
78933d3fffaSMarius Strobl 			free(pattern_buf_old, M_TEMP);
79033d3fffaSMarius Strobl #endif
791904c3909SGleb Smirnoff 		if (pattern_buf != NULL)
792904c3909SGleb Smirnoff 			free(pattern_buf, M_TEMP);
7938983cfbfSMike Smith 
7948983cfbfSMike Smith 		break;
7958983cfbfSMike Smith 
796b2068c0cSWarner Losh #ifdef PRE7_COMPAT
79733d3fffaSMarius Strobl 	case PCIOCREAD_OLD:
79833d3fffaSMarius Strobl 	case PCIOCWRITE_OLD:
79933d3fffaSMarius Strobl 		io_old = (struct pci_io_old *)data;
80033d3fffaSMarius Strobl 		iodata.pi_sel.pc_domain = 0;
80133d3fffaSMarius Strobl 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
80233d3fffaSMarius Strobl 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
80333d3fffaSMarius Strobl 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
80433d3fffaSMarius Strobl 		iodata.pi_reg = io_old->pi_reg;
80533d3fffaSMarius Strobl 		iodata.pi_width = io_old->pi_width;
80633d3fffaSMarius Strobl 		iodata.pi_data = io_old->pi_data;
80733d3fffaSMarius Strobl 		data = (caddr_t)&iodata;
80833d3fffaSMarius Strobl 		/* FALLTHROUGH */
80933d3fffaSMarius Strobl #endif
81066f314b5SStefan Eßer 	case PCIOCREAD:
8118983cfbfSMike Smith 	case PCIOCWRITE:
8128983cfbfSMike Smith 		io = (struct pci_io *)data;
8138983cfbfSMike Smith 		switch(io->pi_width) {
8148983cfbfSMike Smith 		case 4:
8158983cfbfSMike Smith 		case 2:
8168983cfbfSMike Smith 		case 1:
817d16d35fdSAndriy Gapon 			/* Make sure register is not negative and aligned. */
81833d3fffaSMarius Strobl 			if (io->pi_reg < 0 ||
81933d3fffaSMarius Strobl 			    io->pi_reg & (io->pi_width - 1)) {
82066f314b5SStefan Eßer 				error = EINVAL;
82140ed3f47SRuslan Ermilov 				break;
82240ed3f47SRuslan Ermilov 			}
8238983cfbfSMike Smith 			/*
8248983cfbfSMike Smith 			 * Assume that the user-level bus number is
82537ce43b7SBruce M Simpson 			 * in fact the physical PCI bus number.
82637ce43b7SBruce M Simpson 			 * Look up the grandparent, i.e. the bridge device,
82737ce43b7SBruce M Simpson 			 * so that we can issue configuration space cycles.
8288983cfbfSMike Smith 			 */
82955aaf894SMarius Strobl 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
83055aaf894SMarius Strobl 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
83155aaf894SMarius Strobl 			    io->pi_sel.pc_func);
83237ce43b7SBruce M Simpson 			if (pcidev) {
83333d3fffaSMarius Strobl 				brdev = device_get_parent(
83433d3fffaSMarius Strobl 				    device_get_parent(pcidev));
83537ce43b7SBruce M Simpson 
836b2068c0cSWarner Losh #ifdef PRE7_COMPAT
83733d3fffaSMarius Strobl 				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
83833d3fffaSMarius Strobl #else
83966f314b5SStefan Eßer 				if (cmd == PCIOCWRITE)
84033d3fffaSMarius Strobl #endif
84137ce43b7SBruce M Simpson 					PCIB_WRITE_CONFIG(brdev,
84237ce43b7SBruce M Simpson 							  io->pi_sel.pc_bus,
8438983cfbfSMike Smith 							  io->pi_sel.pc_dev,
8448983cfbfSMike Smith 							  io->pi_sel.pc_func,
8458983cfbfSMike Smith 							  io->pi_reg,
8468983cfbfSMike Smith 							  io->pi_data,
8478983cfbfSMike Smith 							  io->pi_width);
848b2068c0cSWarner Losh #ifdef PRE7_COMPAT
84933d3fffaSMarius Strobl 				else if (cmd == PCIOCREAD_OLD)
85033d3fffaSMarius Strobl 					io_old->pi_data =
85133d3fffaSMarius Strobl 						PCIB_READ_CONFIG(brdev,
85233d3fffaSMarius Strobl 							  io->pi_sel.pc_bus,
85333d3fffaSMarius Strobl 							  io->pi_sel.pc_dev,
85433d3fffaSMarius Strobl 							  io->pi_sel.pc_func,
85533d3fffaSMarius Strobl 							  io->pi_reg,
85633d3fffaSMarius Strobl 							  io->pi_width);
85733d3fffaSMarius Strobl #endif
85866f314b5SStefan Eßer 				else
85966f314b5SStefan Eßer 					io->pi_data =
86037ce43b7SBruce M Simpson 						PCIB_READ_CONFIG(brdev,
86137ce43b7SBruce M Simpson 							  io->pi_sel.pc_bus,
86266f314b5SStefan Eßer 							  io->pi_sel.pc_dev,
86366f314b5SStefan Eßer 							  io->pi_sel.pc_func,
86466f314b5SStefan Eßer 							  io->pi_reg,
86566f314b5SStefan Eßer 							  io->pi_width);
8668983cfbfSMike Smith 				error = 0;
8678983cfbfSMike Smith 			} else {
8688910aa92SPaul Saab #ifdef COMPAT_FREEBSD4
86933d3fffaSMarius Strobl 				if (cmd == PCIOCREAD_OLD) {
87033d3fffaSMarius Strobl 					io_old->pi_data = -1;
8718910aa92SPaul Saab 					error = 0;
8728910aa92SPaul Saab 				} else
8738910aa92SPaul Saab #endif
8748983cfbfSMike Smith 					error = ENODEV;
8758983cfbfSMike Smith 			}
8768983cfbfSMike Smith 			break;
8778983cfbfSMike Smith 		default:
878d08239c1SJohn-Mark Gurney 			error = EINVAL;
8798983cfbfSMike Smith 			break;
8808983cfbfSMike Smith 		}
8818983cfbfSMike Smith 		break;
8828983cfbfSMike Smith 
883da1e0915SJohn Baldwin 	case PCIOCGETBAR:
884da1e0915SJohn Baldwin 		bio = (struct pci_bar_io *)data;
885da1e0915SJohn Baldwin 
886da1e0915SJohn Baldwin 		/*
887da1e0915SJohn Baldwin 		 * Assume that the user-level bus number is
888da1e0915SJohn Baldwin 		 * in fact the physical PCI bus number.
889da1e0915SJohn Baldwin 		 */
890da1e0915SJohn Baldwin 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
891da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
892da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_func);
893da1e0915SJohn Baldwin 		if (pcidev == NULL) {
894da1e0915SJohn Baldwin 			error = ENODEV;
895da1e0915SJohn Baldwin 			break;
896da1e0915SJohn Baldwin 		}
897a90dd577SJohn Baldwin 		pm = pci_find_bar(pcidev, bio->pbi_reg);
898a90dd577SJohn Baldwin 		if (pm == NULL) {
899da1e0915SJohn Baldwin 			error = EINVAL;
900da1e0915SJohn Baldwin 			break;
901da1e0915SJohn Baldwin 		}
902a90dd577SJohn Baldwin 		bio->pbi_base = pm->pm_value;
903a90dd577SJohn Baldwin 		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
904a90dd577SJohn Baldwin 		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
905da1e0915SJohn Baldwin 		error = 0;
906da1e0915SJohn Baldwin 		break;
907762aad81SNeel Natu 	case PCIOCATTACHED:
908762aad81SNeel Natu 		error = 0;
909762aad81SNeel Natu 		io = (struct pci_io *)data;
910762aad81SNeel Natu 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
911762aad81SNeel Natu 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
912762aad81SNeel Natu 		if (pcidev != NULL)
913762aad81SNeel Natu 			io->pi_data = device_is_attached(pcidev);
914762aad81SNeel Natu 		else
915762aad81SNeel Natu 			error = ENODEV;
916762aad81SNeel Natu 		break;
9178983cfbfSMike Smith 	default:
9188983cfbfSMike Smith 		error = ENOTTY;
9198983cfbfSMike Smith 		break;
9208983cfbfSMike Smith 	}
9218983cfbfSMike Smith 
9228983cfbfSMike Smith 	return (error);
9238983cfbfSMike Smith }
924