xref: /freebsd/sys/dev/pci/pci_user.c (revision b01bf72b6e1ce532f721d54f5271ac5c0b8fb141)
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 
228*b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
229*b01bf72bSMaxim Sobolev struct pci_conf_old32 {
230*b01bf72bSMaxim Sobolev        struct pcisel_old pc_sel;       /* bus+slot+function */
231*b01bf72bSMaxim Sobolev        u_int8_t        pc_hdr;         /* PCI header type */
232*b01bf72bSMaxim Sobolev        u_int16_t       pc_subvendor;   /* card vendor ID */
233*b01bf72bSMaxim Sobolev        u_int16_t       pc_subdevice;   /* card device ID, assigned by
234*b01bf72bSMaxim Sobolev                                           card vendor */
235*b01bf72bSMaxim Sobolev        u_int16_t       pc_vendor;      /* chip vendor ID */
236*b01bf72bSMaxim Sobolev        u_int16_t       pc_device;      /* chip device ID, assigned by
237*b01bf72bSMaxim Sobolev                                           chip vendor */
238*b01bf72bSMaxim Sobolev        u_int8_t        pc_class;       /* chip PCI class */
239*b01bf72bSMaxim Sobolev        u_int8_t        pc_subclass;    /* chip PCI subclass */
240*b01bf72bSMaxim Sobolev        u_int8_t        pc_progif;      /* chip PCI programming interface */
241*b01bf72bSMaxim Sobolev        u_int8_t        pc_revid;       /* chip revision ID */
242*b01bf72bSMaxim Sobolev        char            pd_name[PCI_MAXNAMELEN + 1];  /* device name */
243*b01bf72bSMaxim Sobolev        u_int32_t       pd_unit;        /* device unit number (u_long) */
244*b01bf72bSMaxim Sobolev };
245*b01bf72bSMaxim Sobolev 
246*b01bf72bSMaxim Sobolev struct pci_match_conf_old32 {
247*b01bf72bSMaxim Sobolev        struct pcisel_old       pc_sel;         /* bus+slot+function */
248*b01bf72bSMaxim Sobolev        char                    pd_name[PCI_MAXNAMELEN + 1];  /* device name */
249*b01bf72bSMaxim Sobolev        u_int32_t               pd_unit;        /* Unit number (u_long) */
250*b01bf72bSMaxim Sobolev        u_int16_t               pc_vendor;      /* PCI Vendor ID */
251*b01bf72bSMaxim Sobolev        u_int16_t               pc_device;      /* PCI Device ID */
252*b01bf72bSMaxim Sobolev        u_int8_t                pc_class;       /* PCI class */
253*b01bf72bSMaxim Sobolev        pci_getconf_flags_old   flags;          /* Matching expression */
254*b01bf72bSMaxim Sobolev };
255*b01bf72bSMaxim Sobolev 
256*b01bf72bSMaxim Sobolev struct pci_conf_io32 {
257*b01bf72bSMaxim Sobolev        u_int32_t               pat_buf_len;    /* pattern buffer length */
258*b01bf72bSMaxim Sobolev        u_int32_t               num_patterns;   /* number of patterns */
259*b01bf72bSMaxim Sobolev        u_int32_t               patterns;       /* pattern buffer (struct pci_match_conf_old32 *) */
260*b01bf72bSMaxim Sobolev        u_int32_t               match_buf_len;  /* match buffer length */
261*b01bf72bSMaxim Sobolev        u_int32_t               num_matches;    /* number of matches returned */
262*b01bf72bSMaxim Sobolev        u_int32_t               matches;        /* match buffer (struct pci_conf_old32 *) */
263*b01bf72bSMaxim Sobolev        u_int32_t               offset;         /* offset into device list */
264*b01bf72bSMaxim Sobolev        u_int32_t               generation;     /* device list generation */
265*b01bf72bSMaxim Sobolev        pci_getconf_status      status;         /* request status */
266*b01bf72bSMaxim Sobolev };
267*b01bf72bSMaxim Sobolev 
268*b01bf72bSMaxim Sobolev #define        PCIOCGETCONF_OLD32      _IOWR('p', 1, struct pci_conf_io32)
269*b01bf72bSMaxim Sobolev #endif
270*b01bf72bSMaxim Sobolev 
27133d3fffaSMarius Strobl #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
27233d3fffaSMarius Strobl #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
27333d3fffaSMarius Strobl #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
27433d3fffaSMarius Strobl 
27533d3fffaSMarius Strobl static int	pci_conf_match_old(struct pci_match_conf_old *matches,
27633d3fffaSMarius Strobl 		    int num_matches, struct pci_conf *match_buf);
27733d3fffaSMarius Strobl 
27833d3fffaSMarius Strobl static int
27933d3fffaSMarius Strobl pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
28033d3fffaSMarius Strobl     struct pci_conf *match_buf)
28133d3fffaSMarius Strobl {
28233d3fffaSMarius Strobl 	int i;
28333d3fffaSMarius Strobl 
28433d3fffaSMarius Strobl 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
28533d3fffaSMarius Strobl 		return(1);
28633d3fffaSMarius Strobl 
28733d3fffaSMarius Strobl 	for (i = 0; i < num_matches; i++) {
28833d3fffaSMarius Strobl 		if (match_buf->pc_sel.pc_domain != 0)
28933d3fffaSMarius Strobl 			continue;
29033d3fffaSMarius Strobl 
29133d3fffaSMarius Strobl 		/*
29233d3fffaSMarius Strobl 		 * I'm not sure why someone would do this...but...
29333d3fffaSMarius Strobl 		 */
29433d3fffaSMarius Strobl 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
29533d3fffaSMarius Strobl 			continue;
29633d3fffaSMarius Strobl 
29733d3fffaSMarius Strobl 		/*
29833d3fffaSMarius Strobl 		 * Look at each of the match flags.  If it's set, do the
29933d3fffaSMarius Strobl 		 * comparison.  If the comparison fails, we don't have a
30033d3fffaSMarius Strobl 		 * match, go on to the next item if there is one.
30133d3fffaSMarius Strobl 		 */
30233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
30333d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
30433d3fffaSMarius Strobl 			continue;
30533d3fffaSMarius Strobl 
30633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
30733d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
30833d3fffaSMarius Strobl 			continue;
30933d3fffaSMarius Strobl 
31033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
31133d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
31233d3fffaSMarius Strobl 			continue;
31333d3fffaSMarius Strobl 
31433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
31533d3fffaSMarius Strobl 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
31633d3fffaSMarius Strobl 			continue;
31733d3fffaSMarius Strobl 
31833d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
31933d3fffaSMarius Strobl 		 && (match_buf->pc_device != matches[i].pc_device))
32033d3fffaSMarius Strobl 			continue;
32133d3fffaSMarius Strobl 
32233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
32333d3fffaSMarius Strobl 		 && (match_buf->pc_class != matches[i].pc_class))
32433d3fffaSMarius Strobl 			continue;
32533d3fffaSMarius Strobl 
32633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
32733d3fffaSMarius Strobl 		 && (match_buf->pd_unit != matches[i].pd_unit))
32833d3fffaSMarius Strobl 			continue;
32933d3fffaSMarius Strobl 
33033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
33133d3fffaSMarius Strobl 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
33233d3fffaSMarius Strobl 			     sizeof(match_buf->pd_name)) != 0))
33333d3fffaSMarius Strobl 			continue;
33433d3fffaSMarius Strobl 
33533d3fffaSMarius Strobl 		return(0);
33633d3fffaSMarius Strobl 	}
33733d3fffaSMarius Strobl 
33833d3fffaSMarius Strobl 	return(1);
33933d3fffaSMarius Strobl }
34033d3fffaSMarius Strobl 
341*b01bf72bSMaxim Sobolev static int
342*b01bf72bSMaxim Sobolev pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
343*b01bf72bSMaxim Sobolev     struct pci_conf *match_buf)
344*b01bf72bSMaxim Sobolev {
345*b01bf72bSMaxim Sobolev        int i;
346*b01bf72bSMaxim Sobolev 
347*b01bf72bSMaxim Sobolev        if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
348*b01bf72bSMaxim Sobolev                return(1);
349*b01bf72bSMaxim Sobolev 
350*b01bf72bSMaxim Sobolev        for (i = 0; i < num_matches; i++) {
351*b01bf72bSMaxim Sobolev                if (match_buf->pc_sel.pc_domain != 0)
352*b01bf72bSMaxim Sobolev                        continue;
353*b01bf72bSMaxim Sobolev 
354*b01bf72bSMaxim Sobolev                /*
355*b01bf72bSMaxim Sobolev                 * I'm not sure why someone would do this...but...
356*b01bf72bSMaxim Sobolev                 */
357*b01bf72bSMaxim Sobolev                if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
358*b01bf72bSMaxim Sobolev                        continue;
359*b01bf72bSMaxim Sobolev 
360*b01bf72bSMaxim Sobolev                /*
361*b01bf72bSMaxim Sobolev                 * Look at each of the match flags.  If it's set, do the
362*b01bf72bSMaxim Sobolev                 * comparison.  If the comparison fails, we don't have a
363*b01bf72bSMaxim Sobolev                 * match, go on to the next item if there is one.
364*b01bf72bSMaxim Sobolev                 */
365*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
366*b01bf72bSMaxim Sobolev                 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
367*b01bf72bSMaxim Sobolev                        continue;
368*b01bf72bSMaxim Sobolev 
369*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
370*b01bf72bSMaxim Sobolev                 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
371*b01bf72bSMaxim Sobolev                        continue;
372*b01bf72bSMaxim Sobolev 
373*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
374*b01bf72bSMaxim Sobolev                 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
375*b01bf72bSMaxim Sobolev                        continue;
376*b01bf72bSMaxim Sobolev 
377*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
378*b01bf72bSMaxim Sobolev                 && (match_buf->pc_vendor != matches[i].pc_vendor))
379*b01bf72bSMaxim Sobolev                        continue;
380*b01bf72bSMaxim Sobolev 
381*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
382*b01bf72bSMaxim Sobolev                 && (match_buf->pc_device != matches[i].pc_device))
383*b01bf72bSMaxim Sobolev                        continue;
384*b01bf72bSMaxim Sobolev 
385*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
386*b01bf72bSMaxim Sobolev                 && (match_buf->pc_class != matches[i].pc_class))
387*b01bf72bSMaxim Sobolev                        continue;
388*b01bf72bSMaxim Sobolev 
389*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
390*b01bf72bSMaxim Sobolev                 && ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
391*b01bf72bSMaxim Sobolev                        continue;
392*b01bf72bSMaxim Sobolev 
393*b01bf72bSMaxim Sobolev                if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
394*b01bf72bSMaxim Sobolev                 && (strncmp(matches[i].pd_name, match_buf->pd_name,
395*b01bf72bSMaxim Sobolev                             sizeof(match_buf->pd_name)) != 0))
396*b01bf72bSMaxim Sobolev                        continue;
397*b01bf72bSMaxim Sobolev 
398*b01bf72bSMaxim Sobolev                return(0);
399*b01bf72bSMaxim Sobolev        }
400*b01bf72bSMaxim Sobolev 
401*b01bf72bSMaxim Sobolev        return(1);
402*b01bf72bSMaxim Sobolev }
403*b01bf72bSMaxim Sobolev 
40433d3fffaSMarius Strobl #endif
40533d3fffaSMarius Strobl 
4068983cfbfSMike Smith static int
40789c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
4088983cfbfSMike Smith {
40933d3fffaSMarius Strobl 	device_t pcidev, brdev;
41033d3fffaSMarius Strobl 	void *confdata;
4118983cfbfSMike Smith 	const char *name;
41233d3fffaSMarius Strobl 	struct devlist *devlist_head;
41333d3fffaSMarius Strobl 	struct pci_conf_io *cio;
41433d3fffaSMarius Strobl 	struct pci_devinfo *dinfo;
41533d3fffaSMarius Strobl 	struct pci_io *io;
416da1e0915SJohn Baldwin 	struct pci_bar_io *bio;
41733d3fffaSMarius Strobl 	struct pci_match_conf *pattern_buf;
418a90dd577SJohn Baldwin 	struct pci_map *pm;
41933d3fffaSMarius Strobl 	size_t confsz, iolen, pbufsz;
42033d3fffaSMarius Strobl 	int error, ionum, i, num_patterns;
421b2068c0cSWarner Losh #ifdef PRE7_COMPAT
422*b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
423*b01bf72bSMaxim Sobolev        struct pci_conf_io32 *cio32;
424*b01bf72bSMaxim Sobolev #endif
42533d3fffaSMarius Strobl 	struct pci_conf_old conf_old;
426*b01bf72bSMaxim Sobolev        struct pci_conf_old32 conf_old32;
42733d3fffaSMarius Strobl 	struct pci_io iodata;
42833d3fffaSMarius Strobl 	struct pci_io_old *io_old;
42933d3fffaSMarius Strobl 	struct pci_match_conf_old *pattern_buf_old;
430*b01bf72bSMaxim Sobolev        struct pci_match_conf_old32 *pattern_buf_old32;
4318983cfbfSMike Smith 
432*b01bf72bSMaxim Sobolev        cio = NULL;
433*b01bf72bSMaxim Sobolev        cio32 = NULL;
43433d3fffaSMarius Strobl 	io_old = NULL;
43533d3fffaSMarius Strobl 	pattern_buf_old = NULL;
436*b01bf72bSMaxim Sobolev        pattern_buf_old32 = NULL;
43733d3fffaSMarius Strobl 
438da1e0915SJohn Baldwin 	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
439da1e0915SJohn Baldwin 	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
44033d3fffaSMarius Strobl 		return EPERM;
44133d3fffaSMarius Strobl #else
442da1e0915SJohn Baldwin 	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
4438983cfbfSMike Smith 		return EPERM;
44433d3fffaSMarius Strobl #endif
4458983cfbfSMike Smith 
4468983cfbfSMike Smith 	switch(cmd) {
447b2068c0cSWarner Losh #ifdef PRE7_COMPAT
448*b01bf72bSMaxim Sobolev        case PCIOCGETCONF_OLD32:
449*b01bf72bSMaxim Sobolev                cio32 = (struct pci_conf_io32 *)data;
450*b01bf72bSMaxim Sobolev                cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
451*b01bf72bSMaxim Sobolev                cio->pat_buf_len = cio32->pat_buf_len;
452*b01bf72bSMaxim Sobolev                cio->num_patterns = cio32->num_patterns;
453*b01bf72bSMaxim Sobolev                cio->patterns = (void *)(uintptr_t)cio32->patterns;
454*b01bf72bSMaxim Sobolev                cio->match_buf_len = cio32->match_buf_len;
455*b01bf72bSMaxim Sobolev                cio->num_matches = cio32->num_matches;
456*b01bf72bSMaxim Sobolev                cio->matches = (void *)(uintptr_t)cio32->matches;
457*b01bf72bSMaxim Sobolev                cio->offset = cio32->offset;
458*b01bf72bSMaxim Sobolev                cio->generation = cio32->generation;
459*b01bf72bSMaxim Sobolev                cio->status = cio32->status;
460*b01bf72bSMaxim Sobolev                cio32->num_matches = 0;
461*b01bf72bSMaxim Sobolev                /* FALLTHROUGH */
462*b01bf72bSMaxim Sobolev 
46333d3fffaSMarius Strobl 	case PCIOCGETCONF_OLD:
46433d3fffaSMarius Strobl 		/* FALLTHROUGH */
46533d3fffaSMarius Strobl #endif
4668983cfbfSMike Smith 	case PCIOCGETCONF:
467*b01bf72bSMaxim Sobolev                if (cio == NULL)
4688983cfbfSMike Smith                        cio = (struct pci_conf_io *)data;
4698983cfbfSMike Smith 
47033d3fffaSMarius Strobl 		pattern_buf = NULL;
4718983cfbfSMike Smith 		num_patterns = 0;
4728983cfbfSMike Smith 		dinfo = NULL;
4738983cfbfSMike Smith 
47433d3fffaSMarius Strobl 		cio->num_matches = 0;
47533d3fffaSMarius Strobl 
4768983cfbfSMike Smith 		/*
4778983cfbfSMike Smith 		 * If the user specified an offset into the device list,
4788983cfbfSMike Smith 		 * but the list has changed since they last called this
4798983cfbfSMike Smith 		 * ioctl, tell them that the list has changed.  They will
4808983cfbfSMike Smith 		 * have to get the list from the beginning.
4818983cfbfSMike Smith 		 */
4828983cfbfSMike Smith 		if ((cio->offset != 0)
4838983cfbfSMike Smith 		 && (cio->generation != pci_generation)){
4848983cfbfSMike Smith 			cio->status = PCI_GETCONF_LIST_CHANGED;
4858983cfbfSMike Smith 			error = 0;
486*b01bf72bSMaxim Sobolev                        goto getconfexit;
4878983cfbfSMike Smith 		}
4888983cfbfSMike Smith 
4898983cfbfSMike Smith 		/*
4908983cfbfSMike Smith 		 * Check to see whether the user has asked for an offset
4918983cfbfSMike Smith 		 * past the end of our list.
4928983cfbfSMike Smith 		 */
4938983cfbfSMike Smith 		if (cio->offset >= pci_numdevs) {
4948983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
4958983cfbfSMike Smith 			error = 0;
496*b01bf72bSMaxim Sobolev                        goto getconfexit;
4978983cfbfSMike Smith 		}
4988983cfbfSMike Smith 
4998983cfbfSMike Smith 		/* get the head of the device queue */
5008983cfbfSMike Smith 		devlist_head = &pci_devq;
5018983cfbfSMike Smith 
5028983cfbfSMike Smith 		/*
5038983cfbfSMike Smith 		 * Determine how much room we have for pci_conf structures.
5048983cfbfSMike Smith 		 * Round the user's buffer size down to the nearest
5058983cfbfSMike Smith 		 * multiple of sizeof(struct pci_conf) in case the user
5068983cfbfSMike Smith 		 * didn't specify a multiple of that size.
5078983cfbfSMike Smith 		 */
508b2068c0cSWarner Losh #ifdef PRE7_COMPAT
509*b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
510*b01bf72bSMaxim Sobolev                if (cmd == PCIOCGETCONF_OLD32)
511*b01bf72bSMaxim Sobolev                        confsz = sizeof(struct pci_conf_old32);
512*b01bf72bSMaxim Sobolev                else
513*b01bf72bSMaxim Sobolev #endif
51433d3fffaSMarius Strobl 		if (cmd == PCIOCGETCONF_OLD)
51533d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf_old);
51633d3fffaSMarius Strobl 		else
51733d3fffaSMarius Strobl #endif
51833d3fffaSMarius Strobl 			confsz = sizeof(struct pci_conf);
51933d3fffaSMarius Strobl 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
52033d3fffaSMarius Strobl 		    pci_numdevs * confsz);
5218983cfbfSMike Smith 
5228983cfbfSMike Smith 		/*
5238983cfbfSMike Smith 		 * Since we know that iolen is a multiple of the size of
5248983cfbfSMike Smith 		 * the pciconf union, it's okay to do this.
5258983cfbfSMike Smith 		 */
52633d3fffaSMarius Strobl 		ionum = iolen / confsz;
5278983cfbfSMike Smith 
5288983cfbfSMike Smith 		/*
5298983cfbfSMike Smith 		 * If this test is true, the user wants the pci_conf
5308983cfbfSMike Smith 		 * structures returned to match the supplied entries.
5318983cfbfSMike Smith 		 */
532e3f932deSJohn-Mark Gurney 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
5338983cfbfSMike Smith 		 && (cio->pat_buf_len > 0)) {
5348983cfbfSMike Smith 			/*
5358983cfbfSMike Smith 			 * pat_buf_len needs to be:
5368983cfbfSMike Smith 			 * num_patterns * sizeof(struct pci_match_conf)
5378983cfbfSMike Smith 			 * While it is certainly possible the user just
5388983cfbfSMike Smith 			 * allocated a large buffer, but set the number of
5398983cfbfSMike Smith 			 * matches correctly, it is far more likely that
5408983cfbfSMike Smith 			 * their kernel doesn't match the userland utility
5418983cfbfSMike Smith 			 * they're using.  It's also possible that the user
5428983cfbfSMike Smith 			 * forgot to initialize some variables.  Yes, this
5438983cfbfSMike Smith 			 * may be overly picky, but I hazard to guess that
5448983cfbfSMike Smith 			 * it's far more likely to just catch folks that
5458983cfbfSMike Smith 			 * updated their kernel but not their userland.
5468983cfbfSMike Smith 			 */
547b2068c0cSWarner Losh #ifdef PRE7_COMPAT
548*b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
549*b01bf72bSMaxim Sobolev                        if (cmd == PCIOCGETCONF_OLD32)
550*b01bf72bSMaxim Sobolev                                pbufsz = sizeof(struct pci_match_conf_old32);
551*b01bf72bSMaxim Sobolev                        else
552*b01bf72bSMaxim Sobolev #endif
55333d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD)
55433d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf_old);
55533d3fffaSMarius Strobl 			else
55633d3fffaSMarius Strobl #endif
55733d3fffaSMarius Strobl 				pbufsz = sizeof(struct pci_match_conf);
55833d3fffaSMarius Strobl 			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
55933d3fffaSMarius Strobl 				/* The user made a mistake, return an error. */
5608983cfbfSMike Smith 				cio->status = PCI_GETCONF_ERROR;
5618983cfbfSMike Smith 				error = EINVAL;
562*b01bf72bSMaxim Sobolev                                goto getconfexit;
5638983cfbfSMike Smith 			}
5648983cfbfSMike Smith 
5658983cfbfSMike Smith 			/*
5668983cfbfSMike Smith 			 * Allocate a buffer to hold the patterns.
5678983cfbfSMike Smith 			 */
568b2068c0cSWarner Losh #ifdef PRE7_COMPAT
569*b01bf72bSMaxim Sobolev                        if (cmd == PCIOCGETCONF_OLD32) {
570*b01bf72bSMaxim Sobolev                                pattern_buf_old32 = malloc(cio->pat_buf_len,
571*b01bf72bSMaxim Sobolev                                    M_TEMP, M_WAITOK);
572*b01bf72bSMaxim Sobolev                                error = copyin(cio->patterns,
573*b01bf72bSMaxim Sobolev                                    pattern_buf_old32, cio->pat_buf_len);
574*b01bf72bSMaxim Sobolev                        } else
57533d3fffaSMarius Strobl 			if (cmd == PCIOCGETCONF_OLD) {
57633d3fffaSMarius Strobl 				pattern_buf_old = malloc(cio->pat_buf_len,
57733d3fffaSMarius Strobl 				    M_TEMP, M_WAITOK);
57833d3fffaSMarius Strobl 				error = copyin(cio->patterns,
57933d3fffaSMarius Strobl 				    pattern_buf_old, cio->pat_buf_len);
58033d3fffaSMarius Strobl 			} else
58133d3fffaSMarius Strobl #endif
58233d3fffaSMarius Strobl 			{
5838983cfbfSMike Smith 				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
584a163d034SWarner Losh 				    M_WAITOK);
5858983cfbfSMike Smith 				error = copyin(cio->patterns, pattern_buf,
5868983cfbfSMike Smith 				    cio->pat_buf_len);
58733d3fffaSMarius Strobl 			}
588d08239c1SJohn-Mark Gurney 			if (error != 0) {
589d08239c1SJohn-Mark Gurney 				error = EINVAL;
590d08239c1SJohn-Mark Gurney 				goto getconfexit;
591d08239c1SJohn-Mark Gurney 			}
5928983cfbfSMike Smith 			num_patterns = cio->num_patterns;
5938983cfbfSMike Smith 		} else if ((cio->num_patterns > 0)
5948983cfbfSMike Smith 			|| (cio->pat_buf_len > 0)) {
5958983cfbfSMike Smith 			/*
5968983cfbfSMike Smith 			 * The user made a mistake, spit out an error.
5978983cfbfSMike Smith 			 */
5988983cfbfSMike Smith 			cio->status = PCI_GETCONF_ERROR;
5998983cfbfSMike Smith 			error = EINVAL;
600*b01bf72bSMaxim Sobolev                        goto getconfexit;
60133d3fffaSMarius Strobl 		}
6028983cfbfSMike Smith 
6038983cfbfSMike Smith 		/*
6048983cfbfSMike Smith 		 * Go through the list of devices and copy out the devices
6058983cfbfSMike Smith 		 * that match the user's criteria.
6068983cfbfSMike Smith 		 */
6078983cfbfSMike Smith 		for (cio->num_matches = 0, error = 0, i = 0,
6088983cfbfSMike Smith 		     dinfo = STAILQ_FIRST(devlist_head);
6098983cfbfSMike Smith 		     (dinfo != NULL) && (cio->num_matches < ionum)
610d08239c1SJohn-Mark Gurney 		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
6118983cfbfSMike Smith 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
6128983cfbfSMike Smith 
6138983cfbfSMike Smith 			if (i < cio->offset)
6148983cfbfSMike Smith 				continue;
6158983cfbfSMike Smith 
6168983cfbfSMike Smith 			/* Populate pd_name and pd_unit */
6178983cfbfSMike Smith 			name = NULL;
6180678f786SJohn Baldwin 			if (dinfo->cfg.dev)
6198983cfbfSMike Smith 				name = device_get_name(dinfo->cfg.dev);
6208983cfbfSMike Smith 			if (name) {
6218983cfbfSMike Smith 				strncpy(dinfo->conf.pd_name, name,
6228983cfbfSMike Smith 					sizeof(dinfo->conf.pd_name));
6238983cfbfSMike Smith 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
6248983cfbfSMike Smith 				dinfo->conf.pd_unit =
6258983cfbfSMike Smith 					device_get_unit(dinfo->cfg.dev);
6260678f786SJohn Baldwin 			} else {
6270678f786SJohn Baldwin 				dinfo->conf.pd_name[0] = '\0';
6280678f786SJohn Baldwin 				dinfo->conf.pd_unit = 0;
6298983cfbfSMike Smith 			}
6308983cfbfSMike Smith 
631b2068c0cSWarner Losh #ifdef PRE7_COMPAT
632*b01bf72bSMaxim Sobolev                        if ((cmd == PCIOCGETCONF_OLD32 &&
633*b01bf72bSMaxim Sobolev                            (pattern_buf_old32 == NULL ||
634*b01bf72bSMaxim Sobolev                            pci_conf_match_old32(pattern_buf_old32,
635*b01bf72bSMaxim Sobolev                            num_patterns, &dinfo->conf) == 0)) ||
636*b01bf72bSMaxim Sobolev                            (cmd == PCIOCGETCONF_OLD &&
63733d3fffaSMarius Strobl 			    (pattern_buf_old == NULL ||
63833d3fffaSMarius Strobl 			    pci_conf_match_old(pattern_buf_old, num_patterns,
63933d3fffaSMarius Strobl 			    &dinfo->conf) == 0)) ||
64033d3fffaSMarius Strobl 			    (cmd == PCIOCGETCONF &&
64133d3fffaSMarius Strobl 			    (pattern_buf == NULL ||
64233d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
64333d3fffaSMarius Strobl 			    &dinfo->conf) == 0))) {
64433d3fffaSMarius Strobl #else
64533d3fffaSMarius Strobl 			if (pattern_buf == NULL ||
64633d3fffaSMarius Strobl 			    pci_conf_match(pattern_buf, num_patterns,
64733d3fffaSMarius Strobl 			    &dinfo->conf) == 0) {
64833d3fffaSMarius Strobl #endif
6498983cfbfSMike Smith 				/*
6508983cfbfSMike Smith 				 * If we've filled up the user's buffer,
6518983cfbfSMike Smith 				 * break out at this point.  Since we've
6528983cfbfSMike Smith 				 * got a match here, we'll pick right back
6538983cfbfSMike Smith 				 * up at the matching entry.  We can also
6548983cfbfSMike Smith 				 * tell the user that there are more matches
6558983cfbfSMike Smith 				 * left.
6568983cfbfSMike Smith 				 */
6578983cfbfSMike Smith 				if (cio->num_matches >= ionum)
6588983cfbfSMike Smith 					break;
6598983cfbfSMike Smith 
660b2068c0cSWarner Losh #ifdef PRE7_COMPAT
661*b01bf72bSMaxim Sobolev                                if (cmd == PCIOCGETCONF_OLD32) {
662*b01bf72bSMaxim Sobolev                                        conf_old32.pc_sel.pc_bus =
663*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_sel.pc_bus;
664*b01bf72bSMaxim Sobolev                                        conf_old32.pc_sel.pc_dev =
665*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_sel.pc_dev;
666*b01bf72bSMaxim Sobolev                                        conf_old32.pc_sel.pc_func =
667*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_sel.pc_func;
668*b01bf72bSMaxim Sobolev                                        conf_old32.pc_hdr = dinfo->conf.pc_hdr;
669*b01bf72bSMaxim Sobolev                                        conf_old32.pc_subvendor =
670*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_subvendor;
671*b01bf72bSMaxim Sobolev                                        conf_old32.pc_subdevice =
672*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_subdevice;
673*b01bf72bSMaxim Sobolev                                        conf_old32.pc_vendor =
674*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_vendor;
675*b01bf72bSMaxim Sobolev                                        conf_old32.pc_device =
676*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_device;
677*b01bf72bSMaxim Sobolev                                        conf_old32.pc_class =
678*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_class;
679*b01bf72bSMaxim Sobolev                                        conf_old32.pc_subclass =
680*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_subclass;
681*b01bf72bSMaxim Sobolev                                        conf_old32.pc_progif =
682*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_progif;
683*b01bf72bSMaxim Sobolev                                        conf_old32.pc_revid =
684*b01bf72bSMaxim Sobolev                                            dinfo->conf.pc_revid;
685*b01bf72bSMaxim Sobolev                                        strncpy(conf_old32.pd_name,
686*b01bf72bSMaxim Sobolev                                            dinfo->conf.pd_name,
687*b01bf72bSMaxim Sobolev                                            sizeof(conf_old32.pd_name));
688*b01bf72bSMaxim Sobolev                                        conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
689*b01bf72bSMaxim Sobolev                                        conf_old32.pd_unit =
690*b01bf72bSMaxim Sobolev                                            (u_int32_t)dinfo->conf.pd_unit;
691*b01bf72bSMaxim Sobolev                                        confdata = &conf_old32;
692*b01bf72bSMaxim Sobolev                                } else
69333d3fffaSMarius Strobl 				if (cmd == PCIOCGETCONF_OLD) {
69433d3fffaSMarius Strobl 					conf_old.pc_sel.pc_bus =
69533d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_bus;
69633d3fffaSMarius Strobl 					conf_old.pc_sel.pc_dev =
69733d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_dev;
69833d3fffaSMarius Strobl 					conf_old.pc_sel.pc_func =
69933d3fffaSMarius Strobl 					    dinfo->conf.pc_sel.pc_func;
70033d3fffaSMarius Strobl 					conf_old.pc_hdr = dinfo->conf.pc_hdr;
70133d3fffaSMarius Strobl 					conf_old.pc_subvendor =
70233d3fffaSMarius Strobl 					    dinfo->conf.pc_subvendor;
70333d3fffaSMarius Strobl 					conf_old.pc_subdevice =
70433d3fffaSMarius Strobl 					    dinfo->conf.pc_subdevice;
70533d3fffaSMarius Strobl 					conf_old.pc_vendor =
70633d3fffaSMarius Strobl 					    dinfo->conf.pc_vendor;
70733d3fffaSMarius Strobl 					conf_old.pc_device =
70833d3fffaSMarius Strobl 					    dinfo->conf.pc_device;
70933d3fffaSMarius Strobl 					conf_old.pc_class =
71033d3fffaSMarius Strobl 					    dinfo->conf.pc_class;
71133d3fffaSMarius Strobl 					conf_old.pc_subclass =
71233d3fffaSMarius Strobl 					    dinfo->conf.pc_subclass;
71333d3fffaSMarius Strobl 					conf_old.pc_progif =
71433d3fffaSMarius Strobl 					    dinfo->conf.pc_progif;
71533d3fffaSMarius Strobl 					conf_old.pc_revid =
71633d3fffaSMarius Strobl 					    dinfo->conf.pc_revid;
717c5860546SMarius Strobl 					strncpy(conf_old.pd_name,
718c5860546SMarius Strobl 					    dinfo->conf.pd_name,
71933d3fffaSMarius Strobl 					    sizeof(conf_old.pd_name));
720c5860546SMarius Strobl 					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
72133d3fffaSMarius Strobl 					conf_old.pd_unit =
72233d3fffaSMarius Strobl 					    dinfo->conf.pd_unit;
72333d3fffaSMarius Strobl 					confdata = &conf_old;
72433d3fffaSMarius Strobl 				} else
72533d3fffaSMarius Strobl #endif
72633d3fffaSMarius Strobl 					confdata = &dinfo->conf;
72733d3fffaSMarius Strobl 				/* Only if we can copy it out do we count it. */
72833d3fffaSMarius Strobl 				if (!(error = copyout(confdata,
729c5860546SMarius Strobl 				    (caddr_t)cio->matches +
730c5860546SMarius Strobl 				    confsz * cio->num_matches, confsz)))
73133d3fffaSMarius Strobl 					cio->num_matches++;
7328983cfbfSMike Smith 			}
733d08239c1SJohn-Mark Gurney 		}
7348983cfbfSMike Smith 
7358983cfbfSMike Smith 		/*
7368983cfbfSMike Smith 		 * Set the pointer into the list, so if the user is getting
7378983cfbfSMike Smith 		 * n records at a time, where n < pci_numdevs,
7388983cfbfSMike Smith 		 */
7398983cfbfSMike Smith 		cio->offset = i;
7408983cfbfSMike Smith 
7418983cfbfSMike Smith 		/*
7428983cfbfSMike Smith 		 * Set the generation, the user will need this if they make
7438983cfbfSMike Smith 		 * another ioctl call with offset != 0.
7448983cfbfSMike Smith 		 */
7458983cfbfSMike Smith 		cio->generation = pci_generation;
7468983cfbfSMike Smith 
7478983cfbfSMike Smith 		/*
7488983cfbfSMike Smith 		 * If this is the last device, inform the user so he won't
7498983cfbfSMike Smith 		 * bother asking for more devices.  If dinfo isn't NULL, we
7508983cfbfSMike Smith 		 * know that there are more matches in the list because of
7518983cfbfSMike Smith 		 * the way the traversal is done.
7528983cfbfSMike Smith 		 */
7538983cfbfSMike Smith 		if (dinfo == NULL)
7548983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
7558983cfbfSMike Smith 		else
7568983cfbfSMike Smith 			cio->status = PCI_GETCONF_MORE_DEVS;
7578983cfbfSMike Smith 
758d08239c1SJohn-Mark Gurney getconfexit:
759*b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
760*b01bf72bSMaxim Sobolev                if (cmd == PCIOCGETCONF_OLD32) {
761*b01bf72bSMaxim Sobolev                        cio32->status = cio->status;
762*b01bf72bSMaxim Sobolev                        cio32->generation = cio->generation;
763*b01bf72bSMaxim Sobolev                        cio32->offset = cio->offset;
764*b01bf72bSMaxim Sobolev                        cio32->num_matches = cio->num_matches;
765*b01bf72bSMaxim Sobolev                        if (cio != NULL)
766*b01bf72bSMaxim Sobolev                                free(cio, M_TEMP);
767*b01bf72bSMaxim Sobolev                }
768*b01bf72bSMaxim Sobolev #endif
769*b01bf72bSMaxim Sobolev 
7708983cfbfSMike Smith 		if (pattern_buf != NULL)
7718983cfbfSMike Smith 			free(pattern_buf, M_TEMP);
772b2068c0cSWarner Losh #ifdef PRE7_COMPAT
773*b01bf72bSMaxim Sobolev                if (pattern_buf_old32 != NULL)
774*b01bf72bSMaxim Sobolev                        free(pattern_buf_old32, M_TEMP);
77533d3fffaSMarius Strobl 		if (pattern_buf_old != NULL)
77633d3fffaSMarius Strobl 			free(pattern_buf_old, M_TEMP);
77733d3fffaSMarius Strobl #endif
7788983cfbfSMike Smith 
7798983cfbfSMike Smith 		break;
7808983cfbfSMike Smith 
781b2068c0cSWarner Losh #ifdef PRE7_COMPAT
78233d3fffaSMarius Strobl 	case PCIOCREAD_OLD:
78333d3fffaSMarius Strobl 	case PCIOCWRITE_OLD:
78433d3fffaSMarius Strobl 		io_old = (struct pci_io_old *)data;
78533d3fffaSMarius Strobl 		iodata.pi_sel.pc_domain = 0;
78633d3fffaSMarius Strobl 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
78733d3fffaSMarius Strobl 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
78833d3fffaSMarius Strobl 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
78933d3fffaSMarius Strobl 		iodata.pi_reg = io_old->pi_reg;
79033d3fffaSMarius Strobl 		iodata.pi_width = io_old->pi_width;
79133d3fffaSMarius Strobl 		iodata.pi_data = io_old->pi_data;
79233d3fffaSMarius Strobl 		data = (caddr_t)&iodata;
79333d3fffaSMarius Strobl 		/* FALLTHROUGH */
79433d3fffaSMarius Strobl #endif
79566f314b5SStefan Eßer 	case PCIOCREAD:
7968983cfbfSMike Smith 	case PCIOCWRITE:
7978983cfbfSMike Smith 		io = (struct pci_io *)data;
7988983cfbfSMike Smith 		switch(io->pi_width) {
7998983cfbfSMike Smith 		case 4:
8008983cfbfSMike Smith 		case 2:
8018983cfbfSMike Smith 		case 1:
802d16d35fdSAndriy Gapon 			/* Make sure register is not negative and aligned. */
80333d3fffaSMarius Strobl 			if (io->pi_reg < 0 ||
80433d3fffaSMarius Strobl 			    io->pi_reg & (io->pi_width - 1)) {
80566f314b5SStefan Eßer 				error = EINVAL;
80640ed3f47SRuslan Ermilov 				break;
80740ed3f47SRuslan Ermilov 			}
8088983cfbfSMike Smith 			/*
8098983cfbfSMike Smith 			 * Assume that the user-level bus number is
81037ce43b7SBruce M Simpson 			 * in fact the physical PCI bus number.
81137ce43b7SBruce M Simpson 			 * Look up the grandparent, i.e. the bridge device,
81237ce43b7SBruce M Simpson 			 * so that we can issue configuration space cycles.
8138983cfbfSMike Smith 			 */
81455aaf894SMarius Strobl 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
81555aaf894SMarius Strobl 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
81655aaf894SMarius Strobl 			    io->pi_sel.pc_func);
81737ce43b7SBruce M Simpson 			if (pcidev) {
81833d3fffaSMarius Strobl 				brdev = device_get_parent(
81933d3fffaSMarius Strobl 				    device_get_parent(pcidev));
82037ce43b7SBruce M Simpson 
821b2068c0cSWarner Losh #ifdef PRE7_COMPAT
82233d3fffaSMarius Strobl 				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
82333d3fffaSMarius Strobl #else
82466f314b5SStefan Eßer 				if (cmd == PCIOCWRITE)
82533d3fffaSMarius Strobl #endif
82637ce43b7SBruce M Simpson 					PCIB_WRITE_CONFIG(brdev,
82737ce43b7SBruce M Simpson 							  io->pi_sel.pc_bus,
8288983cfbfSMike Smith 							  io->pi_sel.pc_dev,
8298983cfbfSMike Smith 							  io->pi_sel.pc_func,
8308983cfbfSMike Smith 							  io->pi_reg,
8318983cfbfSMike Smith 							  io->pi_data,
8328983cfbfSMike Smith 							  io->pi_width);
833b2068c0cSWarner Losh #ifdef PRE7_COMPAT
83433d3fffaSMarius Strobl 				else if (cmd == PCIOCREAD_OLD)
83533d3fffaSMarius Strobl 					io_old->pi_data =
83633d3fffaSMarius Strobl 						PCIB_READ_CONFIG(brdev,
83733d3fffaSMarius Strobl 							  io->pi_sel.pc_bus,
83833d3fffaSMarius Strobl 							  io->pi_sel.pc_dev,
83933d3fffaSMarius Strobl 							  io->pi_sel.pc_func,
84033d3fffaSMarius Strobl 							  io->pi_reg,
84133d3fffaSMarius Strobl 							  io->pi_width);
84233d3fffaSMarius Strobl #endif
84366f314b5SStefan Eßer 				else
84466f314b5SStefan Eßer 					io->pi_data =
84537ce43b7SBruce M Simpson 						PCIB_READ_CONFIG(brdev,
84637ce43b7SBruce M Simpson 							  io->pi_sel.pc_bus,
84766f314b5SStefan Eßer 							  io->pi_sel.pc_dev,
84866f314b5SStefan Eßer 							  io->pi_sel.pc_func,
84966f314b5SStefan Eßer 							  io->pi_reg,
85066f314b5SStefan Eßer 							  io->pi_width);
8518983cfbfSMike Smith 				error = 0;
8528983cfbfSMike Smith 			} else {
8538910aa92SPaul Saab #ifdef COMPAT_FREEBSD4
85433d3fffaSMarius Strobl 				if (cmd == PCIOCREAD_OLD) {
85533d3fffaSMarius Strobl 					io_old->pi_data = -1;
8568910aa92SPaul Saab 					error = 0;
8578910aa92SPaul Saab 				} else
8588910aa92SPaul Saab #endif
8598983cfbfSMike Smith 					error = ENODEV;
8608983cfbfSMike Smith 			}
8618983cfbfSMike Smith 			break;
8628983cfbfSMike Smith 		default:
863d08239c1SJohn-Mark Gurney 			error = EINVAL;
8648983cfbfSMike Smith 			break;
8658983cfbfSMike Smith 		}
8668983cfbfSMike Smith 		break;
8678983cfbfSMike Smith 
868da1e0915SJohn Baldwin 	case PCIOCGETBAR:
869da1e0915SJohn Baldwin 		bio = (struct pci_bar_io *)data;
870da1e0915SJohn Baldwin 
871da1e0915SJohn Baldwin 		/*
872da1e0915SJohn Baldwin 		 * Assume that the user-level bus number is
873da1e0915SJohn Baldwin 		 * in fact the physical PCI bus number.
874da1e0915SJohn Baldwin 		 */
875da1e0915SJohn Baldwin 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
876da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
877da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_func);
878da1e0915SJohn Baldwin 		if (pcidev == NULL) {
879da1e0915SJohn Baldwin 			error = ENODEV;
880da1e0915SJohn Baldwin 			break;
881da1e0915SJohn Baldwin 		}
882a90dd577SJohn Baldwin 		pm = pci_find_bar(pcidev, bio->pbi_reg);
883a90dd577SJohn Baldwin 		if (pm == NULL) {
884da1e0915SJohn Baldwin 			error = EINVAL;
885da1e0915SJohn Baldwin 			break;
886da1e0915SJohn Baldwin 		}
887a90dd577SJohn Baldwin 		bio->pbi_base = pm->pm_value;
888a90dd577SJohn Baldwin 		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
889a90dd577SJohn Baldwin 		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
890da1e0915SJohn Baldwin 		error = 0;
891da1e0915SJohn Baldwin 		break;
892762aad81SNeel Natu 	case PCIOCATTACHED:
893762aad81SNeel Natu 		error = 0;
894762aad81SNeel Natu 		io = (struct pci_io *)data;
895762aad81SNeel Natu 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
896762aad81SNeel Natu 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
897762aad81SNeel Natu 		if (pcidev != NULL)
898762aad81SNeel Natu 			io->pi_data = device_is_attached(pcidev);
899762aad81SNeel Natu 		else
900762aad81SNeel Natu 			error = ENODEV;
901762aad81SNeel Natu 		break;
9028983cfbfSMike Smith 	default:
9038983cfbfSMike Smith 		error = ENOTTY;
9048983cfbfSMike Smith 		break;
9058983cfbfSMike Smith 	}
9068983cfbfSMike Smith 
9078983cfbfSMike Smith 	return (error);
9088983cfbfSMike Smith }
909