pci_user.c revision 244695
159290Sjlemon/*-
272969Sjlemon * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3133741Sjmg * All rights reserved.
4197240Ssson *
559290Sjlemon * Redistribution and use in source and binary forms, with or without
659290Sjlemon * modification, are permitted provided that the following conditions
759290Sjlemon * are met:
859290Sjlemon * 1. Redistributions of source code must retain the above copyright
959290Sjlemon *    notice unmodified, this list of conditions, and the following
1059290Sjlemon *    disclaimer.
1159290Sjlemon * 2. Redistributions in binary form must reproduce the above copyright
1259290Sjlemon *    notice, this list of conditions and the following disclaimer in the
1359290Sjlemon *    documentation and/or other materials provided with the distribution.
1459290Sjlemon *
1559290Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1659290Sjlemon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1759290Sjlemon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1859290Sjlemon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1959290Sjlemon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2059290Sjlemon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2159290Sjlemon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2259290Sjlemon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2359290Sjlemon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2459290Sjlemon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2559290Sjlemon */
2659290Sjlemon
2759290Sjlemon#include <sys/cdefs.h>
2859290Sjlemon__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 244695 2012-12-26 13:07:17Z davidxu $");
29116182Sobrien
30116182Sobrien#include "opt_bus.h"	/* XXX trim includes */
31116182Sobrien#include "opt_compat.h"
32162592Sjmg
33162592Sjmg#include <sys/param.h>
3459290Sjlemon#include <sys/systm.h>
3559290Sjlemon#include <sys/malloc.h>
3659290Sjlemon#include <sys/module.h>
3776166Smarkm#include <sys/linker.h>
3876166Smarkm#include <sys/fcntl.h>
3959290Sjlemon#include <sys/conf.h>
40132138Salfred#include <sys/kernel.h>
4159290Sjlemon#include <sys/proc.h>
4259290Sjlemon#include <sys/queue.h>
43108524Salfred#include <sys/types.h>
44132138Salfred
4559290Sjlemon#include <vm/vm.h>
46133741Sjmg#include <vm/pmap.h>
4770834Swollman#include <vm/vm_extern.h>
4859290Sjlemon
4959290Sjlemon#include <sys/bus.h>
5059290Sjlemon#include <machine/bus.h>
5159290Sjlemon#include <sys/rman.h>
5259290Sjlemon#include <machine/resource.h>
53132138Salfred
54132138Salfred#include <sys/pciio.h>
5559290Sjlemon#include <dev/pci/pcireg.h>
5659290Sjlemon#include <dev/pci/pcivar.h>
5759290Sjlemon
5884138Sjlemon#include "pcib_if.h"
5959290Sjlemon#include "pci_if.h"
60142934Sps
61133741Sjmg/*
6259290Sjlemon * This is the user interface to PCI configuration space.
63162592Sjmg */
64162592Sjmg
65162592Sjmgstatic d_open_t 	pci_open;
6659290Sjlemonstatic d_close_t	pci_close;
6792751Sjeffstatic int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
6859290Sjlemon			       struct pci_conf *match_buf);
69141616Sphkstatic d_ioctl_t	pci_ioctl;
70141616Sphk
71133741Sjmgstruct cdevsw pcicdev = {
72133741Sjmg	.d_version =	D_VERSION,
73133741Sjmg	.d_flags =	D_NEEDGIANT,
74133741Sjmg	.d_open =	pci_open,
75133741Sjmg	.d_close =	pci_close,
76133741Sjmg	.d_ioctl =	pci_ioctl,
77133741Sjmg	.d_name =	"pci",
78133741Sjmg};
79133741Sjmg
80133741Sjmgstatic int
81133741Sjmgpci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
82133741Sjmg{
83133741Sjmg	int error;
84133741Sjmg
85133741Sjmg	if (oflags & FWRITE) {
86133741Sjmg		error = securelevel_gt(td->td_ucred, 0);
8784138Sjlemon		if (error)
88133741Sjmg			return (error);
89133741Sjmg	}
90146950Sps
91146950Sps	return (0);
92162594Sjmg}
93162594Sjmg
94170029Srwatsonstatic int
95133741Sjmgpci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
96133741Sjmg{
97133741Sjmg	return 0;
98133741Sjmg}
99133741Sjmg
100146950Sps/*
101146950Sps * Match a single pci_conf structure against an array of pci_match_conf
102146950Sps * structures.  The first argument, 'matches', is an array of num_matches
10359290Sjlemon * pci_match_conf structures.  match_buf is a pointer to the pci_conf
104133741Sjmg * structure that will be compared to every entry in the matches array.
105133741Sjmg * This function returns 1 on failure, 0 on success.
10659290Sjlemon */
107108255Sphkstatic int
108108255Sphkpci_conf_match(struct pci_match_conf *matches, int num_matches,
109175140Sjhb	       struct pci_conf *match_buf)
110108255Sphk{
111108255Sphk	int i;
112108255Sphk
113108255Sphk	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
114108255Sphk		return(1);
115108238Sphk
11672521Sjlemon	for (i = 0; i < num_matches; i++) {
117116546Sphk		/*
118116546Sphk		 * I'm not sure why someone would do this...but...
119175140Sjhb		 */
120116546Sphk		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
121116546Sphk			continue;
122116546Sphk
123116546Sphk		/*
124116546Sphk		 * Look at each of the match flags.  If it's set, do the
12572521Sjlemon		 * comparison.  If the comparison fails, we don't have a
12672521Sjlemon		 * match, go on to the next item if there is one.
127133741Sjmg		 */
12883366Sjulian		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
12959290Sjlemon		 && (match_buf->pc_sel.pc_domain !=
13059290Sjlemon		 matches[i].pc_sel.pc_domain))
13159290Sjlemon			continue;
132133741Sjmg
13359290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
13459290Sjlemon		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
13572521Sjlemon			continue;
13672521Sjlemon
13772521Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
13872521Sjlemon		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
13972521Sjlemon			continue;
14072521Sjlemon
14179989Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
14279989Sjlemon		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
14379989Sjlemon			continue;
14479989Sjlemon
145197241Ssson		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
146197241Ssson		 && (match_buf->pc_vendor != matches[i].pc_vendor))
147197241Ssson			continue;
148197241Ssson
14972521Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
150197134Srwatson		 && (match_buf->pc_device != matches[i].pc_device))
151197134Srwatson			continue;
152197134Srwatson
153197134Srwatson		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
154197134Srwatson		 && (match_buf->pc_class != matches[i].pc_class))
155197134Srwatson			continue;
156197134Srwatson
157197134Srwatson		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
158197134Srwatson		 && (match_buf->pd_unit != matches[i].pd_unit))
159133741Sjmg			continue;
160197134Srwatson
161197134Srwatson		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
162197134Srwatson		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
163197134Srwatson			     sizeof(match_buf->pd_name)) != 0))
164197134Srwatson			continue;
165197134Srwatson
166197134Srwatson		return(0);
167197134Srwatson	}
168197134Srwatson
169197134Srwatson	return(1);
170197134Srwatson}
171197134Srwatson
172197241Ssson#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
173197241Ssson    defined(COMPAT_FREEBSD6)
174197241Ssson#define PRE7_COMPAT
175197241Ssson
176197241Sssontypedef enum {
177197241Ssson	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
17872521Sjlemon	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
17992751Sjeff	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
18084138Sjlemon	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
18184138Sjlemon	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
18284138Sjlemon	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
18384138Sjlemon	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
18459290Sjlemon	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
185133741Sjmg	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
186133741Sjmg} pci_getconf_flags_old;
187133741Sjmg
188133741Sjmgstruct pcisel_old {
189133741Sjmg	u_int8_t	pc_bus;		/* bus number */
190133741Sjmg	u_int8_t	pc_dev;		/* device on this bus */
191133741Sjmg	u_int8_t	pc_func;	/* function on this device */
192133741Sjmg};
193133741Sjmg
194133741Sjmgstruct pci_conf_old {
195133741Sjmg	struct pcisel_old pc_sel;	/* bus+slot+function */
19659290Sjlemon	u_int8_t	pc_hdr;		/* PCI header type */
197133741Sjmg	u_int16_t	pc_subvendor;	/* card vendor ID */
198133741Sjmg	u_int16_t	pc_subdevice;	/* card device ID, assigned by
199133741Sjmg					   card vendor */
200133741Sjmg	u_int16_t	pc_vendor;	/* chip vendor ID */
201133741Sjmg	u_int16_t	pc_device;	/* chip device ID, assigned by
202133741Sjmg					   chip vendor */
203133741Sjmg	u_int8_t	pc_class;	/* chip PCI class */
204133741Sjmg	u_int8_t	pc_subclass;	/* chip PCI subclass */
205133741Sjmg	u_int8_t	pc_progif;	/* chip PCI programming interface */
206133741Sjmg	u_int8_t	pc_revid;	/* chip revision ID */
207133741Sjmg	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
208133741Sjmg	u_long		pd_unit;	/* device unit number */
209133741Sjmg};
210133741Sjmg
211133741Sjmgstruct pci_match_conf_old {
212133741Sjmg	struct pcisel_old	pc_sel;		/* bus+slot+function */
213133741Sjmg	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
214133741Sjmg	u_long			pd_unit;	/* Unit number */
215133741Sjmg	u_int16_t		pc_vendor;	/* PCI Vendor ID */
216133741Sjmg	u_int16_t		pc_device;	/* PCI Device ID */
217133741Sjmg	u_int8_t		pc_class;	/* PCI class */
218133741Sjmg	pci_getconf_flags_old	flags;		/* Matching expression */
219133741Sjmg};
220133741Sjmg
221147730Sssouhlalstruct pci_io_old {
222133741Sjmg	struct pcisel_old pi_sel;	/* device to operate on */
223133741Sjmg	int		pi_reg;		/* configuration register to examine */
224147730Sssouhlal	int		pi_width;	/* width (in bytes) of read or write */
225147730Sssouhlal	u_int32_t	pi_data;	/* data to write or result of read */
226133741Sjmg};
227147730Sssouhlal
228147730Sssouhlal#ifdef COMPAT_FREEBSD32
229147730Sssouhlalstruct pci_conf_old32 {
230147730Sssouhlal	struct pcisel_old pc_sel;	/* bus+slot+function */
231147730Sssouhlal	uint8_t		pc_hdr;		/* PCI header type */
232147730Sssouhlal	uint16_t	pc_subvendor;	/* card vendor ID */
233147730Sssouhlal	uint16_t	pc_subdevice;	/* card device ID, assigned by
234147730Sssouhlal					   card vendor */
235193951Skib	uint16_t	pc_vendor;	/* chip vendor ID */
236147730Sssouhlal	uint16_t	pc_device;	/* chip device ID, assigned by
237193951Skib					   chip vendor */
238193951Skib	uint8_t		pc_class;	/* chip PCI class */
239147730Sssouhlal	uint8_t		pc_subclass;	/* chip PCI subclass */
240147730Sssouhlal	uint8_t		pc_progif;	/* chip PCI programming interface */
241147730Sssouhlal	uint8_t		pc_revid;	/* chip revision ID */
242147730Sssouhlal	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
243147730Sssouhlal	uint32_t	pd_unit;	/* device unit number (u_long) */
24459290Sjlemon};
24559290Sjlemon
24659290Sjlemonstruct pci_match_conf_old32 {
24759290Sjlemon	struct pcisel_old pc_sel;	/* bus+slot+function */
24888633Salfred	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
24988633Salfred	uint32_t	pd_unit;	/* Unit number (u_long) */
25088633Salfred	uint16_t	pc_vendor;	/* PCI Vendor ID */
25188633Salfred	uint16_t	pc_device;	/* PCI Device ID */
25288633Salfred	uint8_t		pc_class;	/* PCI class */
25388633Salfred	pci_getconf_flags_old flags;	/* Matching expression */
25488633Salfred};
255197134Srwatson
256197134Srwatsonstruct pci_conf_io32 {
257197134Srwatson	uint32_t	pat_buf_len;	/* pattern buffer length */
258197134Srwatson	uint32_t	num_patterns;	/* number of patterns */
25988633Salfred	uint32_t	patterns;	/* pattern buffer
260133741Sjmg					   (struct pci_match_conf_old32 *) */
26159290Sjlemon	uint32_t	match_buf_len;	/* match buffer length */
262131562Salfred	uint32_t	num_matches;	/* number of matches returned */
26359290Sjlemon	uint32_t	matches;	/* match buffer
26459290Sjlemon					   (struct pci_conf_old32 *) */
26572521Sjlemon	uint32_t	offset;		/* offset into device list */
26659290Sjlemon	uint32_t	generation;	/* device list generation */
267133741Sjmg	pci_getconf_status status;	/* request status */
268133741Sjmg};
269133741Sjmg
270133741Sjmg#define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
271133741Sjmg#endif	/* COMPAT_FREEBSD32 */
272133741Sjmg
273133741Sjmg#define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
274133741Sjmg#define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
275133741Sjmg#define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
276133741Sjmg
277133741Sjmgstatic int	pci_conf_match_old(struct pci_match_conf_old *matches,
278133741Sjmg		    int num_matches, struct pci_conf *match_buf);
279133741Sjmg
280133741Sjmgstatic int
281133741Sjmgpci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
282133741Sjmg    struct pci_conf *match_buf)
283151260Sambrisko{
284197241Ssson	int i;
28559290Sjlemon
28659290Sjlemon	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
287133741Sjmg		return(1);
288133741Sjmg
289133741Sjmg	for (i = 0; i < num_matches; i++) {
290133741Sjmg		if (match_buf->pc_sel.pc_domain != 0)
29159290Sjlemon			continue;
29272521Sjlemon
29359290Sjlemon		/*
294133635Sjmg		 * I'm not sure why someone would do this...but...
29572521Sjlemon		 */
29659290Sjlemon		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
29759290Sjlemon			continue;
29872521Sjlemon
29959290Sjlemon		/*
30072521Sjlemon		 * Look at each of the match flags.  If it's set, do the
30159290Sjlemon		 * comparison.  If the comparison fails, we don't have a
302109153Sdillon		 * match, go on to the next item if there is one.
30359290Sjlemon		 */
30472521Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
305133741Sjmg		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
30659290Sjlemon			continue;
307133741Sjmg
30872521Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
309133741Sjmg		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
310133741Sjmg			continue;
31159290Sjlemon
31259290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
31359290Sjlemon		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
31459290Sjlemon			continue;
31559290Sjlemon
31659290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
317109153Sdillon		 && (match_buf->pc_vendor != matches[i].pc_vendor))
31859290Sjlemon			continue;
319133741Sjmg
32059290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
32159290Sjlemon		 && (match_buf->pc_device != matches[i].pc_device))
32259290Sjlemon			continue;
32359290Sjlemon
32459290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
32559290Sjlemon		 && (match_buf->pc_class != matches[i].pc_class))
326109153Sdillon			continue;
32759290Sjlemon
32859290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
32959290Sjlemon		 && (match_buf->pd_unit != matches[i].pd_unit))
33059290Sjlemon			continue;
33159290Sjlemon
332133741Sjmg		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
33359290Sjlemon		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
33459290Sjlemon			     sizeof(match_buf->pd_name)) != 0))
33559290Sjlemon			continue;
33659290Sjlemon
337113377Skbyanc		return(0);
33875451Srwatson	}
33959290Sjlemon
340113377Skbyanc	return(1);
34159290Sjlemon}
342113377Skbyanc
343113377Skbyanc#ifdef COMPAT_FREEBSD32
344113377Skbyancstatic int
345133741Sjmgpci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
346133741Sjmg    struct pci_conf *match_buf)
347113377Skbyanc{
348133741Sjmg	int i;
349122019Scognet
350122019Scognet	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
351133741Sjmg		return(1);
35275451Srwatson
35359290Sjlemon	for (i = 0; i < num_matches; i++) {
35459290Sjlemon		if (match_buf->pc_sel.pc_domain != 0)
35559290Sjlemon			continue;
35659290Sjlemon
35759290Sjlemon		/*
35859290Sjlemon		 * I'm not sure why someone would do this...but...
35959290Sjlemon		 */
36059290Sjlemon		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
36159290Sjlemon			continue;
36259290Sjlemon
36359290Sjlemon		/*
36459290Sjlemon		 * Look at each of the match flags.  If it's set, do the
36559290Sjlemon		 * comparison.  If the comparison fails, we don't have a
366122686Scognet		 * match, go on to the next item if there is one.
367133741Sjmg		 */
368113377Skbyanc		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
369113377Skbyanc		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
370113377Skbyanc			continue;
371113377Skbyanc
372113377Skbyanc		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
373113377Skbyanc		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
374113377Skbyanc			continue;
375133741Sjmg
376113377Skbyanc		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
37771500Sjhb		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
37859290Sjlemon			continue;
37959290Sjlemon
38059290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
38159290Sjlemon		    (match_buf->pc_vendor != matches[i].pc_vendor))
38259290Sjlemon			continue;
38359290Sjlemon
38459290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
38559290Sjlemon		    (match_buf->pc_device != matches[i].pc_device))
38659290Sjlemon			continue;
38759290Sjlemon
38859290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
38959290Sjlemon		    (match_buf->pc_class != matches[i].pc_class))
390133741Sjmg			continue;
39159290Sjlemon
39259290Sjlemon		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
39359290Sjlemon		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
394133741Sjmg			continue;
39559290Sjlemon
396133741Sjmg		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
397133741Sjmg		    (strncmp(matches[i].pd_name, match_buf->pd_name,
398133741Sjmg		    sizeof(match_buf->pd_name)) != 0))
39959290Sjlemon			continue;
40059290Sjlemon
401133741Sjmg		return (0);
40259290Sjlemon	}
40359290Sjlemon
40459290Sjlemon	return (1);
405133741Sjmg}
40659290Sjlemon#endif	/* COMPAT_FREEBSD32 */
40759290Sjlemon#endif	/* PRE7_COMPAT */
40859290Sjlemon
40959290Sjlemonstatic int
41059290Sjlemonpci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
41159290Sjlemon{
41259290Sjlemon	device_t pcidev, brdev;
41359290Sjlemon	void *confdata;
41459290Sjlemon	const char *name;
41559290Sjlemon	struct devlist *devlist_head;
41659290Sjlemon	struct pci_conf_io *cio = NULL;
41759290Sjlemon	struct pci_devinfo *dinfo;
41859290Sjlemon	struct pci_io *io;
41959290Sjlemon	struct pci_bar_io *bio;
42059290Sjlemon	struct pci_match_conf *pattern_buf;
42159290Sjlemon	struct pci_map *pm;
42259290Sjlemon	size_t confsz, iolen, pbufsz;
423133741Sjmg	int error, ionum, i, num_patterns;
424133741Sjmg#ifdef PRE7_COMPAT
425133590Srwatson#ifdef COMPAT_FREEBSD32
426164451Sjhb	struct pci_conf_io32 *cio32 = NULL;
427133741Sjmg	struct pci_conf_old32 conf_old32;
42859290Sjlemon	struct pci_match_conf_old32 *pattern_buf_old32 = NULL;
42959290Sjlemon#endif
43059290Sjlemon	struct pci_conf_old conf_old;
431180340Skib	struct pci_io iodata;
432180340Skib	struct pci_io_old *io_old;
43359290Sjlemon	struct pci_match_conf_old *pattern_buf_old = NULL;
434180340Skib
435180340Skib	io_old = NULL;
436180340Skib
437180340Skib	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
438180340Skib	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
439180340Skib		return EPERM;
440180340Skib#else
441180340Skib	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
442180340Skib		return EPERM;
443180340Skib#endif
444180340Skib
445180340Skib	switch(cmd) {
446180340Skib#ifdef PRE7_COMPAT
447180340Skib#ifdef COMPAT_FREEBSD32
448180340Skib       case PCIOCGETCONF_OLD32:
449180340Skib               cio32 = (struct pci_conf_io32 *)data;
450180340Skib               cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
451180340Skib               cio->pat_buf_len = cio32->pat_buf_len;
452180340Skib               cio->num_patterns = cio32->num_patterns;
453180340Skib               cio->patterns = (void *)(uintptr_t)cio32->patterns;
454180340Skib               cio->match_buf_len = cio32->match_buf_len;
455180340Skib               cio->num_matches = cio32->num_matches;
456180340Skib               cio->matches = (void *)(uintptr_t)cio32->matches;
457180340Skib               cio->offset = cio32->offset;
458180340Skib               cio->generation = cio32->generation;
459180340Skib               cio->status = cio32->status;
460180340Skib               cio32->num_matches = 0;
461180340Skib               break;
462180340Skib#endif
463180340Skib	case PCIOCGETCONF_OLD:
46459290Sjlemon#endif
465180340Skib	case PCIOCGETCONF:
46659290Sjlemon		cio = (struct pci_conf_io *)data;
467180340Skib	}
468180340Skib
469180340Skib	switch(cmd) {
470180340Skib#ifdef PRE7_COMPAT
471180340Skib#ifdef COMPAT_FREEBSD32
472180340Skib	case PCIOCGETCONF_OLD32:
473180340Skib#endif
474180340Skib	case PCIOCGETCONF_OLD:
475180340Skib#endif
476180340Skib	case PCIOCGETCONF:
477180340Skib
478180340Skib		pattern_buf = NULL;
479180340Skib		num_patterns = 0;
480180340Skib		dinfo = NULL;
481180340Skib
482180340Skib		cio->num_matches = 0;
483180340Skib
484180340Skib		/*
485180340Skib		 * If the user specified an offset into the device list,
486180340Skib		 * but the list has changed since they last called this
487180340Skib		 * ioctl, tell them that the list has changed.  They will
488180340Skib		 * have to get the list from the beginning.
489180340Skib		 */
490180340Skib		if ((cio->offset != 0)
49159290Sjlemon		 && (cio->generation != pci_generation)){
49259290Sjlemon			cio->status = PCI_GETCONF_LIST_CHANGED;
49359290Sjlemon			error = 0;
494180340Skib			goto getconfexit;
495180340Skib		}
496180340Skib
497180340Skib		/*
498180340Skib		 * Check to see whether the user has asked for an offset
49959290Sjlemon		 * past the end of our list.
50059290Sjlemon		 */
501180340Skib		if (cio->offset >= pci_numdevs) {
502180340Skib			cio->status = PCI_GETCONF_LAST_DEVICE;
503180340Skib			error = 0;
504180340Skib			goto getconfexit;
50559290Sjlemon		}
506180340Skib
50759290Sjlemon		/* get the head of the device queue */
50859290Sjlemon		devlist_head = &pci_devq;
509133741Sjmg
510133741Sjmg		/*
511133741Sjmg		 * Determine how much room we have for pci_conf structures.
512133741Sjmg		 * Round the user's buffer size down to the nearest
513133741Sjmg		 * multiple of sizeof(struct pci_conf) in case the user
514133741Sjmg		 * didn't specify a multiple of that size.
515133741Sjmg		 */
516133741Sjmg#ifdef PRE7_COMPAT
517133741Sjmg#ifdef COMPAT_FREEBSD32
518133741Sjmg		if (cmd == PCIOCGETCONF_OLD32)
519133741Sjmg			confsz = sizeof(struct pci_conf_old32);
520133741Sjmg		else
521133741Sjmg#endif
522133741Sjmg		if (cmd == PCIOCGETCONF_OLD)
52379989Sjlemon			confsz = sizeof(struct pci_conf_old);
52479989Sjlemon		else
52579989Sjlemon#endif
52679989Sjlemon			confsz = sizeof(struct pci_conf);
52784138Sjlemon		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
52879989Sjlemon		    pci_numdevs * confsz);
52979989Sjlemon
530133741Sjmg		/*
53179989Sjlemon		 * Since we know that iolen is a multiple of the size of
532133741Sjmg		 * the pciconf union, it's okay to do this.
53384138Sjlemon		 */
534177860Sjeff		ionum = iolen / confsz;
535133741Sjmg
53679989Sjlemon		/*
53779989Sjlemon		 * If this test is true, the user wants the pci_conf
53879989Sjlemon		 * structures returned to match the supplied entries.
53979989Sjlemon		 */
54079989Sjlemon		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
541133590Srwatson		 && (cio->pat_buf_len > 0)) {
542133741Sjmg			/*
54379989Sjlemon			 * pat_buf_len needs to be:
54479989Sjlemon			 * num_patterns * sizeof(struct pci_match_conf)
54579989Sjlemon			 * While it is certainly possible the user just
54684138Sjlemon			 * allocated a large buffer, but set the number of
54779989Sjlemon			 * matches correctly, it is far more likely that
548133741Sjmg			 * their kernel doesn't match the userland utility
549133741Sjmg			 * they're using.  It's also possible that the user
550133741Sjmg			 * forgot to initialize some variables.  Yes, this
551133741Sjmg			 * may be overly picky, but I hazard to guess that
55284138Sjlemon			 * it's far more likely to just catch folks that
553133741Sjmg			 * updated their kernel but not their userland.
55484138Sjlemon			 */
55579989Sjlemon#ifdef PRE7_COMPAT
556136500Sjmg#ifdef COMPAT_FREEBSD32
557184214Sdes			if (cmd == PCIOCGETCONF_OLD32)
558142217Srwatson				pbufsz = sizeof(struct pci_match_conf_old32);
559127982Scperciva			else
560177860Sjeff#endif
561177860Sjeff			if (cmd == PCIOCGETCONF_OLD)
56279989Sjlemon				pbufsz = sizeof(struct pci_match_conf_old);
56379989Sjlemon			else
56479989Sjlemon#endif
56579989Sjlemon				pbufsz = sizeof(struct pci_match_conf);
566133741Sjmg			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
56779989Sjlemon				/* The user made a mistake, return an error. */
56879989Sjlemon				cio->status = PCI_GETCONF_ERROR;
56979989Sjlemon				error = EINVAL;
57084138Sjlemon				goto getconfexit;
57179989Sjlemon			}
57284138Sjlemon
573127982Scperciva			/*
574184205Sdes			 * Allocate a buffer to hold the patterns.
575133741Sjmg			 */
576136500Sjmg#ifdef PRE7_COMPAT
57779989Sjlemon#ifdef COMPAT_FREEBSD32
57879989Sjlemon			if (cmd == PCIOCGETCONF_OLD32) {
579133741Sjmg				pattern_buf_old32 = malloc(cio->pat_buf_len,
58079989Sjlemon				    M_TEMP, M_WAITOK);
58179989Sjlemon				error = copyin(cio->patterns,
58279989Sjlemon				    pattern_buf_old32, cio->pat_buf_len);
58379989Sjlemon			} else
58479989Sjlemon#endif /* COMPAT_FREEBSD32 */
58579989Sjlemon			if (cmd == PCIOCGETCONF_OLD) {
58679989Sjlemon				pattern_buf_old = malloc(cio->pat_buf_len,
587197241Ssson				    M_TEMP, M_WAITOK);
588197241Ssson				error = copyin(cio->patterns,
589197241Ssson				    pattern_buf_old, cio->pat_buf_len);
590197241Ssson			} else
591197241Ssson#endif /* PRE7_COMPAT */
592197241Ssson			{
593197241Ssson				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
594197241Ssson				    M_WAITOK);
595197241Ssson				error = copyin(cio->patterns, pattern_buf,
596197241Ssson				    cio->pat_buf_len);
597197241Ssson			}
598197241Ssson			if (error != 0) {
599197241Ssson				error = EINVAL;
600197241Ssson				goto getconfexit;
601197241Ssson			}
602197241Ssson			num_patterns = cio->num_patterns;
603197241Ssson		} else if ((cio->num_patterns > 0)
604197241Ssson			|| (cio->pat_buf_len > 0)) {
605197241Ssson			/*
606197241Ssson			 * The user made a mistake, spit out an error.
607197241Ssson			 */
608197241Ssson			cio->status = PCI_GETCONF_ERROR;
609197241Ssson			error = EINVAL;
610197241Ssson                       goto getconfexit;
611197241Ssson		}
612197241Ssson
613197241Ssson		/*
614197241Ssson		 * Go through the list of devices and copy out the devices
615197241Ssson		 * that match the user's criteria.
616197241Ssson		 */
617197241Ssson		for (cio->num_matches = 0, error = 0, i = 0,
618197241Ssson		     dinfo = STAILQ_FIRST(devlist_head);
619197241Ssson		     (dinfo != NULL) && (cio->num_matches < ionum)
620197241Ssson		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
621197241Ssson		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
622197241Ssson
623197241Ssson			if (i < cio->offset)
624197241Ssson				continue;
625197241Ssson
626197241Ssson			/* Populate pd_name and pd_unit */
627197241Ssson			name = NULL;
628197241Ssson			if (dinfo->cfg.dev)
629197241Ssson				name = device_get_name(dinfo->cfg.dev);
630197241Ssson			if (name) {
631197241Ssson				strncpy(dinfo->conf.pd_name, name,
632197241Ssson					sizeof(dinfo->conf.pd_name));
633197241Ssson				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
634197241Ssson				dinfo->conf.pd_unit =
635197241Ssson					device_get_unit(dinfo->cfg.dev);
636197241Ssson			} else {
637197241Ssson				dinfo->conf.pd_name[0] = '\0';
638197241Ssson				dinfo->conf.pd_unit = 0;
639197241Ssson			}
640197241Ssson
641197241Ssson#ifdef PRE7_COMPAT
642197241Ssson			if (
643197241Ssson#ifdef COMPAT_FREEBSD32
644197241Ssson			    (cmd == PCIOCGETCONF_OLD32 &&
645197241Ssson			    (pattern_buf_old32 == NULL ||
646197241Ssson			    pci_conf_match_old32(pattern_buf_old32,
647197241Ssson			    num_patterns, &dinfo->conf) == 0)) ||
648197241Ssson#endif
649197241Ssson			    (cmd == PCIOCGETCONF_OLD &&
650197241Ssson			    (pattern_buf_old == NULL ||
651197241Ssson			    pci_conf_match_old(pattern_buf_old, num_patterns,
652197241Ssson			    &dinfo->conf) == 0)) ||
653197241Ssson			    (cmd == PCIOCGETCONF &&
654197241Ssson			    (pattern_buf == NULL ||
655197241Ssson			    pci_conf_match(pattern_buf, num_patterns,
656197241Ssson			    &dinfo->conf) == 0))) {
657197241Ssson#else
658197241Ssson			if (pattern_buf == NULL ||
659197241Ssson			    pci_conf_match(pattern_buf, num_patterns,
660197241Ssson			    &dinfo->conf) == 0) {
661197241Ssson#endif
662197241Ssson				/*
663197241Ssson				 * If we've filled up the user's buffer,
664197241Ssson				 * break out at this point.  Since we've
665197241Ssson				 * got a match here, we'll pick right back
666197241Ssson				 * up at the matching entry.  We can also
667197241Ssson				 * tell the user that there are more matches
668197241Ssson				 * left.
669197241Ssson				 */
670197241Ssson				if (cio->num_matches >= ionum)
671197241Ssson					break;
672197241Ssson
673197241Ssson#ifdef PRE7_COMPAT
674197241Ssson#ifdef COMPAT_FREEBSD32
67561468Sjlemon				if (cmd == PCIOCGETCONF_OLD32) {
67683366Sjulian					conf_old32.pc_sel.pc_bus =
67759290Sjlemon					    dinfo->conf.pc_sel.pc_bus;
67882710Sdillon					conf_old32.pc_sel.pc_dev =
67959290Sjlemon					    dinfo->conf.pc_sel.pc_dev;
68061468Sjlemon					conf_old32.pc_sel.pc_func =
68161468Sjlemon					    dinfo->conf.pc_sel.pc_func;
68259290Sjlemon					conf_old32.pc_hdr = dinfo->conf.pc_hdr;
68383366Sjulian					conf_old32.pc_subvendor =
68483366Sjulian					    dinfo->conf.pc_subvendor;
68561468Sjlemon					conf_old32.pc_subdevice =
68682710Sdillon					    dinfo->conf.pc_subdevice;
687133741Sjmg					conf_old32.pc_vendor =
688121256Sdwmalone					    dinfo->conf.pc_vendor;
689133741Sjmg					conf_old32.pc_device =
690133741Sjmg					    dinfo->conf.pc_device;
69189306Salfred					conf_old32.pc_class =
692133741Sjmg					    dinfo->conf.pc_class;
693193951Skib					conf_old32.pc_subclass =
694133741Sjmg					    dinfo->conf.pc_subclass;
695133741Sjmg					conf_old32.pc_progif =
696168355Srwatson					    dinfo->conf.pc_progif;
697133741Sjmg					conf_old32.pc_revid =
698168355Srwatson					    dinfo->conf.pc_revid;
699133741Sjmg					strncpy(conf_old32.pd_name,
700174988Sjeff					    dinfo->conf.pd_name,
701121256Sdwmalone					    sizeof(conf_old32.pd_name));
702133741Sjmg					conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
70383366Sjulian					conf_old32.pd_unit =
70482710Sdillon					    (uint32_t)dinfo->conf.pd_unit;
70561468Sjlemon					confdata = &conf_old32;
70659290Sjlemon				} else
70759290Sjlemon#endif /* COMPAT_FREEBSD32 */
70859290Sjlemon				if (cmd == PCIOCGETCONF_OLD) {
70959290Sjlemon					conf_old.pc_sel.pc_bus =
71059290Sjlemon					    dinfo->conf.pc_sel.pc_bus;
71163977Speter					conf_old.pc_sel.pc_dev =
71259290Sjlemon					    dinfo->conf.pc_sel.pc_dev;
71363452Sjlemon					conf_old.pc_sel.pc_func =
71459290Sjlemon					    dinfo->conf.pc_sel.pc_func;
71563977Speter					conf_old.pc_hdr = dinfo->conf.pc_hdr;
71659290Sjlemon					conf_old.pc_subvendor =
71759290Sjlemon					    dinfo->conf.pc_subvendor;
71859290Sjlemon					conf_old.pc_subdevice =
71983366Sjulian					    dinfo->conf.pc_subdevice;
72059290Sjlemon					conf_old.pc_vendor =
721142934Sps					    dinfo->conf.pc_vendor;
722146950Sps					conf_old.pc_device =
723146950Sps					    dinfo->conf.pc_device;
724146950Sps					conf_old.pc_class =
725142934Sps					    dinfo->conf.pc_class;
726162592Sjmg					conf_old.pc_subclass =
727162592Sjmg					    dinfo->conf.pc_subclass;
728162592Sjmg					conf_old.pc_progif =
729162592Sjmg					    dinfo->conf.pc_progif;
730162592Sjmg					conf_old.pc_revid =
731162592Sjmg					    dinfo->conf.pc_revid;
732142934Sps					strncpy(conf_old.pd_name,
733142934Sps					    dinfo->conf.pd_name,
734142934Sps					    sizeof(conf_old.pd_name));
735142934Sps					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
736142934Sps					conf_old.pd_unit =
737142934Sps					    dinfo->conf.pd_unit;
738142934Sps					confdata = &conf_old;
739142934Sps				} else
740142934Sps#endif /* PRE7_COMPAT */
741162592Sjmg					confdata = &dinfo->conf;
742162592Sjmg				/* Only if we can copy it out do we count it. */
743162592Sjmg				if (!(error = copyout(confdata,
744162592Sjmg				    (caddr_t)cio->matches +
745162592Sjmg				    confsz * cio->num_matches, confsz)))
746162592Sjmg					cio->num_matches++;
747162592Sjmg			}
748162592Sjmg		}
749162592Sjmg
750162592Sjmg		/*
751162592Sjmg		 * Set the pointer into the list, so if the user is getting
752162592Sjmg		 * n records at a time, where n < pci_numdevs,
753162592Sjmg		 */
754162592Sjmg		cio->offset = i;
755162592Sjmg
756162592Sjmg		/*
757162592Sjmg		 * Set the generation, the user will need this if they make
758162592Sjmg		 * another ioctl call with offset != 0.
759162592Sjmg		 */
760162592Sjmg		cio->generation = pci_generation;
761162592Sjmg
762162592Sjmg		/*
763162592Sjmg		 * If this is the last device, inform the user so he won't
764162592Sjmg		 * bother asking for more devices.  If dinfo isn't NULL, we
765162592Sjmg		 * know that there are more matches in the list because of
766162592Sjmg		 * the way the traversal is done.
767162592Sjmg		 */
768142934Sps		if (dinfo == NULL)
769142934Sps			cio->status = PCI_GETCONF_LAST_DEVICE;
770142934Sps		else
771146950Sps			cio->status = PCI_GETCONF_MORE_DEVS;
772142934Sps
773142934Spsgetconfexit:
774146950Sps#ifdef PRE7_COMPAT
775142934Sps#ifdef COMPAT_FREEBSD32
776146950Sps		if (cmd == PCIOCGETCONF_OLD32) {
777142934Sps			cio32->status = cio->status;
778142934Sps			cio32->generation = cio->generation;
779146950Sps			cio32->offset = cio->offset;
780146950Sps			cio32->num_matches = cio->num_matches;
781146950Sps			free(cio, M_TEMP);
782146950Sps		}
783146950Sps		if (pattern_buf_old32 != NULL)
784146950Sps			free(pattern_buf_old32, M_TEMP);
785142934Sps#endif
786142934Sps		if (pattern_buf_old != NULL)
787142934Sps			free(pattern_buf_old, M_TEMP);
788146950Sps#endif
789146950Sps		if (pattern_buf != NULL)
790146950Sps			free(pattern_buf, M_TEMP);
791146950Sps
792146950Sps		break;
793146950Sps
794146950Sps#ifdef PRE7_COMPAT
795146950Sps	case PCIOCREAD_OLD:
796146950Sps	case PCIOCWRITE_OLD:
797146950Sps		io_old = (struct pci_io_old *)data;
798146950Sps		iodata.pi_sel.pc_domain = 0;
799146950Sps		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
800146950Sps		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
801146950Sps		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
802146950Sps		iodata.pi_reg = io_old->pi_reg;
803146950Sps		iodata.pi_width = io_old->pi_width;
804146950Sps		iodata.pi_data = io_old->pi_data;
805146950Sps		data = (caddr_t)&iodata;
806142934Sps		/* FALLTHROUGH */
807146950Sps#endif
808146950Sps	case PCIOCREAD:
809142934Sps	case PCIOCWRITE:
810133741Sjmg		io = (struct pci_io *)data;
811142934Sps		switch(io->pi_width) {
81259290Sjlemon		case 4:
81386341Sdillon		case 2:
81459290Sjlemon		case 1:
81559290Sjlemon			/* Make sure register is not negative and aligned. */
816142934Sps			if (io->pi_reg < 0 ||
81789319Salfred			    io->pi_reg & (io->pi_width - 1)) {
818170029Srwatson				error = EINVAL;
819133741Sjmg				break;
820133741Sjmg			}
82159290Sjlemon			/*
82259290Sjlemon			 * Assume that the user-level bus number is
823142934Sps			 * in fact the physical PCI bus number.
824146950Sps			 * Look up the grandparent, i.e. the bridge device,
825146950Sps			 * so that we can issue configuration space cycles.
826146950Sps			 */
827146950Sps			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
828146950Sps			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
82959290Sjlemon			    io->pi_sel.pc_func);
830142934Sps			if (pcidev) {
831151260Sambrisko				brdev = device_get_parent(
832151260Sambrisko				    device_get_parent(pcidev));
83363452Sjlemon
834133741Sjmg#ifdef PRE7_COMPAT
835197243Ssson				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
836142934Sps#else
83763452Sjlemon				if (cmd == PCIOCWRITE)
83863452Sjlemon#endif
839146950Sps					PCIB_WRITE_CONFIG(brdev,
840146950Sps							  io->pi_sel.pc_bus,
841142934Sps							  io->pi_sel.pc_dev,
84259290Sjlemon							  io->pi_sel.pc_func,
84359290Sjlemon							  io->pi_reg,
84468883Sdillon							  io->pi_data,
84559290Sjlemon							  io->pi_width);
84659290Sjlemon#ifdef PRE7_COMPAT
84759290Sjlemon				else if (cmd == PCIOCREAD_OLD)
848142934Sps					io_old->pi_data =
84959290Sjlemon						PCIB_READ_CONFIG(brdev,
85059290Sjlemon							  io->pi_sel.pc_bus,
851133741Sjmg							  io->pi_sel.pc_dev,
85268883Sdillon							  io->pi_sel.pc_func,
85368883Sdillon							  io->pi_reg,
85459290Sjlemon							  io->pi_width);
85559290Sjlemon#endif
856146950Sps				else
85768883Sdillon					io->pi_data =
858133741Sjmg						PCIB_READ_CONFIG(brdev,
859133741Sjmg							  io->pi_sel.pc_bus,
860170066Srwatson							  io->pi_sel.pc_dev,
86159290Sjlemon							  io->pi_sel.pc_func,
86259290Sjlemon							  io->pi_reg,
86359290Sjlemon							  io->pi_width);
86459290Sjlemon				error = 0;
86588633Salfred			} else {
86688633Salfred#ifdef COMPAT_FREEBSD4
867133741Sjmg				if (cmd == PCIOCREAD_OLD) {
86888633Salfred					io_old->pi_data = -1;
869133741Sjmg					error = 0;
870133741Sjmg				} else
871133741Sjmg#endif
872133741Sjmg					error = ENODEV;
873133741Sjmg			}
874133741Sjmg			break;
875133741Sjmg		default:
876133741Sjmg			error = EINVAL;
877133741Sjmg			break;
878133741Sjmg		}
879133741Sjmg		break;
880133741Sjmg
881133741Sjmg	case PCIOCGETBAR:
882133741Sjmg		bio = (struct pci_bar_io *)data;
883133741Sjmg
884133741Sjmg		/*
88588633Salfred		 * Assume that the user-level bus number is
88688633Salfred		 * in fact the physical PCI bus number.
88788633Salfred		 */
88888633Salfred		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
88988633Salfred		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
89088633Salfred		    bio->pbi_sel.pc_func);
891133741Sjmg		if (pcidev == NULL) {
89288633Salfred			error = ENODEV;
893133741Sjmg			break;
894133741Sjmg		}
895133741Sjmg		pm = pci_find_bar(pcidev, bio->pbi_reg);
896133741Sjmg		if (pm == NULL) {
897133741Sjmg			error = EINVAL;
898133741Sjmg			break;
899133741Sjmg		}
900133741Sjmg		bio->pbi_base = pm->pm_value;
901133741Sjmg		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
902133741Sjmg		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
903133741Sjmg		error = 0;
904133741Sjmg		break;
905133741Sjmg	case PCIOCATTACHED:
906133741Sjmg		error = 0;
907133741Sjmg		io = (struct pci_io *)data;
908133741Sjmg		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
909133741Sjmg				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
91088633Salfred		if (pcidev != NULL)
91188633Salfred			io->pi_data = device_is_attached(pcidev);
912133741Sjmg		else
913133741Sjmg			error = ENODEV;
914133741Sjmg		break;
915133741Sjmg	default:
916133741Sjmg		error = ENOTTY;
917133741Sjmg		break;
918133741Sjmg	}
919133741Sjmg
920133741Sjmg	return (error);
921133741Sjmg}
922133741Sjmg