xref: /freebsd/sys/dev/pci/pci_user.c (revision 7e14be0b0717105f4b3b8c62df82a1e883d8ebb6)
1aad970f1SDavid E. O'Brien /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4fce5d19dSStefan Eßer  * Copyright 1997, Stefan Esser <se@freebsd.org>
58983cfbfSMike Smith  *
68983cfbfSMike Smith  * Redistribution and use in source and binary forms, with or without
78983cfbfSMike Smith  * modification, are permitted provided that the following conditions
88983cfbfSMike Smith  * are met:
98983cfbfSMike Smith  * 1. Redistributions of source code must retain the above copyright
108983cfbfSMike Smith  *    notice unmodified, this list of conditions, and the following
118983cfbfSMike Smith  *    disclaimer.
128983cfbfSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
138983cfbfSMike Smith  *    notice, this list of conditions and the following disclaimer in the
148983cfbfSMike Smith  *    documentation and/or other materials provided with the distribution.
158983cfbfSMike Smith  *
168983cfbfSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178983cfbfSMike Smith  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188983cfbfSMike Smith  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198983cfbfSMike Smith  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208983cfbfSMike Smith  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
218983cfbfSMike Smith  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
228983cfbfSMike Smith  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
238983cfbfSMike Smith  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
248983cfbfSMike Smith  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
258983cfbfSMike Smith  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
268983cfbfSMike Smith  */
278983cfbfSMike Smith 
28aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
29aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
30aad970f1SDavid E. O'Brien 
318983cfbfSMike Smith #include "opt_bus.h"	/* XXX trim includes */
328983cfbfSMike Smith 
3387842989SKonstantin Belousov #include <sys/types.h>
348983cfbfSMike Smith #include <sys/param.h>
358983cfbfSMike Smith #include <sys/systm.h>
368983cfbfSMike Smith #include <sys/malloc.h>
378983cfbfSMike Smith #include <sys/module.h>
388983cfbfSMike Smith #include <sys/linker.h>
398983cfbfSMike Smith #include <sys/fcntl.h>
408983cfbfSMike Smith #include <sys/conf.h>
418983cfbfSMike Smith #include <sys/kernel.h>
4287842989SKonstantin Belousov #include <sys/mman.h>
438002488bSRobert Watson #include <sys/proc.h>
448983cfbfSMike Smith #include <sys/queue.h>
4587842989SKonstantin Belousov #include <sys/rwlock.h>
4687842989SKonstantin Belousov #include <sys/sglist.h>
478983cfbfSMike Smith 
488983cfbfSMike Smith #include <vm/vm.h>
498983cfbfSMike Smith #include <vm/pmap.h>
508983cfbfSMike Smith #include <vm/vm_extern.h>
5187842989SKonstantin Belousov #include <vm/vm_map.h>
5287842989SKonstantin Belousov #include <vm/vm_object.h>
5387842989SKonstantin Belousov #include <vm/vm_page.h>
5487842989SKonstantin Belousov #include <vm/vm_pager.h>
558983cfbfSMike Smith 
568983cfbfSMike Smith #include <sys/bus.h>
578983cfbfSMike Smith #include <machine/bus.h>
588983cfbfSMike Smith #include <sys/rman.h>
598983cfbfSMike Smith #include <machine/resource.h>
608983cfbfSMike Smith 
618983cfbfSMike Smith #include <sys/pciio.h>
6238d8c994SWarner Losh #include <dev/pci/pcireg.h>
6338d8c994SWarner Losh #include <dev/pci/pcivar.h>
648983cfbfSMike Smith 
658983cfbfSMike Smith #include "pcib_if.h"
668983cfbfSMike Smith #include "pci_if.h"
678983cfbfSMike Smith 
68b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
69b7edb6faSBrooks Davis struct pci_conf32 {
70b7edb6faSBrooks Davis 	struct pcisel	pc_sel;		/* domain+bus+slot+function */
71b7edb6faSBrooks Davis 	u_int8_t	pc_hdr;		/* PCI header type */
72b7edb6faSBrooks Davis 	u_int16_t	pc_subvendor;	/* card vendor ID */
73b7edb6faSBrooks Davis 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
74b7edb6faSBrooks Davis 					   card vendor */
75b7edb6faSBrooks Davis 	u_int16_t	pc_vendor;	/* chip vendor ID */
76b7edb6faSBrooks Davis 	u_int16_t	pc_device;	/* chip device ID, assigned by
77b7edb6faSBrooks Davis 					   chip vendor */
78b7edb6faSBrooks Davis 	u_int8_t	pc_class;	/* chip PCI class */
79b7edb6faSBrooks Davis 	u_int8_t	pc_subclass;	/* chip PCI subclass */
80b7edb6faSBrooks Davis 	u_int8_t	pc_progif;	/* chip PCI programming interface */
81b7edb6faSBrooks Davis 	u_int8_t	pc_revid;	/* chip revision ID */
82b7edb6faSBrooks Davis 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
83b7edb6faSBrooks Davis 	u_int32_t	pd_unit;	/* device unit number */
84b7edb6faSBrooks Davis };
85b7edb6faSBrooks Davis 
86b7edb6faSBrooks Davis struct pci_match_conf32 {
87b7edb6faSBrooks Davis 	struct pcisel		pc_sel;		/* domain+bus+slot+function */
88b7edb6faSBrooks Davis 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
89b7edb6faSBrooks Davis 	u_int32_t		pd_unit;	/* Unit number */
90b7edb6faSBrooks Davis 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
91b7edb6faSBrooks Davis 	u_int16_t		pc_device;	/* PCI Device ID */
92b7edb6faSBrooks Davis 	u_int8_t		pc_class;	/* PCI class */
93b7edb6faSBrooks Davis 	u_int32_t		flags;		/* Matching expression */
94b7edb6faSBrooks Davis };
95b7edb6faSBrooks Davis 
96b7edb6faSBrooks Davis struct pci_conf_io32 {
97b7edb6faSBrooks Davis 	u_int32_t		pat_buf_len;	/* pattern buffer length */
98b7edb6faSBrooks Davis 	u_int32_t		num_patterns;	/* number of patterns */
99b7edb6faSBrooks Davis 	u_int32_t		patterns;	/* struct pci_match_conf ptr */
100b7edb6faSBrooks Davis 	u_int32_t		match_buf_len;	/* match buffer length */
101b7edb6faSBrooks Davis 	u_int32_t		num_matches;	/* number of matches returned */
102b7edb6faSBrooks Davis 	u_int32_t		matches;	/* struct pci_conf ptr */
103b7edb6faSBrooks Davis 	u_int32_t		offset;		/* offset into device list */
104b7edb6faSBrooks Davis 	u_int32_t		generation;	/* device list generation */
105b7edb6faSBrooks Davis 	u_int32_t		status;		/* request status */
106b7edb6faSBrooks Davis };
107b7edb6faSBrooks Davis 
108b7edb6faSBrooks Davis #define	PCIOCGETCONF32	_IOC_NEWTYPE(PCIOCGETCONF, struct pci_conf_io32)
109b7edb6faSBrooks Davis #endif
110b7edb6faSBrooks Davis 
1118983cfbfSMike Smith /*
1128983cfbfSMike Smith  * This is the user interface to PCI configuration space.
1138983cfbfSMike Smith  */
1148983cfbfSMike Smith 
115b40ce416SJulian Elischer static d_open_t 	pci_open;
116b40ce416SJulian Elischer static d_close_t	pci_close;
117b40ce416SJulian Elischer static d_ioctl_t	pci_ioctl;
1188983cfbfSMike Smith 
1198983cfbfSMike Smith struct cdevsw pcicdev = {
120dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
121dd615d09SWarner Losh 	.d_flags =	0,
1227ac40f5fSPoul-Henning Kamp 	.d_open =	pci_open,
1237ac40f5fSPoul-Henning Kamp 	.d_close =	pci_close,
1247ac40f5fSPoul-Henning Kamp 	.d_ioctl =	pci_ioctl,
1257ac40f5fSPoul-Henning Kamp 	.d_name =	"pci",
1268983cfbfSMike Smith };
1278983cfbfSMike Smith 
1288983cfbfSMike Smith static int
12989c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
1308983cfbfSMike Smith {
1318002488bSRobert Watson 	int error;
1328002488bSRobert Watson 
1338002488bSRobert Watson 	if (oflags & FWRITE) {
134a854ed98SJohn Baldwin 		error = securelevel_gt(td->td_ucred, 0);
1358002488bSRobert Watson 		if (error)
1368002488bSRobert Watson 			return (error);
1378983cfbfSMike Smith 	}
1388002488bSRobert Watson 
1398002488bSRobert Watson 	return (0);
1408983cfbfSMike Smith }
1418983cfbfSMike Smith 
1428983cfbfSMike Smith static int
14389c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
1448983cfbfSMike Smith {
1458983cfbfSMike Smith 	return 0;
1468983cfbfSMike Smith }
1478983cfbfSMike Smith 
1488983cfbfSMike Smith /*
1498983cfbfSMike Smith  * Match a single pci_conf structure against an array of pci_match_conf
1508983cfbfSMike Smith  * structures.  The first argument, 'matches', is an array of num_matches
1518983cfbfSMike Smith  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
1528983cfbfSMike Smith  * structure that will be compared to every entry in the matches array.
1538983cfbfSMike Smith  * This function returns 1 on failure, 0 on success.
1548983cfbfSMike Smith  */
1558983cfbfSMike Smith static int
156e9ed3a70SBrooks Davis pci_conf_match_native(struct pci_match_conf *matches, int num_matches,
1578983cfbfSMike Smith 	       struct pci_conf *match_buf)
1588983cfbfSMike Smith {
1598983cfbfSMike Smith 	int i;
1608983cfbfSMike Smith 
1618983cfbfSMike Smith 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
1628983cfbfSMike Smith 		return(1);
1638983cfbfSMike Smith 
1648983cfbfSMike Smith 	for (i = 0; i < num_matches; i++) {
1658983cfbfSMike Smith 		/*
1668983cfbfSMike Smith 		 * I'm not sure why someone would do this...but...
1678983cfbfSMike Smith 		 */
1688983cfbfSMike Smith 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
1698983cfbfSMike Smith 			continue;
1708983cfbfSMike Smith 
1718983cfbfSMike Smith 		/*
1728983cfbfSMike Smith 		 * Look at each of the match flags.  If it's set, do the
1738983cfbfSMike Smith 		 * comparison.  If the comparison fails, we don't have a
1748983cfbfSMike Smith 		 * match, go on to the next item if there is one.
1758983cfbfSMike Smith 		 */
17655aaf894SMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
17755aaf894SMarius Strobl 		 && (match_buf->pc_sel.pc_domain !=
17855aaf894SMarius Strobl 		 matches[i].pc_sel.pc_domain))
17955aaf894SMarius Strobl 			continue;
18055aaf894SMarius Strobl 
1818983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
1828983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
1838983cfbfSMike Smith 			continue;
1848983cfbfSMike Smith 
1858983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
1868983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
1878983cfbfSMike Smith 			continue;
1888983cfbfSMike Smith 
1898983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
1908983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
1918983cfbfSMike Smith 			continue;
1928983cfbfSMike Smith 
1938983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
1948983cfbfSMike Smith 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
1958983cfbfSMike Smith 			continue;
1968983cfbfSMike Smith 
1978983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
1988983cfbfSMike Smith 		 && (match_buf->pc_device != matches[i].pc_device))
1998983cfbfSMike Smith 			continue;
2008983cfbfSMike Smith 
2018983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
2028983cfbfSMike Smith 		 && (match_buf->pc_class != matches[i].pc_class))
2038983cfbfSMike Smith 			continue;
2048983cfbfSMike Smith 
2058983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
2068983cfbfSMike Smith 		 && (match_buf->pd_unit != matches[i].pd_unit))
2078983cfbfSMike Smith 			continue;
2088983cfbfSMike Smith 
2098983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
2108983cfbfSMike Smith 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
2118983cfbfSMike Smith 			     sizeof(match_buf->pd_name)) != 0))
2128983cfbfSMike Smith 			continue;
2138983cfbfSMike Smith 
2148983cfbfSMike Smith 		return(0);
2158983cfbfSMike Smith 	}
2168983cfbfSMike Smith 
2178983cfbfSMike Smith 	return(1);
2188983cfbfSMike Smith }
2198983cfbfSMike Smith 
220b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
221b7edb6faSBrooks Davis static int
222b7edb6faSBrooks Davis pci_conf_match32(struct pci_match_conf32 *matches, int num_matches,
223b7edb6faSBrooks Davis 	       struct pci_conf *match_buf)
224b7edb6faSBrooks Davis {
225b7edb6faSBrooks Davis 	int i;
226b7edb6faSBrooks Davis 
227b7edb6faSBrooks Davis 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
228b7edb6faSBrooks Davis 		return(1);
229b7edb6faSBrooks Davis 
230b7edb6faSBrooks Davis 	for (i = 0; i < num_matches; i++) {
231b7edb6faSBrooks Davis 		/*
232b7edb6faSBrooks Davis 		 * I'm not sure why someone would do this...but...
233b7edb6faSBrooks Davis 		 */
234b7edb6faSBrooks Davis 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
235b7edb6faSBrooks Davis 			continue;
236b7edb6faSBrooks Davis 
237b7edb6faSBrooks Davis 		/*
238b7edb6faSBrooks Davis 		 * Look at each of the match flags.  If it's set, do the
239b7edb6faSBrooks Davis 		 * comparison.  If the comparison fails, we don't have a
240b7edb6faSBrooks Davis 		 * match, go on to the next item if there is one.
241b7edb6faSBrooks Davis 		 */
242b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
243b7edb6faSBrooks Davis 		 && (match_buf->pc_sel.pc_domain !=
244b7edb6faSBrooks Davis 		 matches[i].pc_sel.pc_domain))
245b7edb6faSBrooks Davis 			continue;
246b7edb6faSBrooks Davis 
247b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
248b7edb6faSBrooks Davis 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
249b7edb6faSBrooks Davis 			continue;
250b7edb6faSBrooks Davis 
251b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
252b7edb6faSBrooks Davis 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
253b7edb6faSBrooks Davis 			continue;
254b7edb6faSBrooks Davis 
255b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
256b7edb6faSBrooks Davis 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
257b7edb6faSBrooks Davis 			continue;
258b7edb6faSBrooks Davis 
259b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
260b7edb6faSBrooks Davis 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
261b7edb6faSBrooks Davis 			continue;
262b7edb6faSBrooks Davis 
263b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
264b7edb6faSBrooks Davis 		 && (match_buf->pc_device != matches[i].pc_device))
265b7edb6faSBrooks Davis 			continue;
266b7edb6faSBrooks Davis 
267b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
268b7edb6faSBrooks Davis 		 && (match_buf->pc_class != matches[i].pc_class))
269b7edb6faSBrooks Davis 			continue;
270b7edb6faSBrooks Davis 
271b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
272b7edb6faSBrooks Davis 		 && (match_buf->pd_unit != matches[i].pd_unit))
273b7edb6faSBrooks Davis 			continue;
274b7edb6faSBrooks Davis 
275b7edb6faSBrooks Davis 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
276b7edb6faSBrooks Davis 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
277b7edb6faSBrooks Davis 			     sizeof(match_buf->pd_name)) != 0))
278b7edb6faSBrooks Davis 			continue;
279b7edb6faSBrooks Davis 
280b7edb6faSBrooks Davis 		return(0);
281b7edb6faSBrooks Davis 	}
282b7edb6faSBrooks Davis 
283b7edb6faSBrooks Davis 	return(1);
284b7edb6faSBrooks Davis }
285b7edb6faSBrooks Davis #endif	/* COMPAT_FREEBSD32 */
286b7edb6faSBrooks Davis 
28733d3fffaSMarius Strobl #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
28833d3fffaSMarius Strobl     defined(COMPAT_FREEBSD6)
289b2068c0cSWarner Losh #define PRE7_COMPAT
29033d3fffaSMarius Strobl 
29133d3fffaSMarius Strobl typedef enum {
29233d3fffaSMarius Strobl 	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
29333d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
29433d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
29533d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
29633d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
29733d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
29833d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
29933d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
30033d3fffaSMarius Strobl 	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
30133d3fffaSMarius Strobl } pci_getconf_flags_old;
30233d3fffaSMarius Strobl 
30333d3fffaSMarius Strobl struct pcisel_old {
30433d3fffaSMarius Strobl 	u_int8_t	pc_bus;		/* bus number */
30533d3fffaSMarius Strobl 	u_int8_t	pc_dev;		/* device on this bus */
30633d3fffaSMarius Strobl 	u_int8_t	pc_func;	/* function on this device */
30733d3fffaSMarius Strobl };
30833d3fffaSMarius Strobl 
30933d3fffaSMarius Strobl struct pci_conf_old {
31033d3fffaSMarius Strobl 	struct pcisel_old pc_sel;	/* bus+slot+function */
31133d3fffaSMarius Strobl 	u_int8_t	pc_hdr;		/* PCI header type */
31233d3fffaSMarius Strobl 	u_int16_t	pc_subvendor;	/* card vendor ID */
31333d3fffaSMarius Strobl 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
31433d3fffaSMarius Strobl 					   card vendor */
31533d3fffaSMarius Strobl 	u_int16_t	pc_vendor;	/* chip vendor ID */
31633d3fffaSMarius Strobl 	u_int16_t	pc_device;	/* chip device ID, assigned by
31733d3fffaSMarius Strobl 					   chip vendor */
31833d3fffaSMarius Strobl 	u_int8_t	pc_class;	/* chip PCI class */
31933d3fffaSMarius Strobl 	u_int8_t	pc_subclass;	/* chip PCI subclass */
32033d3fffaSMarius Strobl 	u_int8_t	pc_progif;	/* chip PCI programming interface */
32133d3fffaSMarius Strobl 	u_int8_t	pc_revid;	/* chip revision ID */
32233d3fffaSMarius Strobl 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
32333d3fffaSMarius Strobl 	u_long		pd_unit;	/* device unit number */
32433d3fffaSMarius Strobl };
32533d3fffaSMarius Strobl 
32633d3fffaSMarius Strobl struct pci_match_conf_old {
32733d3fffaSMarius Strobl 	struct pcisel_old	pc_sel;		/* bus+slot+function */
32833d3fffaSMarius Strobl 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
32933d3fffaSMarius Strobl 	u_long			pd_unit;	/* Unit number */
33033d3fffaSMarius Strobl 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
33133d3fffaSMarius Strobl 	u_int16_t		pc_device;	/* PCI Device ID */
33233d3fffaSMarius Strobl 	u_int8_t		pc_class;	/* PCI class */
333c5860546SMarius Strobl 	pci_getconf_flags_old	flags;		/* Matching expression */
33433d3fffaSMarius Strobl };
33533d3fffaSMarius Strobl 
33633d3fffaSMarius Strobl struct pci_io_old {
33733d3fffaSMarius Strobl 	struct pcisel_old pi_sel;	/* device to operate on */
33833d3fffaSMarius Strobl 	int		pi_reg;		/* configuration register to examine */
33933d3fffaSMarius Strobl 	int		pi_width;	/* width (in bytes) of read or write */
34033d3fffaSMarius Strobl 	u_int32_t	pi_data;	/* data to write or result of read */
34133d3fffaSMarius Strobl };
34233d3fffaSMarius Strobl 
343b01bf72bSMaxim Sobolev #ifdef COMPAT_FREEBSD32
344b01bf72bSMaxim Sobolev struct pci_conf_old32 {
345b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
346e5280830SGleb Smirnoff 	uint8_t		pc_hdr;		/* PCI header type */
347e5280830SGleb Smirnoff 	uint16_t	pc_subvendor;	/* card vendor ID */
348e5280830SGleb Smirnoff 	uint16_t	pc_subdevice;	/* card device ID, assigned by
349b01bf72bSMaxim Sobolev 					   card vendor */
350e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* chip vendor ID */
351e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* chip device ID, assigned by
352b01bf72bSMaxim Sobolev 					   chip vendor */
353e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* chip PCI class */
354e5280830SGleb Smirnoff 	uint8_t		pc_subclass;	/* chip PCI subclass */
355e5280830SGleb Smirnoff 	uint8_t		pc_progif;	/* chip PCI programming interface */
356e5280830SGleb Smirnoff 	uint8_t		pc_revid;	/* chip revision ID */
357b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
358e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* device unit number (u_long) */
359b01bf72bSMaxim Sobolev };
360b01bf72bSMaxim Sobolev 
361b01bf72bSMaxim Sobolev struct pci_match_conf_old32 {
362b01bf72bSMaxim Sobolev 	struct pcisel_old pc_sel;	/* bus+slot+function */
363b01bf72bSMaxim Sobolev 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
364e5280830SGleb Smirnoff 	uint32_t	pd_unit;	/* Unit number (u_long) */
365e5280830SGleb Smirnoff 	uint16_t	pc_vendor;	/* PCI Vendor ID */
366e5280830SGleb Smirnoff 	uint16_t	pc_device;	/* PCI Device ID */
367e5280830SGleb Smirnoff 	uint8_t		pc_class;	/* PCI class */
368b01bf72bSMaxim Sobolev 	pci_getconf_flags_old flags;	/* Matching expression */
369b01bf72bSMaxim Sobolev };
370b01bf72bSMaxim Sobolev 
371b01bf72bSMaxim Sobolev #define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
372904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
373b01bf72bSMaxim Sobolev 
37433d3fffaSMarius Strobl #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
37533d3fffaSMarius Strobl #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
37633d3fffaSMarius Strobl #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
37733d3fffaSMarius Strobl 
37833d3fffaSMarius Strobl static int
37933d3fffaSMarius Strobl pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
38033d3fffaSMarius Strobl     struct pci_conf *match_buf)
38133d3fffaSMarius Strobl {
38233d3fffaSMarius Strobl 	int i;
38333d3fffaSMarius Strobl 
38433d3fffaSMarius Strobl 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
38533d3fffaSMarius Strobl 		return(1);
38633d3fffaSMarius Strobl 
38733d3fffaSMarius Strobl 	for (i = 0; i < num_matches; i++) {
38833d3fffaSMarius Strobl 		if (match_buf->pc_sel.pc_domain != 0)
38933d3fffaSMarius Strobl 			continue;
39033d3fffaSMarius Strobl 
39133d3fffaSMarius Strobl 		/*
39233d3fffaSMarius Strobl 		 * I'm not sure why someone would do this...but...
39333d3fffaSMarius Strobl 		 */
39433d3fffaSMarius Strobl 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
39533d3fffaSMarius Strobl 			continue;
39633d3fffaSMarius Strobl 
39733d3fffaSMarius Strobl 		/*
39833d3fffaSMarius Strobl 		 * Look at each of the match flags.  If it's set, do the
39933d3fffaSMarius Strobl 		 * comparison.  If the comparison fails, we don't have a
40033d3fffaSMarius Strobl 		 * match, go on to the next item if there is one.
40133d3fffaSMarius Strobl 		 */
40233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
40333d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
40433d3fffaSMarius Strobl 			continue;
40533d3fffaSMarius Strobl 
40633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
40733d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
40833d3fffaSMarius Strobl 			continue;
40933d3fffaSMarius Strobl 
41033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
41133d3fffaSMarius Strobl 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
41233d3fffaSMarius Strobl 			continue;
41333d3fffaSMarius Strobl 
41433d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
41533d3fffaSMarius Strobl 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
41633d3fffaSMarius Strobl 			continue;
41733d3fffaSMarius Strobl 
41833d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
41933d3fffaSMarius Strobl 		 && (match_buf->pc_device != matches[i].pc_device))
42033d3fffaSMarius Strobl 			continue;
42133d3fffaSMarius Strobl 
42233d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
42333d3fffaSMarius Strobl 		 && (match_buf->pc_class != matches[i].pc_class))
42433d3fffaSMarius Strobl 			continue;
42533d3fffaSMarius Strobl 
42633d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
42733d3fffaSMarius Strobl 		 && (match_buf->pd_unit != matches[i].pd_unit))
42833d3fffaSMarius Strobl 			continue;
42933d3fffaSMarius Strobl 
43033d3fffaSMarius Strobl 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
43133d3fffaSMarius Strobl 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
43233d3fffaSMarius Strobl 			     sizeof(match_buf->pd_name)) != 0))
43333d3fffaSMarius Strobl 			continue;
43433d3fffaSMarius Strobl 
43533d3fffaSMarius Strobl 		return(0);
43633d3fffaSMarius Strobl 	}
43733d3fffaSMarius Strobl 
43833d3fffaSMarius Strobl 	return(1);
43933d3fffaSMarius Strobl }
44033d3fffaSMarius Strobl 
441904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
442b01bf72bSMaxim Sobolev static int
443b01bf72bSMaxim Sobolev pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
444b01bf72bSMaxim Sobolev     struct pci_conf *match_buf)
445b01bf72bSMaxim Sobolev {
446b01bf72bSMaxim Sobolev 	int i;
447b01bf72bSMaxim Sobolev 
448b01bf72bSMaxim Sobolev 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
449b01bf72bSMaxim Sobolev 		return(1);
450b01bf72bSMaxim Sobolev 
451b01bf72bSMaxim Sobolev 	for (i = 0; i < num_matches; i++) {
452b01bf72bSMaxim Sobolev 		if (match_buf->pc_sel.pc_domain != 0)
453b01bf72bSMaxim Sobolev 			continue;
454b01bf72bSMaxim Sobolev 
455b01bf72bSMaxim Sobolev 		/*
456b01bf72bSMaxim Sobolev 		 * I'm not sure why someone would do this...but...
457b01bf72bSMaxim Sobolev 		 */
458b01bf72bSMaxim Sobolev 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
459b01bf72bSMaxim Sobolev 			continue;
460b01bf72bSMaxim Sobolev 
461b01bf72bSMaxim Sobolev 		/*
462b01bf72bSMaxim Sobolev 		 * Look at each of the match flags.  If it's set, do the
463b01bf72bSMaxim Sobolev 		 * comparison.  If the comparison fails, we don't have a
464b01bf72bSMaxim Sobolev 		 * match, go on to the next item if there is one.
465b01bf72bSMaxim Sobolev 		 */
466e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
467e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
468b01bf72bSMaxim Sobolev 			continue;
469b01bf72bSMaxim Sobolev 
470e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
471e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
472b01bf72bSMaxim Sobolev 			continue;
473b01bf72bSMaxim Sobolev 
474e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
475e5280830SGleb Smirnoff 		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
476b01bf72bSMaxim Sobolev 			continue;
477b01bf72bSMaxim Sobolev 
478e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
479e5280830SGleb Smirnoff 		    (match_buf->pc_vendor != matches[i].pc_vendor))
480b01bf72bSMaxim Sobolev 			continue;
481b01bf72bSMaxim Sobolev 
482e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
483e5280830SGleb Smirnoff 		    (match_buf->pc_device != matches[i].pc_device))
484b01bf72bSMaxim Sobolev 			continue;
485b01bf72bSMaxim Sobolev 
486e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
487e5280830SGleb Smirnoff 		    (match_buf->pc_class != matches[i].pc_class))
488b01bf72bSMaxim Sobolev 			continue;
489b01bf72bSMaxim Sobolev 
490e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
491e5280830SGleb Smirnoff 		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
492b01bf72bSMaxim Sobolev 			continue;
493b01bf72bSMaxim Sobolev 
494e5280830SGleb Smirnoff 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
495e5280830SGleb Smirnoff 		    (strncmp(matches[i].pd_name, match_buf->pd_name,
496b01bf72bSMaxim Sobolev 		    sizeof(match_buf->pd_name)) != 0))
497b01bf72bSMaxim Sobolev 			continue;
498b01bf72bSMaxim Sobolev 
499b01bf72bSMaxim Sobolev 		return (0);
500b01bf72bSMaxim Sobolev 	}
501b01bf72bSMaxim Sobolev 
502b01bf72bSMaxim Sobolev 	return (1);
503b01bf72bSMaxim Sobolev }
504904c3909SGleb Smirnoff #endif	/* COMPAT_FREEBSD32 */
505e9ed3a70SBrooks Davis #endif	/* !PRE7_COMPAT */
506e9ed3a70SBrooks Davis 
507e9ed3a70SBrooks Davis union pci_conf_union {
508e9ed3a70SBrooks Davis 	struct pci_conf		pc;
509b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
510b7edb6faSBrooks Davis 	struct pci_conf32	pc32;
511b7edb6faSBrooks Davis #endif
512e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
513e9ed3a70SBrooks Davis 	struct pci_conf_old	pco;
514e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32
515e9ed3a70SBrooks Davis 	struct pci_conf_old32	pco32;
516e9ed3a70SBrooks Davis #endif
517e9ed3a70SBrooks Davis #endif
518e9ed3a70SBrooks Davis };
519e9ed3a70SBrooks Davis 
520e9ed3a70SBrooks Davis static int
521e9ed3a70SBrooks Davis pci_conf_match(u_long cmd, struct pci_match_conf *matches, int num_matches,
522e9ed3a70SBrooks Davis     struct pci_conf *match_buf)
523e9ed3a70SBrooks Davis {
524e9ed3a70SBrooks Davis 
525e9ed3a70SBrooks Davis 	switch (cmd) {
526e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
527e9ed3a70SBrooks Davis 		return (pci_conf_match_native(
528e9ed3a70SBrooks Davis 		    (struct pci_match_conf *)matches, num_matches, match_buf));
529b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
530b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
531b7edb6faSBrooks Davis 		return (pci_conf_match32((struct pci_match_conf32 *)matches,
532b7edb6faSBrooks Davis 		    num_matches, match_buf));
533b7edb6faSBrooks Davis #endif
534e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
535e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
536e9ed3a70SBrooks Davis 		return (pci_conf_match_old(
537e9ed3a70SBrooks Davis 		    (struct pci_match_conf_old *)matches, num_matches,
538e9ed3a70SBrooks Davis 		    match_buf));
539e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32
540e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD32:
541e9ed3a70SBrooks Davis 		return (pci_conf_match_old32(
542e9ed3a70SBrooks Davis 		    (struct pci_match_conf_old32 *)matches, num_matches,
543e9ed3a70SBrooks Davis 		    match_buf));
544e9ed3a70SBrooks Davis #endif
545e9ed3a70SBrooks Davis #endif
546e9ed3a70SBrooks Davis 	default:
547e9ed3a70SBrooks Davis 		/* programmer error */
548e9ed3a70SBrooks Davis 		return (0);
549e9ed3a70SBrooks Davis 	}
550e9ed3a70SBrooks Davis }
55133d3fffaSMarius Strobl 
55274aa2d49SJohn Baldwin /*
55374aa2d49SJohn Baldwin  * Like PVE_NEXT but takes an explicit length since 'pve' is a user
55474aa2d49SJohn Baldwin  * pointer that cannot be dereferenced.
55574aa2d49SJohn Baldwin  */
55674aa2d49SJohn Baldwin #define	PVE_NEXT_LEN(pve, datalen)					\
55774aa2d49SJohn Baldwin 	((struct pci_vpd_element *)((char *)(pve) +			\
55874aa2d49SJohn Baldwin 	    sizeof(struct pci_vpd_element) + (datalen)))
55974aa2d49SJohn Baldwin 
5608983cfbfSMike Smith static int
56184b755dfSJohn Baldwin pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
56284b755dfSJohn Baldwin {
56384b755dfSJohn Baldwin 	struct pci_vpd_element vpd_element, *vpd_user;
56484b755dfSJohn Baldwin 	struct pcicfg_vpd *vpd;
56584b755dfSJohn Baldwin 	size_t len;
56684b755dfSJohn Baldwin 	int error, i;
56784b755dfSJohn Baldwin 
56884b755dfSJohn Baldwin 	vpd = pci_fetch_vpd_list(dev);
56984b755dfSJohn Baldwin 	if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL)
57084b755dfSJohn Baldwin 		return (ENXIO);
57184b755dfSJohn Baldwin 
57284b755dfSJohn Baldwin 	/*
57384b755dfSJohn Baldwin 	 * Calculate the amount of space needed in the data buffer.  An
57484b755dfSJohn Baldwin 	 * identifier element is always present followed by the read-only
57584b755dfSJohn Baldwin 	 * and read-write keywords.
57684b755dfSJohn Baldwin 	 */
57784b755dfSJohn Baldwin 	len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident);
57884b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_rocnt; i++)
57984b755dfSJohn Baldwin 		len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len;
58084b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_wcnt; i++)
58184b755dfSJohn Baldwin 		len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len;
58284b755dfSJohn Baldwin 
58384b755dfSJohn Baldwin 	if (lvio->plvi_len == 0) {
58484b755dfSJohn Baldwin 		lvio->plvi_len = len;
58584b755dfSJohn Baldwin 		return (0);
58684b755dfSJohn Baldwin 	}
58784b755dfSJohn Baldwin 	if (lvio->plvi_len < len) {
58884b755dfSJohn Baldwin 		lvio->plvi_len = len;
58984b755dfSJohn Baldwin 		return (ENOMEM);
59084b755dfSJohn Baldwin 	}
59184b755dfSJohn Baldwin 
59284b755dfSJohn Baldwin 	/*
59384b755dfSJohn Baldwin 	 * Copyout the identifier string followed by each keyword and
59484b755dfSJohn Baldwin 	 * value.
59584b755dfSJohn Baldwin 	 */
59684b755dfSJohn Baldwin 	vpd_user = lvio->plvi_data;
59784b755dfSJohn Baldwin 	vpd_element.pve_keyword[0] = '\0';
59884b755dfSJohn Baldwin 	vpd_element.pve_keyword[1] = '\0';
59984b755dfSJohn Baldwin 	vpd_element.pve_flags = PVE_FLAG_IDENT;
60084b755dfSJohn Baldwin 	vpd_element.pve_datalen = strlen(vpd->vpd_ident);
60184b755dfSJohn Baldwin 	error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
60284b755dfSJohn Baldwin 	if (error)
60384b755dfSJohn Baldwin 		return (error);
60484b755dfSJohn Baldwin 	error = copyout(vpd->vpd_ident, vpd_user->pve_data,
60584b755dfSJohn Baldwin 	    strlen(vpd->vpd_ident));
60684b755dfSJohn Baldwin 	if (error)
60784b755dfSJohn Baldwin 		return (error);
60874aa2d49SJohn Baldwin 	vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
60984b755dfSJohn Baldwin 	vpd_element.pve_flags = 0;
61084b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_rocnt; i++) {
61184b755dfSJohn Baldwin 		vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
61284b755dfSJohn Baldwin 		vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1];
61384b755dfSJohn Baldwin 		vpd_element.pve_datalen = vpd->vpd_ros[i].len;
61484b755dfSJohn Baldwin 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
61584b755dfSJohn Baldwin 		if (error)
61684b755dfSJohn Baldwin 			return (error);
61784b755dfSJohn Baldwin 		error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data,
61884b755dfSJohn Baldwin 		    vpd->vpd_ros[i].len);
61984b755dfSJohn Baldwin 		if (error)
62084b755dfSJohn Baldwin 			return (error);
62174aa2d49SJohn Baldwin 		vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
62284b755dfSJohn Baldwin 	}
62384b755dfSJohn Baldwin 	vpd_element.pve_flags = PVE_FLAG_RW;
62484b755dfSJohn Baldwin 	for (i = 0; i < vpd->vpd_wcnt; i++) {
62584b755dfSJohn Baldwin 		vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0];
62684b755dfSJohn Baldwin 		vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1];
62784b755dfSJohn Baldwin 		vpd_element.pve_datalen = vpd->vpd_w[i].len;
62884b755dfSJohn Baldwin 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
62984b755dfSJohn Baldwin 		if (error)
63084b755dfSJohn Baldwin 			return (error);
63184b755dfSJohn Baldwin 		error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data,
63284b755dfSJohn Baldwin 		    vpd->vpd_w[i].len);
63384b755dfSJohn Baldwin 		if (error)
63484b755dfSJohn Baldwin 			return (error);
63574aa2d49SJohn Baldwin 		vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);
63684b755dfSJohn Baldwin 	}
63784b755dfSJohn Baldwin 	KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
63884b755dfSJohn Baldwin 	    ("length mismatch"));
63984b755dfSJohn Baldwin 	lvio->plvi_len = len;
64084b755dfSJohn Baldwin 	return (0);
64184b755dfSJohn Baldwin }
64284b755dfSJohn Baldwin 
643e9ed3a70SBrooks Davis static size_t
644e9ed3a70SBrooks Davis pci_match_conf_size(u_long cmd)
6458983cfbfSMike Smith {
6468983cfbfSMike Smith 
64784b755dfSJohn Baldwin 	switch (cmd) {
648e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
649e9ed3a70SBrooks Davis 		return (sizeof(struct pci_match_conf));
650b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
651b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
652b7edb6faSBrooks Davis 		return (sizeof(struct pci_match_conf32));
653b7edb6faSBrooks Davis #endif
65484b755dfSJohn Baldwin #ifdef PRE7_COMPAT
655e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
656e9ed3a70SBrooks Davis 		return (sizeof(struct pci_match_conf_old));
65784b755dfSJohn Baldwin #ifdef COMPAT_FREEBSD32
65884b755dfSJohn Baldwin 	case PCIOCGETCONF_OLD32:
659e9ed3a70SBrooks Davis 		return (sizeof(struct pci_match_conf_old32));
66084b755dfSJohn Baldwin #endif
661e9ed3a70SBrooks Davis #endif
662e9ed3a70SBrooks Davis 	default:
663e9ed3a70SBrooks Davis 		/* programmer error */
664e9ed3a70SBrooks Davis 		return (0);
665e9ed3a70SBrooks Davis 	}
666e9ed3a70SBrooks Davis }
667e9ed3a70SBrooks Davis 
668e9ed3a70SBrooks Davis static size_t
669e9ed3a70SBrooks Davis pci_conf_size(u_long cmd)
670e9ed3a70SBrooks Davis {
671e9ed3a70SBrooks Davis 
672e9ed3a70SBrooks Davis 	switch (cmd) {
673e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
674e9ed3a70SBrooks Davis 		return (sizeof(struct pci_conf));
675b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
676b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
677b7edb6faSBrooks Davis 		return (sizeof(struct pci_conf32));
678b7edb6faSBrooks Davis #endif
679e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
680e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
681e9ed3a70SBrooks Davis 		return (sizeof(struct pci_conf_old));
682e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32
683e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD32:
684e9ed3a70SBrooks Davis 		return (sizeof(struct pci_conf_old32));
685e9ed3a70SBrooks Davis #endif
686e9ed3a70SBrooks Davis #endif
687e9ed3a70SBrooks Davis 	default:
688e9ed3a70SBrooks Davis 		/* programmer error */
689e9ed3a70SBrooks Davis 		return (0);
690e9ed3a70SBrooks Davis 	}
691e9ed3a70SBrooks Davis }
692e9ed3a70SBrooks Davis 
693e9ed3a70SBrooks Davis static void
694e9ed3a70SBrooks Davis pci_conf_io_init(struct pci_conf_io *cio, caddr_t data, u_long cmd)
695e9ed3a70SBrooks Davis {
696b7edb6faSBrooks Davis #if defined(COMPAT_FREEBSD32)
697e9ed3a70SBrooks Davis 	struct pci_conf_io32 *cio32;
698e9ed3a70SBrooks Davis #endif
699e9ed3a70SBrooks Davis 
700e9ed3a70SBrooks Davis 	switch (cmd) {
701e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
702e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
70384b755dfSJohn Baldwin 	case PCIOCGETCONF_OLD:
70484b755dfSJohn Baldwin #endif
705e9ed3a70SBrooks Davis 		*cio = *(struct pci_conf_io *)data;
706e9ed3a70SBrooks Davis 		return;
70784b755dfSJohn Baldwin 
708b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
709b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
710b7edb6faSBrooks Davis #ifdef PRE7_COMPAT
711b01bf72bSMaxim Sobolev 	case PCIOCGETCONF_OLD32:
712b7edb6faSBrooks Davis #endif
713b01bf72bSMaxim Sobolev                cio32 = (struct pci_conf_io32 *)data;
714b01bf72bSMaxim Sobolev                cio->pat_buf_len = cio32->pat_buf_len;
715b01bf72bSMaxim Sobolev                cio->num_patterns = cio32->num_patterns;
716b01bf72bSMaxim Sobolev                cio->patterns = (void *)(uintptr_t)cio32->patterns;
717b01bf72bSMaxim Sobolev                cio->match_buf_len = cio32->match_buf_len;
718b01bf72bSMaxim Sobolev                cio->num_matches = cio32->num_matches;
719b01bf72bSMaxim Sobolev                cio->matches = (void *)(uintptr_t)cio32->matches;
720b01bf72bSMaxim Sobolev                cio->offset = cio32->offset;
721b01bf72bSMaxim Sobolev                cio->generation = cio32->generation;
722b01bf72bSMaxim Sobolev                cio->status = cio32->status;
723e9ed3a70SBrooks Davis                return;
724904c3909SGleb Smirnoff #endif
725e9ed3a70SBrooks Davis 
726e9ed3a70SBrooks Davis 	default:
727e9ed3a70SBrooks Davis 		/* programmer error */
728e9ed3a70SBrooks Davis 		return;
729e9ed3a70SBrooks Davis 	}
730904c3909SGleb Smirnoff }
731904c3909SGleb Smirnoff 
732e9ed3a70SBrooks Davis static void
733e9ed3a70SBrooks Davis pci_conf_io_update_data(const struct pci_conf_io *cio, caddr_t data,
734e9ed3a70SBrooks Davis     u_long cmd)
735e9ed3a70SBrooks Davis {
736e9ed3a70SBrooks Davis 	struct pci_conf_io *d_cio;
737b7edb6faSBrooks Davis #if defined(COMPAT_FREEBSD32)
738e9ed3a70SBrooks Davis 	struct pci_conf_io32 *cio32;
739e9ed3a70SBrooks Davis #endif
740e9ed3a70SBrooks Davis 
741904c3909SGleb Smirnoff 	switch (cmd) {
742e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
743e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
744e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
745e9ed3a70SBrooks Davis #endif
746e9ed3a70SBrooks Davis 		d_cio = (struct pci_conf_io *)data;
747e9ed3a70SBrooks Davis 		d_cio->status = cio->status;
748e9ed3a70SBrooks Davis 		d_cio->generation = cio->generation;
749e9ed3a70SBrooks Davis 		d_cio->offset = cio->offset;
750e9ed3a70SBrooks Davis 		d_cio->num_matches = cio->num_matches;
751e9ed3a70SBrooks Davis 		return;
752e9ed3a70SBrooks Davis 
753b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
754b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
755b7edb6faSBrooks Davis #ifdef PRE7_COMPAT
756e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD32:
757b7edb6faSBrooks Davis #endif
758e9ed3a70SBrooks Davis 		cio32 = (struct pci_conf_io32 *)data;
759e9ed3a70SBrooks Davis 
760e9ed3a70SBrooks Davis 		cio32->status = cio->status;
761e9ed3a70SBrooks Davis 		cio32->generation = cio->generation;
762e9ed3a70SBrooks Davis 		cio32->offset = cio->offset;
763e9ed3a70SBrooks Davis 		cio32->num_matches = cio->num_matches;
764e9ed3a70SBrooks Davis 		return;
765e9ed3a70SBrooks Davis #endif
766e9ed3a70SBrooks Davis 
767e9ed3a70SBrooks Davis 	default:
768e9ed3a70SBrooks Davis 		/* programmer error */
769e9ed3a70SBrooks Davis 		return;
770e9ed3a70SBrooks Davis 	}
771e9ed3a70SBrooks Davis }
772e9ed3a70SBrooks Davis 
773e9ed3a70SBrooks Davis static void
774e9ed3a70SBrooks Davis pci_conf_for_copyout(const struct pci_conf *pcp, union pci_conf_union *pcup,
775e9ed3a70SBrooks Davis     u_long cmd)
776e9ed3a70SBrooks Davis {
777e9ed3a70SBrooks Davis 
778e9ed3a70SBrooks Davis 	memset(pcup, 0, sizeof(*pcup));
779e9ed3a70SBrooks Davis 
780e9ed3a70SBrooks Davis 	switch (cmd) {
781e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
782e9ed3a70SBrooks Davis 		pcup->pc = *pcp;
783e9ed3a70SBrooks Davis 		return;
784e9ed3a70SBrooks Davis 
785b7edb6faSBrooks Davis #ifdef COMPAT_FREEBSD32
786b7edb6faSBrooks Davis 	case PCIOCGETCONF32:
787b7edb6faSBrooks Davis 		pcup->pc32.pc_sel = pcp->pc_sel;
788b7edb6faSBrooks Davis 		pcup->pc32.pc_hdr = pcp->pc_hdr;
789b7edb6faSBrooks Davis 		pcup->pc32.pc_subvendor = pcp->pc_subvendor;
790b7edb6faSBrooks Davis 		pcup->pc32.pc_subdevice = pcp->pc_subdevice;
791b7edb6faSBrooks Davis 		pcup->pc32.pc_vendor = pcp->pc_vendor;
792b7edb6faSBrooks Davis 		pcup->pc32.pc_device = pcp->pc_device;
793b7edb6faSBrooks Davis 		pcup->pc32.pc_class = pcp->pc_class;
794b7edb6faSBrooks Davis 		pcup->pc32.pc_subclass = pcp->pc_subclass;
795b7edb6faSBrooks Davis 		pcup->pc32.pc_progif = pcp->pc_progif;
796b7edb6faSBrooks Davis 		pcup->pc32.pc_revid = pcp->pc_revid;
797b7edb6faSBrooks Davis 		strlcpy(pcup->pc32.pd_name, pcp->pd_name,
798b7edb6faSBrooks Davis 		    sizeof(pcup->pc32.pd_name));
799b7edb6faSBrooks Davis 		pcup->pc32.pd_unit = (uint32_t)pcp->pd_unit;
800b7edb6faSBrooks Davis 		return;
801b7edb6faSBrooks Davis #endif
802b7edb6faSBrooks Davis 
803904c3909SGleb Smirnoff #ifdef PRE7_COMPAT
804904c3909SGleb Smirnoff #ifdef COMPAT_FREEBSD32
805904c3909SGleb Smirnoff 	case PCIOCGETCONF_OLD32:
806e9ed3a70SBrooks Davis 		pcup->pco32.pc_sel.pc_bus = pcp->pc_sel.pc_bus;
807e9ed3a70SBrooks Davis 		pcup->pco32.pc_sel.pc_dev = pcp->pc_sel.pc_dev;
808e9ed3a70SBrooks Davis 		pcup->pco32.pc_sel.pc_func = pcp->pc_sel.pc_func;
809e9ed3a70SBrooks Davis 		pcup->pco32.pc_hdr = pcp->pc_hdr;
810e9ed3a70SBrooks Davis 		pcup->pco32.pc_subvendor = pcp->pc_subvendor;
811e9ed3a70SBrooks Davis 		pcup->pco32.pc_subdevice = pcp->pc_subdevice;
812e9ed3a70SBrooks Davis 		pcup->pco32.pc_vendor = pcp->pc_vendor;
813e9ed3a70SBrooks Davis 		pcup->pco32.pc_device = pcp->pc_device;
814e9ed3a70SBrooks Davis 		pcup->pco32.pc_class = pcp->pc_class;
815e9ed3a70SBrooks Davis 		pcup->pco32.pc_subclass = pcp->pc_subclass;
816e9ed3a70SBrooks Davis 		pcup->pco32.pc_progif = pcp->pc_progif;
817e9ed3a70SBrooks Davis 		pcup->pco32.pc_revid = pcp->pc_revid;
818e9ed3a70SBrooks Davis 		strlcpy(pcup->pco32.pd_name, pcp->pd_name,
819e9ed3a70SBrooks Davis 		    sizeof(pcup->pco32.pd_name));
820e9ed3a70SBrooks Davis 		pcup->pco32.pd_unit = (uint32_t)pcp->pd_unit;
821e9ed3a70SBrooks Davis 		return;
8228983cfbfSMike Smith 
823e9ed3a70SBrooks Davis #endif /* COMPAT_FREEBSD32 */
824e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
825e9ed3a70SBrooks Davis 		pcup->pco.pc_sel.pc_bus = pcp->pc_sel.pc_bus;
826e9ed3a70SBrooks Davis 		pcup->pco.pc_sel.pc_dev = pcp->pc_sel.pc_dev;
827e9ed3a70SBrooks Davis 		pcup->pco.pc_sel.pc_func = pcp->pc_sel.pc_func;
828e9ed3a70SBrooks Davis 		pcup->pco.pc_hdr = pcp->pc_hdr;
829e9ed3a70SBrooks Davis 		pcup->pco.pc_subvendor = pcp->pc_subvendor;
830e9ed3a70SBrooks Davis 		pcup->pco.pc_subdevice = pcp->pc_subdevice;
831e9ed3a70SBrooks Davis 		pcup->pco.pc_vendor = pcp->pc_vendor;
832e9ed3a70SBrooks Davis 		pcup->pco.pc_device = pcp->pc_device;
833e9ed3a70SBrooks Davis 		pcup->pco.pc_class = pcp->pc_class;
834e9ed3a70SBrooks Davis 		pcup->pco.pc_subclass = pcp->pc_subclass;
835e9ed3a70SBrooks Davis 		pcup->pco.pc_progif = pcp->pc_progif;
836e9ed3a70SBrooks Davis 		pcup->pco.pc_revid = pcp->pc_revid;
837e9ed3a70SBrooks Davis 		strlcpy(pcup->pco.pd_name, pcp->pd_name,
838e9ed3a70SBrooks Davis 		    sizeof(pcup->pco.pd_name));
839e9ed3a70SBrooks Davis 		pcup->pco.pd_unit = pcp->pd_unit;
840e9ed3a70SBrooks Davis 		return;
841e9ed3a70SBrooks Davis #endif /* PRE7_COMPAT */
842e9ed3a70SBrooks Davis 
843e9ed3a70SBrooks Davis 	default:
844e9ed3a70SBrooks Davis 		/* programmer error */
845e9ed3a70SBrooks Davis 		return;
846e9ed3a70SBrooks Davis 	}
847e9ed3a70SBrooks Davis }
848e9ed3a70SBrooks Davis 
849e9ed3a70SBrooks Davis static int
85087842989SKonstantin Belousov pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm)
85187842989SKonstantin Belousov {
85287842989SKonstantin Belousov 	vm_map_t map;
85387842989SKonstantin Belousov 	vm_object_t obj;
85487842989SKonstantin Belousov 	struct thread *td;
85587842989SKonstantin Belousov 	struct sglist *sg;
85687842989SKonstantin Belousov 	struct pci_map *pm;
8579857e00aSMarcin Wojtas 	rman_res_t membase;
85887842989SKonstantin Belousov 	vm_paddr_t pbase;
85987842989SKonstantin Belousov 	vm_size_t plen;
86087842989SKonstantin Belousov 	vm_offset_t addr;
86187842989SKonstantin Belousov 	vm_prot_t prot;
86287842989SKonstantin Belousov 	int error, flags;
86387842989SKonstantin Belousov 
86487842989SKonstantin Belousov 	td = curthread;
86587842989SKonstantin Belousov 	map = &td->td_proc->p_vmspace->vm_map;
86687842989SKonstantin Belousov 	if ((pbm->pbm_flags & ~(PCIIO_BAR_MMAP_FIXED | PCIIO_BAR_MMAP_EXCL |
86787842989SKonstantin Belousov 	    PCIIO_BAR_MMAP_RW | PCIIO_BAR_MMAP_ACTIVATE)) != 0 ||
86887842989SKonstantin Belousov 	    pbm->pbm_memattr != (vm_memattr_t)pbm->pbm_memattr ||
86987842989SKonstantin Belousov 	    !pmap_is_valid_memattr(map->pmap, pbm->pbm_memattr))
87087842989SKonstantin Belousov 		return (EINVAL);
87187842989SKonstantin Belousov 
87287842989SKonstantin Belousov 	/* Fetch the BAR physical base and length. */
87387842989SKonstantin Belousov 	pm = pci_find_bar(pcidev, pbm->pbm_reg);
87487842989SKonstantin Belousov 	if (pm == NULL)
87587842989SKonstantin Belousov 		return (EINVAL);
87687842989SKonstantin Belousov 	if (!pci_bar_enabled(pcidev, pm))
87787842989SKonstantin Belousov 		return (EBUSY); /* XXXKIB enable if _ACTIVATE */
87887842989SKonstantin Belousov 	if (!PCI_BAR_MEM(pm->pm_value))
87987842989SKonstantin Belousov 		return (EIO);
8809857e00aSMarcin Wojtas 	error = BUS_TRANSLATE_RESOURCE(pcidev, SYS_RES_MEMORY,
8819857e00aSMarcin Wojtas 	    pm->pm_value & PCIM_BAR_MEM_BASE, &membase);
882f2f1ab39SMarcin Wojtas 	if (error != 0)
883f2f1ab39SMarcin Wojtas 		return (error);
884f2f1ab39SMarcin Wojtas 
885f48c41acSHans Petter Selasky 	pbase = trunc_page(membase);
886f48c41acSHans Petter Selasky 	plen = round_page(membase + ((pci_addr_t)1 << pm->pm_size)) -
88787842989SKonstantin Belousov 	    pbase;
88887842989SKonstantin Belousov 	prot = VM_PROT_READ | (((pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) ?
88987842989SKonstantin Belousov 	    VM_PROT_WRITE : 0);
89087842989SKonstantin Belousov 
89187842989SKonstantin Belousov 	/* Create vm structures and mmap. */
89287842989SKonstantin Belousov 	sg = sglist_alloc(1, M_WAITOK);
89387842989SKonstantin Belousov 	error = sglist_append_phys(sg, pbase, plen);
89487842989SKonstantin Belousov 	if (error != 0)
89587842989SKonstantin Belousov 		goto out;
89687842989SKonstantin Belousov 	obj = vm_pager_allocate(OBJT_SG, sg, plen, prot, 0, td->td_ucred);
89787842989SKonstantin Belousov 	if (obj == NULL) {
89887842989SKonstantin Belousov 		error = EIO;
89987842989SKonstantin Belousov 		goto out;
90087842989SKonstantin Belousov 	}
90187842989SKonstantin Belousov 	obj->memattr = pbm->pbm_memattr;
90287842989SKonstantin Belousov 	flags = MAP_SHARED;
90387842989SKonstantin Belousov 	addr = 0;
90487842989SKonstantin Belousov 	if ((pbm->pbm_flags & PCIIO_BAR_MMAP_FIXED) != 0) {
90587842989SKonstantin Belousov 		addr = (uintptr_t)pbm->pbm_map_base;
90687842989SKonstantin Belousov 		flags |= MAP_FIXED;
90787842989SKonstantin Belousov 	}
90887842989SKonstantin Belousov 	if ((pbm->pbm_flags & PCIIO_BAR_MMAP_EXCL) != 0)
90987842989SKonstantin Belousov 		flags |= MAP_CHECK_EXCL;
91087842989SKonstantin Belousov 	error = vm_mmap_object(map, &addr, plen, prot, prot, flags, obj, 0,
91187842989SKonstantin Belousov 	    FALSE, td);
91287842989SKonstantin Belousov 	if (error != 0) {
91387842989SKonstantin Belousov 		vm_object_deallocate(obj);
91487842989SKonstantin Belousov 		goto out;
91587842989SKonstantin Belousov 	}
91687842989SKonstantin Belousov 	pbm->pbm_map_base = (void *)addr;
91787842989SKonstantin Belousov 	pbm->pbm_map_length = plen;
918f48c41acSHans Petter Selasky 	pbm->pbm_bar_off = membase - pbase;
91987842989SKonstantin Belousov 	pbm->pbm_bar_length = (pci_addr_t)1 << pm->pm_size;
92087842989SKonstantin Belousov 
92187842989SKonstantin Belousov out:
92287842989SKonstantin Belousov 	sglist_free(sg);
92387842989SKonstantin Belousov 	return (error);
92487842989SKonstantin Belousov }
92587842989SKonstantin Belousov 
92687842989SKonstantin Belousov static int
927*7e14be0bSMark Johnston pci_bar_io(device_t pcidev, struct pci_bar_ioreq *pbi)
928*7e14be0bSMark Johnston {
929*7e14be0bSMark Johnston 	struct pci_map *pm;
930*7e14be0bSMark Johnston 	struct resource *res;
931*7e14be0bSMark Johnston 	uint32_t offset, width;
932*7e14be0bSMark Johnston 	int bar, error, type;
933*7e14be0bSMark Johnston 
934*7e14be0bSMark Johnston 	if (pbi->pbi_op != PCIBARIO_READ &&
935*7e14be0bSMark Johnston 	    pbi->pbi_op != PCIBARIO_WRITE)
936*7e14be0bSMark Johnston 		return (EINVAL);
937*7e14be0bSMark Johnston 
938*7e14be0bSMark Johnston 	bar = PCIR_BAR(pbi->pbi_bar);
939*7e14be0bSMark Johnston 	pm = pci_find_bar(pcidev, bar);
940*7e14be0bSMark Johnston 	if (pm == NULL)
941*7e14be0bSMark Johnston 		return (EINVAL);
942*7e14be0bSMark Johnston 
943*7e14be0bSMark Johnston 	offset = pbi->pbi_offset;
944*7e14be0bSMark Johnston 	width = pbi->pbi_width;
945*7e14be0bSMark Johnston 
946*7e14be0bSMark Johnston 	if (offset + width < offset ||
947*7e14be0bSMark Johnston 	    ((pci_addr_t)1 << pm->pm_size) < offset + width)
948*7e14be0bSMark Johnston 		return (EINVAL);
949*7e14be0bSMark Johnston 
950*7e14be0bSMark Johnston 	type = PCI_BAR_MEM(pm->pm_value) ? SYS_RES_MEMORY : SYS_RES_IOPORT;
951*7e14be0bSMark Johnston 
952*7e14be0bSMark Johnston 	/*
953*7e14be0bSMark Johnston 	 * This will fail if a driver has allocated the resource.  This could be
954*7e14be0bSMark Johnston 	 * worked around by detecting that case and using bus_map_resource() to
955*7e14be0bSMark Johnston 	 * populate the handle, but so far this is not needed.
956*7e14be0bSMark Johnston 	 */
957*7e14be0bSMark Johnston 	res = bus_alloc_resource_any(pcidev, type, &bar, RF_ACTIVE);
958*7e14be0bSMark Johnston 	if (res == NULL)
959*7e14be0bSMark Johnston 		return (ENOENT);
960*7e14be0bSMark Johnston 
961*7e14be0bSMark Johnston 	error = 0;
962*7e14be0bSMark Johnston 	switch (pbi->pbi_op) {
963*7e14be0bSMark Johnston 	case PCIBARIO_READ:
964*7e14be0bSMark Johnston 		switch (pbi->pbi_width) {
965*7e14be0bSMark Johnston 		case 1:
966*7e14be0bSMark Johnston 			pbi->pbi_value = bus_read_1(res, offset);
967*7e14be0bSMark Johnston 			break;
968*7e14be0bSMark Johnston 		case 2:
969*7e14be0bSMark Johnston 			pbi->pbi_value = bus_read_2(res, offset);
970*7e14be0bSMark Johnston 			break;
971*7e14be0bSMark Johnston 		case 4:
972*7e14be0bSMark Johnston 			pbi->pbi_value = bus_read_4(res, offset);
973*7e14be0bSMark Johnston 			break;
974*7e14be0bSMark Johnston #ifndef __i386__
975*7e14be0bSMark Johnston 		case 8:
976*7e14be0bSMark Johnston 			pbi->pbi_value = bus_read_8(res, offset);
977*7e14be0bSMark Johnston 			break;
978*7e14be0bSMark Johnston #endif
979*7e14be0bSMark Johnston 		default:
980*7e14be0bSMark Johnston 			error = EINVAL;
981*7e14be0bSMark Johnston 			break;
982*7e14be0bSMark Johnston 		}
983*7e14be0bSMark Johnston 		break;
984*7e14be0bSMark Johnston 	case PCIBARIO_WRITE:
985*7e14be0bSMark Johnston 		switch (pbi->pbi_width) {
986*7e14be0bSMark Johnston 		case 1:
987*7e14be0bSMark Johnston 			bus_write_1(res, offset, pbi->pbi_value);
988*7e14be0bSMark Johnston 			break;
989*7e14be0bSMark Johnston 		case 2:
990*7e14be0bSMark Johnston 			bus_write_2(res, offset, pbi->pbi_value);
991*7e14be0bSMark Johnston 			break;
992*7e14be0bSMark Johnston 		case 4:
993*7e14be0bSMark Johnston 			bus_write_4(res, offset, pbi->pbi_value);
994*7e14be0bSMark Johnston 			break;
995*7e14be0bSMark Johnston #ifndef __i386__
996*7e14be0bSMark Johnston 		case 8:
997*7e14be0bSMark Johnston 			bus_write_8(res, offset, pbi->pbi_value);
998*7e14be0bSMark Johnston 			break;
999*7e14be0bSMark Johnston #endif
1000*7e14be0bSMark Johnston 		default:
1001*7e14be0bSMark Johnston 			error = EINVAL;
1002*7e14be0bSMark Johnston 			break;
1003*7e14be0bSMark Johnston 		}
1004*7e14be0bSMark Johnston 		break;
1005*7e14be0bSMark Johnston 	}
1006*7e14be0bSMark Johnston 
1007*7e14be0bSMark Johnston 	bus_release_resource(pcidev, type, bar, res);
1008*7e14be0bSMark Johnston 
1009*7e14be0bSMark Johnston 	return (error);
1010*7e14be0bSMark Johnston }
1011*7e14be0bSMark Johnston 
1012*7e14be0bSMark Johnston static int
1013e9ed3a70SBrooks Davis pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
1014e9ed3a70SBrooks Davis {
1015e9ed3a70SBrooks Davis 	device_t pcidev;
1016e9ed3a70SBrooks Davis 	const char *name;
1017e9ed3a70SBrooks Davis 	struct devlist *devlist_head;
1018e9ed3a70SBrooks Davis 	struct pci_conf_io *cio = NULL;
1019e9ed3a70SBrooks Davis 	struct pci_devinfo *dinfo;
1020e9ed3a70SBrooks Davis 	struct pci_io *io;
1021*7e14be0bSMark Johnston 	struct pci_bar_ioreq *pbi;
1022e9ed3a70SBrooks Davis 	struct pci_bar_io *bio;
1023e9ed3a70SBrooks Davis 	struct pci_list_vpd_io *lvio;
1024e9ed3a70SBrooks Davis 	struct pci_match_conf *pattern_buf;
1025e9ed3a70SBrooks Davis 	struct pci_map *pm;
102687842989SKonstantin Belousov 	struct pci_bar_mmap *pbm;
1027e9ed3a70SBrooks Davis 	size_t confsz, iolen;
1028e9ed3a70SBrooks Davis 	int error, ionum, i, num_patterns;
1029e9ed3a70SBrooks Davis 	union pci_conf_union pcu;
1030e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
1031e9ed3a70SBrooks Davis 	struct pci_io iodata;
1032e9ed3a70SBrooks Davis 	struct pci_io_old *io_old;
1033e9ed3a70SBrooks Davis 
1034e9ed3a70SBrooks Davis 	io_old = NULL;
1035e9ed3a70SBrooks Davis #endif
1036e9ed3a70SBrooks Davis 
103785ae35efSKonstantin Belousov 	/*
103885ae35efSKonstantin Belousov 	 * Interpret read-only opened /dev/pci as a promise that no
103985ae35efSKonstantin Belousov 	 * operation of the file descriptor could modify system state,
104085ae35efSKonstantin Belousov 	 * including side-effects due to reading devices registers.
104185ae35efSKonstantin Belousov 	 */
104285ae35efSKonstantin Belousov 	if ((flag & FWRITE) == 0) {
1043e9ed3a70SBrooks Davis 		switch (cmd) {
1044e9ed3a70SBrooks Davis 		case PCIOCGETCONF:
1045b56f51f1SBrooks Davis #ifdef COMPAT_FREEBSD32
1046b56f51f1SBrooks Davis 		case PCIOCGETCONF32:
1047b56f51f1SBrooks Davis #endif
1048e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
1049e9ed3a70SBrooks Davis 		case PCIOCGETCONF_OLD:
1050e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32
1051e9ed3a70SBrooks Davis 		case PCIOCGETCONF_OLD32:
1052e9ed3a70SBrooks Davis #endif
1053e9ed3a70SBrooks Davis #endif
1054e9ed3a70SBrooks Davis 		case PCIOCGETBAR:
1055e9ed3a70SBrooks Davis 		case PCIOCLISTVPD:
1056e9ed3a70SBrooks Davis 			break;
1057e9ed3a70SBrooks Davis 		default:
1058e9ed3a70SBrooks Davis 			return (EPERM);
1059e9ed3a70SBrooks Davis 		}
1060e9ed3a70SBrooks Davis 	}
1061e9ed3a70SBrooks Davis 
106296b506a5SWarner Losh 	/* Giant because newbus is Giant locked revisit with newbus locking */
106396b506a5SWarner Losh 	mtx_lock(&Giant);
106496b506a5SWarner Losh 
1065e9ed3a70SBrooks Davis 	switch (cmd) {
1066e9ed3a70SBrooks Davis 	case PCIOCGETCONF:
1067b56f51f1SBrooks Davis #ifdef COMPAT_FREEBSD32
1068b56f51f1SBrooks Davis 	case PCIOCGETCONF32:
1069b56f51f1SBrooks Davis #endif
1070e9ed3a70SBrooks Davis #ifdef PRE7_COMPAT
1071e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD:
1072e9ed3a70SBrooks Davis #ifdef COMPAT_FREEBSD32
1073e9ed3a70SBrooks Davis 	case PCIOCGETCONF_OLD32:
1074e9ed3a70SBrooks Davis #endif
1075e9ed3a70SBrooks Davis #endif
1076e9ed3a70SBrooks Davis 		cio = malloc(sizeof(struct pci_conf_io), M_TEMP,
1077e9ed3a70SBrooks Davis 		    M_WAITOK | M_ZERO);
1078e9ed3a70SBrooks Davis 		pci_conf_io_init(cio, data, cmd);
107933d3fffaSMarius Strobl 		pattern_buf = NULL;
10808983cfbfSMike Smith 		num_patterns = 0;
10818983cfbfSMike Smith 		dinfo = NULL;
10828983cfbfSMike Smith 
108333d3fffaSMarius Strobl 		cio->num_matches = 0;
108433d3fffaSMarius Strobl 
10858983cfbfSMike Smith 		/*
10868983cfbfSMike Smith 		 * If the user specified an offset into the device list,
10878983cfbfSMike Smith 		 * but the list has changed since they last called this
10888983cfbfSMike Smith 		 * ioctl, tell them that the list has changed.  They will
10898983cfbfSMike Smith 		 * have to get the list from the beginning.
10908983cfbfSMike Smith 		 */
10918983cfbfSMike Smith 		if ((cio->offset != 0)
10928983cfbfSMike Smith 		 && (cio->generation != pci_generation)){
10938983cfbfSMike Smith 			cio->status = PCI_GETCONF_LIST_CHANGED;
10948983cfbfSMike Smith 			error = 0;
1095b01bf72bSMaxim Sobolev 			goto getconfexit;
10968983cfbfSMike Smith 		}
10978983cfbfSMike Smith 
10988983cfbfSMike Smith 		/*
10998983cfbfSMike Smith 		 * Check to see whether the user has asked for an offset
11008983cfbfSMike Smith 		 * past the end of our list.
11018983cfbfSMike Smith 		 */
11028983cfbfSMike Smith 		if (cio->offset >= pci_numdevs) {
11038983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
11048983cfbfSMike Smith 			error = 0;
1105b01bf72bSMaxim Sobolev 			goto getconfexit;
11068983cfbfSMike Smith 		}
11078983cfbfSMike Smith 
11088983cfbfSMike Smith 		/* get the head of the device queue */
11098983cfbfSMike Smith 		devlist_head = &pci_devq;
11108983cfbfSMike Smith 
11118983cfbfSMike Smith 		/*
11128983cfbfSMike Smith 		 * Determine how much room we have for pci_conf structures.
11138983cfbfSMike Smith 		 * Round the user's buffer size down to the nearest
11148983cfbfSMike Smith 		 * multiple of sizeof(struct pci_conf) in case the user
11158983cfbfSMike Smith 		 * didn't specify a multiple of that size.
11168983cfbfSMike Smith 		 */
1117e9ed3a70SBrooks Davis 		confsz = pci_conf_size(cmd);
111833d3fffaSMarius Strobl 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
111933d3fffaSMarius Strobl 		    pci_numdevs * confsz);
11208983cfbfSMike Smith 
11218983cfbfSMike Smith 		/*
11228983cfbfSMike Smith 		 * Since we know that iolen is a multiple of the size of
11238983cfbfSMike Smith 		 * the pciconf union, it's okay to do this.
11248983cfbfSMike Smith 		 */
112533d3fffaSMarius Strobl 		ionum = iolen / confsz;
11268983cfbfSMike Smith 
11278983cfbfSMike Smith 		/*
11288983cfbfSMike Smith 		 * If this test is true, the user wants the pci_conf
11298983cfbfSMike Smith 		 * structures returned to match the supplied entries.
11308983cfbfSMike Smith 		 */
1131e3f932deSJohn-Mark Gurney 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
11328983cfbfSMike Smith 		 && (cio->pat_buf_len > 0)) {
11338983cfbfSMike Smith 			/*
11348983cfbfSMike Smith 			 * pat_buf_len needs to be:
11358983cfbfSMike Smith 			 * num_patterns * sizeof(struct pci_match_conf)
11368983cfbfSMike Smith 			 * While it is certainly possible the user just
11378983cfbfSMike Smith 			 * allocated a large buffer, but set the number of
11388983cfbfSMike Smith 			 * matches correctly, it is far more likely that
11398983cfbfSMike Smith 			 * their kernel doesn't match the userland utility
11408983cfbfSMike Smith 			 * they're using.  It's also possible that the user
11418983cfbfSMike Smith 			 * forgot to initialize some variables.  Yes, this
11428983cfbfSMike Smith 			 * may be overly picky, but I hazard to guess that
11438983cfbfSMike Smith 			 * it's far more likely to just catch folks that
11448983cfbfSMike Smith 			 * updated their kernel but not their userland.
11458983cfbfSMike Smith 			 */
1146e9ed3a70SBrooks Davis 			if (cio->num_patterns * pci_match_conf_size(cmd) !=
1147e9ed3a70SBrooks Davis 			    cio->pat_buf_len) {
114833d3fffaSMarius Strobl 				/* The user made a mistake, return an error. */
11498983cfbfSMike Smith 				cio->status = PCI_GETCONF_ERROR;
11508983cfbfSMike Smith 				error = EINVAL;
1151b01bf72bSMaxim Sobolev 				goto getconfexit;
11528983cfbfSMike Smith 			}
11538983cfbfSMike Smith 
11548983cfbfSMike Smith 			/*
11558983cfbfSMike Smith 			 * Allocate a buffer to hold the patterns.
11568983cfbfSMike Smith 			 */
11578983cfbfSMike Smith 			pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
1158a163d034SWarner Losh 			    M_WAITOK);
11598983cfbfSMike Smith 			error = copyin(cio->patterns, pattern_buf,
11608983cfbfSMike Smith 			    cio->pat_buf_len);
1161d08239c1SJohn-Mark Gurney 			if (error != 0) {
1162d08239c1SJohn-Mark Gurney 				error = EINVAL;
1163d08239c1SJohn-Mark Gurney 				goto getconfexit;
1164d08239c1SJohn-Mark Gurney 			}
11658983cfbfSMike Smith 			num_patterns = cio->num_patterns;
11668983cfbfSMike Smith 		} else if ((cio->num_patterns > 0)
11678983cfbfSMike Smith 			|| (cio->pat_buf_len > 0)) {
11688983cfbfSMike Smith 			/*
11698983cfbfSMike Smith 			 * The user made a mistake, spit out an error.
11708983cfbfSMike Smith 			 */
11718983cfbfSMike Smith 			cio->status = PCI_GETCONF_ERROR;
11728983cfbfSMike Smith 			error = EINVAL;
1173b01bf72bSMaxim Sobolev                        goto getconfexit;
117433d3fffaSMarius Strobl 		}
11758983cfbfSMike Smith 
11768983cfbfSMike Smith 		/*
11778983cfbfSMike Smith 		 * Go through the list of devices and copy out the devices
11788983cfbfSMike Smith 		 * that match the user's criteria.
11798983cfbfSMike Smith 		 */
118010012d53SJohn Baldwin 		for (cio->num_matches = 0, i = 0,
11818983cfbfSMike Smith 				 dinfo = STAILQ_FIRST(devlist_head);
118210012d53SJohn Baldwin 		     dinfo != NULL;
11838983cfbfSMike Smith 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
11848983cfbfSMike Smith 			if (i < cio->offset)
11858983cfbfSMike Smith 				continue;
11868983cfbfSMike Smith 
11878983cfbfSMike Smith 			/* Populate pd_name and pd_unit */
11888983cfbfSMike Smith 			name = NULL;
11890678f786SJohn Baldwin 			if (dinfo->cfg.dev)
11908983cfbfSMike Smith 				name = device_get_name(dinfo->cfg.dev);
11918983cfbfSMike Smith 			if (name) {
11928983cfbfSMike Smith 				strncpy(dinfo->conf.pd_name, name,
11938983cfbfSMike Smith 					sizeof(dinfo->conf.pd_name));
11948983cfbfSMike Smith 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
11958983cfbfSMike Smith 				dinfo->conf.pd_unit =
11968983cfbfSMike Smith 					device_get_unit(dinfo->cfg.dev);
11970678f786SJohn Baldwin 			} else {
11980678f786SJohn Baldwin 				dinfo->conf.pd_name[0] = '\0';
11990678f786SJohn Baldwin 				dinfo->conf.pd_unit = 0;
12008983cfbfSMike Smith 			}
12018983cfbfSMike Smith 
120233d3fffaSMarius Strobl 			if (pattern_buf == NULL ||
1203e9ed3a70SBrooks Davis 			    pci_conf_match(cmd, pattern_buf, num_patterns,
120433d3fffaSMarius Strobl 			    &dinfo->conf) == 0) {
12058983cfbfSMike Smith 				/*
12068983cfbfSMike Smith 				 * If we've filled up the user's buffer,
12078983cfbfSMike Smith 				 * break out at this point.  Since we've
12088983cfbfSMike Smith 				 * got a match here, we'll pick right back
12098983cfbfSMike Smith 				 * up at the matching entry.  We can also
12108983cfbfSMike Smith 				 * tell the user that there are more matches
12118983cfbfSMike Smith 				 * left.
12128983cfbfSMike Smith 				 */
1213fadd3f8aSConrad Meyer 				if (cio->num_matches >= ionum) {
1214fadd3f8aSConrad Meyer 					error = 0;
12158983cfbfSMike Smith 					break;
1216fadd3f8aSConrad Meyer 				}
12178983cfbfSMike Smith 
1218e9ed3a70SBrooks Davis 				pci_conf_for_copyout(&dinfo->conf, &pcu, cmd);
1219e9ed3a70SBrooks Davis 				error = copyout(&pcu,
1220c5860546SMarius Strobl 				    (caddr_t)cio->matches +
122110012d53SJohn Baldwin 				    confsz * cio->num_matches, confsz);
122210012d53SJohn Baldwin 				if (error)
122310012d53SJohn Baldwin 					break;
122433d3fffaSMarius Strobl 				cio->num_matches++;
12258983cfbfSMike Smith 			}
1226d08239c1SJohn-Mark Gurney 		}
12278983cfbfSMike Smith 
12288983cfbfSMike Smith 		/*
12298983cfbfSMike Smith 		 * Set the pointer into the list, so if the user is getting
12308983cfbfSMike Smith 		 * n records at a time, where n < pci_numdevs,
12318983cfbfSMike Smith 		 */
12328983cfbfSMike Smith 		cio->offset = i;
12338983cfbfSMike Smith 
12348983cfbfSMike Smith 		/*
12358983cfbfSMike Smith 		 * Set the generation, the user will need this if they make
12368983cfbfSMike Smith 		 * another ioctl call with offset != 0.
12378983cfbfSMike Smith 		 */
12388983cfbfSMike Smith 		cio->generation = pci_generation;
12398983cfbfSMike Smith 
12408983cfbfSMike Smith 		/*
12418983cfbfSMike Smith 		 * If this is the last device, inform the user so he won't
12428983cfbfSMike Smith 		 * bother asking for more devices.  If dinfo isn't NULL, we
12438983cfbfSMike Smith 		 * know that there are more matches in the list because of
12448983cfbfSMike Smith 		 * the way the traversal is done.
12458983cfbfSMike Smith 		 */
12468983cfbfSMike Smith 		if (dinfo == NULL)
12478983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
12488983cfbfSMike Smith 		else
12498983cfbfSMike Smith 			cio->status = PCI_GETCONF_MORE_DEVS;
12508983cfbfSMike Smith 
1251d08239c1SJohn-Mark Gurney getconfexit:
1252e9ed3a70SBrooks Davis 		pci_conf_io_update_data(cio, data, cmd);
1253b01bf72bSMaxim Sobolev 		free(cio, M_TEMP);
1254904c3909SGleb Smirnoff 		free(pattern_buf, M_TEMP);
12558983cfbfSMike Smith 
12568983cfbfSMike Smith 		break;
12578983cfbfSMike Smith 
1258b2068c0cSWarner Losh #ifdef PRE7_COMPAT
125933d3fffaSMarius Strobl 	case PCIOCREAD_OLD:
126033d3fffaSMarius Strobl 	case PCIOCWRITE_OLD:
126133d3fffaSMarius Strobl 		io_old = (struct pci_io_old *)data;
126233d3fffaSMarius Strobl 		iodata.pi_sel.pc_domain = 0;
126333d3fffaSMarius Strobl 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
126433d3fffaSMarius Strobl 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
126533d3fffaSMarius Strobl 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
126633d3fffaSMarius Strobl 		iodata.pi_reg = io_old->pi_reg;
126733d3fffaSMarius Strobl 		iodata.pi_width = io_old->pi_width;
126833d3fffaSMarius Strobl 		iodata.pi_data = io_old->pi_data;
126933d3fffaSMarius Strobl 		data = (caddr_t)&iodata;
127033d3fffaSMarius Strobl 		/* FALLTHROUGH */
127133d3fffaSMarius Strobl #endif
127266f314b5SStefan Eßer 	case PCIOCREAD:
12738983cfbfSMike Smith 	case PCIOCWRITE:
12748983cfbfSMike Smith 		io = (struct pci_io *)data;
12758983cfbfSMike Smith 		switch(io->pi_width) {
12768983cfbfSMike Smith 		case 4:
12778983cfbfSMike Smith 		case 2:
12788983cfbfSMike Smith 		case 1:
1279d16d35fdSAndriy Gapon 			/* Make sure register is not negative and aligned. */
128033d3fffaSMarius Strobl 			if (io->pi_reg < 0 ||
128133d3fffaSMarius Strobl 			    io->pi_reg & (io->pi_width - 1)) {
128266f314b5SStefan Eßer 				error = EINVAL;
128340ed3f47SRuslan Ermilov 				break;
128440ed3f47SRuslan Ermilov 			}
12858983cfbfSMike Smith 			/*
12868983cfbfSMike Smith 			 * Assume that the user-level bus number is
128737ce43b7SBruce M Simpson 			 * in fact the physical PCI bus number.
128837ce43b7SBruce M Simpson 			 * Look up the grandparent, i.e. the bridge device,
128937ce43b7SBruce M Simpson 			 * so that we can issue configuration space cycles.
12908983cfbfSMike Smith 			 */
129155aaf894SMarius Strobl 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
129255aaf894SMarius Strobl 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
129355aaf894SMarius Strobl 			    io->pi_sel.pc_func);
129437ce43b7SBruce M Simpson 			if (pcidev) {
1295b2068c0cSWarner Losh #ifdef PRE7_COMPAT
129633d3fffaSMarius Strobl 				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
129733d3fffaSMarius Strobl #else
129866f314b5SStefan Eßer 				if (cmd == PCIOCWRITE)
129933d3fffaSMarius Strobl #endif
13005060ec97SRyan Stone 					pci_write_config(pcidev,
13018983cfbfSMike Smith 							  io->pi_reg,
13028983cfbfSMike Smith 							  io->pi_data,
13038983cfbfSMike Smith 							  io->pi_width);
1304b2068c0cSWarner Losh #ifdef PRE7_COMPAT
130533d3fffaSMarius Strobl 				else if (cmd == PCIOCREAD_OLD)
130633d3fffaSMarius Strobl 					io_old->pi_data =
13075060ec97SRyan Stone 						pci_read_config(pcidev,
130833d3fffaSMarius Strobl 							  io->pi_reg,
130933d3fffaSMarius Strobl 							  io->pi_width);
131033d3fffaSMarius Strobl #endif
131166f314b5SStefan Eßer 				else
131266f314b5SStefan Eßer 					io->pi_data =
13135060ec97SRyan Stone 						pci_read_config(pcidev,
131466f314b5SStefan Eßer 							  io->pi_reg,
131566f314b5SStefan Eßer 							  io->pi_width);
13168983cfbfSMike Smith 				error = 0;
13178983cfbfSMike Smith 			} else {
13188910aa92SPaul Saab #ifdef COMPAT_FREEBSD4
131933d3fffaSMarius Strobl 				if (cmd == PCIOCREAD_OLD) {
132033d3fffaSMarius Strobl 					io_old->pi_data = -1;
13218910aa92SPaul Saab 					error = 0;
13228910aa92SPaul Saab 				} else
13238910aa92SPaul Saab #endif
13248983cfbfSMike Smith 					error = ENODEV;
13258983cfbfSMike Smith 			}
13268983cfbfSMike Smith 			break;
13278983cfbfSMike Smith 		default:
1328d08239c1SJohn-Mark Gurney 			error = EINVAL;
13298983cfbfSMike Smith 			break;
13308983cfbfSMike Smith 		}
13318983cfbfSMike Smith 		break;
13328983cfbfSMike Smith 
1333da1e0915SJohn Baldwin 	case PCIOCGETBAR:
1334da1e0915SJohn Baldwin 		bio = (struct pci_bar_io *)data;
1335da1e0915SJohn Baldwin 
1336da1e0915SJohn Baldwin 		/*
1337da1e0915SJohn Baldwin 		 * Assume that the user-level bus number is
1338da1e0915SJohn Baldwin 		 * in fact the physical PCI bus number.
1339da1e0915SJohn Baldwin 		 */
1340da1e0915SJohn Baldwin 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
1341da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
1342da1e0915SJohn Baldwin 		    bio->pbi_sel.pc_func);
1343da1e0915SJohn Baldwin 		if (pcidev == NULL) {
1344da1e0915SJohn Baldwin 			error = ENODEV;
1345da1e0915SJohn Baldwin 			break;
1346da1e0915SJohn Baldwin 		}
1347a90dd577SJohn Baldwin 		pm = pci_find_bar(pcidev, bio->pbi_reg);
1348a90dd577SJohn Baldwin 		if (pm == NULL) {
1349da1e0915SJohn Baldwin 			error = EINVAL;
1350da1e0915SJohn Baldwin 			break;
1351da1e0915SJohn Baldwin 		}
1352a90dd577SJohn Baldwin 		bio->pbi_base = pm->pm_value;
1353a90dd577SJohn Baldwin 		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
1354a90dd577SJohn Baldwin 		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
1355da1e0915SJohn Baldwin 		error = 0;
1356da1e0915SJohn Baldwin 		break;
1357762aad81SNeel Natu 	case PCIOCATTACHED:
1358762aad81SNeel Natu 		error = 0;
1359762aad81SNeel Natu 		io = (struct pci_io *)data;
1360762aad81SNeel Natu 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
1361762aad81SNeel Natu 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
1362762aad81SNeel Natu 		if (pcidev != NULL)
1363762aad81SNeel Natu 			io->pi_data = device_is_attached(pcidev);
1364762aad81SNeel Natu 		else
1365762aad81SNeel Natu 			error = ENODEV;
1366762aad81SNeel Natu 		break;
136784b755dfSJohn Baldwin 	case PCIOCLISTVPD:
136884b755dfSJohn Baldwin 		lvio = (struct pci_list_vpd_io *)data;
136984b755dfSJohn Baldwin 
137084b755dfSJohn Baldwin 		/*
137184b755dfSJohn Baldwin 		 * Assume that the user-level bus number is
137284b755dfSJohn Baldwin 		 * in fact the physical PCI bus number.
137384b755dfSJohn Baldwin 		 */
137484b755dfSJohn Baldwin 		pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain,
137584b755dfSJohn Baldwin 		    lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev,
137684b755dfSJohn Baldwin 		    lvio->plvi_sel.pc_func);
137784b755dfSJohn Baldwin 		if (pcidev == NULL) {
137884b755dfSJohn Baldwin 			error = ENODEV;
137984b755dfSJohn Baldwin 			break;
138084b755dfSJohn Baldwin 		}
138184b755dfSJohn Baldwin 		error = pci_list_vpd(pcidev, lvio);
138284b755dfSJohn Baldwin 		break;
138387842989SKonstantin Belousov 
138487842989SKonstantin Belousov 	case PCIOCBARMMAP:
138587842989SKonstantin Belousov 		pbm = (struct pci_bar_mmap *)data;
138687842989SKonstantin Belousov 		if ((flag & FWRITE) == 0 &&
138796b506a5SWarner Losh 		    (pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) {
138896b506a5SWarner Losh 			error = EPERM;
138996b506a5SWarner Losh 			break;
139096b506a5SWarner Losh 		}
139187842989SKonstantin Belousov 		pcidev = pci_find_dbsf(pbm->pbm_sel.pc_domain,
139287842989SKonstantin Belousov 		    pbm->pbm_sel.pc_bus, pbm->pbm_sel.pc_dev,
139387842989SKonstantin Belousov 		    pbm->pbm_sel.pc_func);
139487842989SKonstantin Belousov 		error = pcidev == NULL ? ENODEV : pci_bar_mmap(pcidev, pbm);
139587842989SKonstantin Belousov 		break;
139687842989SKonstantin Belousov 
1397*7e14be0bSMark Johnston 	case PCIOCBARIO:
1398*7e14be0bSMark Johnston 		pbi = (struct pci_bar_ioreq *)data;
1399*7e14be0bSMark Johnston 
1400*7e14be0bSMark Johnston 		pcidev = pci_find_dbsf(pbi->pbi_sel.pc_domain,
1401*7e14be0bSMark Johnston 		    pbi->pbi_sel.pc_bus, pbi->pbi_sel.pc_dev,
1402*7e14be0bSMark Johnston 		    pbi->pbi_sel.pc_func);
1403*7e14be0bSMark Johnston 		if (pcidev == NULL) {
1404*7e14be0bSMark Johnston 			error = ENODEV;
1405*7e14be0bSMark Johnston 			break;
1406*7e14be0bSMark Johnston 		}
1407*7e14be0bSMark Johnston 		error = pci_bar_io(pcidev, pbi);
1408*7e14be0bSMark Johnston 		break;
1409*7e14be0bSMark Johnston 
14108983cfbfSMike Smith 	default:
14118983cfbfSMike Smith 		error = ENOTTY;
14128983cfbfSMike Smith 		break;
14138983cfbfSMike Smith 	}
14148983cfbfSMike Smith 
141596b506a5SWarner Losh 	mtx_unlock(&Giant);
141696b506a5SWarner Losh 
14178983cfbfSMike Smith 	return (error);
14188983cfbfSMike Smith }
1419