pci_user.c revision 240992
1119418Sobrien/*-
269953Smsmith * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
369953Smsmith * All rights reserved.
469953Smsmith *
569953Smsmith * Redistribution and use in source and binary forms, with or without
669953Smsmith * modification, are permitted provided that the following conditions
769953Smsmith * are met:
869953Smsmith * 1. Redistributions of source code must retain the above copyright
969953Smsmith *    notice unmodified, this list of conditions, and the following
1069953Smsmith *    disclaimer.
1169953Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1269953Smsmith *    notice, this list of conditions and the following disclaimer in the
1369953Smsmith *    documentation and/or other materials provided with the distribution.
1469953Smsmith *
1569953Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1669953Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1769953Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1869953Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1969953Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2069953Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2169953Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2269953Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2369953Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2469953Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2569953Smsmith */
2669953Smsmith
27119418Sobrien#include <sys/cdefs.h>
28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 240992 2012-09-27 10:46:22Z glebius $");
29119418Sobrien
3069953Smsmith#include "opt_bus.h"	/* XXX trim includes */
31149478Sps#include "opt_compat.h"
3269953Smsmith
3369953Smsmith#include <sys/param.h>
3469953Smsmith#include <sys/systm.h>
3569953Smsmith#include <sys/malloc.h>
3669953Smsmith#include <sys/module.h>
3769953Smsmith#include <sys/linker.h>
3869953Smsmith#include <sys/fcntl.h>
3969953Smsmith#include <sys/conf.h>
4069953Smsmith#include <sys/kernel.h>
4183975Srwatson#include <sys/proc.h>
4269953Smsmith#include <sys/queue.h>
4369953Smsmith#include <sys/types.h>
4469953Smsmith
4569953Smsmith#include <vm/vm.h>
4669953Smsmith#include <vm/pmap.h>
4769953Smsmith#include <vm/vm_extern.h>
4869953Smsmith
4969953Smsmith#include <sys/bus.h>
5069953Smsmith#include <machine/bus.h>
5169953Smsmith#include <sys/rman.h>
5269953Smsmith#include <machine/resource.h>
5369953Smsmith
5469953Smsmith#include <sys/pciio.h>
55119285Simp#include <dev/pci/pcireg.h>
56119285Simp#include <dev/pci/pcivar.h>
5769953Smsmith
5869953Smsmith#include "pcib_if.h"
5969953Smsmith#include "pci_if.h"
6069953Smsmith
6169953Smsmith/*
6269953Smsmith * This is the user interface to PCI configuration space.
6369953Smsmith */
6469953Smsmith
6583366Sjulianstatic d_open_t 	pci_open;
6683366Sjulianstatic d_close_t	pci_close;
6769953Smsmithstatic int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
6869953Smsmith			       struct pci_conf *match_buf);
6983366Sjulianstatic d_ioctl_t	pci_ioctl;
7069953Smsmith
7169953Smsmithstruct cdevsw pcicdev = {
72126080Sphk	.d_version =	D_VERSION,
73126080Sphk	.d_flags =	D_NEEDGIANT,
74111815Sphk	.d_open =	pci_open,
75111815Sphk	.d_close =	pci_close,
76111815Sphk	.d_ioctl =	pci_ioctl,
77111815Sphk	.d_name =	"pci",
7869953Smsmith};
7969953Smsmith
8069953Smsmithstatic int
81130585Sphkpci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
8269953Smsmith{
8383975Srwatson	int error;
8483975Srwatson
8583975Srwatson	if (oflags & FWRITE) {
8691406Sjhb		error = securelevel_gt(td->td_ucred, 0);
8783975Srwatson		if (error)
8883975Srwatson			return (error);
8969953Smsmith	}
9083975Srwatson
9183975Srwatson	return (0);
9269953Smsmith}
9369953Smsmith
9469953Smsmithstatic int
95130585Sphkpci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
9669953Smsmith{
9769953Smsmith	return 0;
9869953Smsmith}
9969953Smsmith
10069953Smsmith/*
10169953Smsmith * Match a single pci_conf structure against an array of pci_match_conf
10269953Smsmith * structures.  The first argument, 'matches', is an array of num_matches
10369953Smsmith * pci_match_conf structures.  match_buf is a pointer to the pci_conf
10469953Smsmith * structure that will be compared to every entry in the matches array.
10569953Smsmith * This function returns 1 on failure, 0 on success.
10669953Smsmith */
10769953Smsmithstatic int
10869953Smsmithpci_conf_match(struct pci_match_conf *matches, int num_matches,
10969953Smsmith	       struct pci_conf *match_buf)
11069953Smsmith{
11169953Smsmith	int i;
11269953Smsmith
11369953Smsmith	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
11469953Smsmith		return(1);
11569953Smsmith
11669953Smsmith	for (i = 0; i < num_matches; i++) {
11769953Smsmith		/*
11869953Smsmith		 * I'm not sure why someone would do this...but...
11969953Smsmith		 */
12069953Smsmith		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
12169953Smsmith			continue;
12269953Smsmith
12369953Smsmith		/*
12469953Smsmith		 * Look at each of the match flags.  If it's set, do the
12569953Smsmith		 * comparison.  If the comparison fails, we don't have a
12669953Smsmith		 * match, go on to the next item if there is one.
12769953Smsmith		 */
128172394Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
129172394Smarius		 && (match_buf->pc_sel.pc_domain !=
130172394Smarius		 matches[i].pc_sel.pc_domain))
131172394Smarius			continue;
132172394Smarius
13369953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
13469953Smsmith		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
13569953Smsmith			continue;
13669953Smsmith
13769953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
13869953Smsmith		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
13969953Smsmith			continue;
14069953Smsmith
14169953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
14269953Smsmith		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
14369953Smsmith			continue;
14469953Smsmith
14569953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
14669953Smsmith		 && (match_buf->pc_vendor != matches[i].pc_vendor))
14769953Smsmith			continue;
14869953Smsmith
14969953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
15069953Smsmith		 && (match_buf->pc_device != matches[i].pc_device))
15169953Smsmith			continue;
15269953Smsmith
15369953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
15469953Smsmith		 && (match_buf->pc_class != matches[i].pc_class))
15569953Smsmith			continue;
15669953Smsmith
15769953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
15869953Smsmith		 && (match_buf->pd_unit != matches[i].pd_unit))
15969953Smsmith			continue;
16069953Smsmith
16169953Smsmith		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
16269953Smsmith		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
16369953Smsmith			     sizeof(match_buf->pd_name)) != 0))
16469953Smsmith			continue;
16569953Smsmith
16669953Smsmith		return(0);
16769953Smsmith	}
16869953Smsmith
16969953Smsmith	return(1);
17069953Smsmith}
17169953Smsmith
172172932Smarius#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
173172932Smarius    defined(COMPAT_FREEBSD6)
174172999Simp#define PRE7_COMPAT
175172932Smarius
176172932Smariustypedef enum {
177172932Smarius	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
178172932Smarius	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
179172932Smarius	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
180172932Smarius	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
181172932Smarius	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
182172932Smarius	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
183172932Smarius	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
184172932Smarius	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
185172932Smarius	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
186172932Smarius} pci_getconf_flags_old;
187172932Smarius
188172932Smariusstruct pcisel_old {
189172932Smarius	u_int8_t	pc_bus;		/* bus number */
190172932Smarius	u_int8_t	pc_dev;		/* device on this bus */
191172932Smarius	u_int8_t	pc_func;	/* function on this device */
192172932Smarius};
193172932Smarius
194172932Smariusstruct pci_conf_old {
195172932Smarius	struct pcisel_old pc_sel;	/* bus+slot+function */
196172932Smarius	u_int8_t	pc_hdr;		/* PCI header type */
197172932Smarius	u_int16_t	pc_subvendor;	/* card vendor ID */
198172932Smarius	u_int16_t	pc_subdevice;	/* card device ID, assigned by
199172932Smarius					   card vendor */
200172932Smarius	u_int16_t	pc_vendor;	/* chip vendor ID */
201172932Smarius	u_int16_t	pc_device;	/* chip device ID, assigned by
202172932Smarius					   chip vendor */
203172932Smarius	u_int8_t	pc_class;	/* chip PCI class */
204172932Smarius	u_int8_t	pc_subclass;	/* chip PCI subclass */
205172932Smarius	u_int8_t	pc_progif;	/* chip PCI programming interface */
206172932Smarius	u_int8_t	pc_revid;	/* chip revision ID */
207172932Smarius	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
208172932Smarius	u_long		pd_unit;	/* device unit number */
209172932Smarius};
210172932Smarius
211172932Smariusstruct pci_match_conf_old {
212172932Smarius	struct pcisel_old	pc_sel;		/* bus+slot+function */
213172932Smarius	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
214172932Smarius	u_long			pd_unit;	/* Unit number */
215172932Smarius	u_int16_t		pc_vendor;	/* PCI Vendor ID */
216172932Smarius	u_int16_t		pc_device;	/* PCI Device ID */
217172932Smarius	u_int8_t		pc_class;	/* PCI class */
218174932Smarius	pci_getconf_flags_old	flags;		/* Matching expression */
219172932Smarius};
220172932Smarius
221172932Smariusstruct pci_io_old {
222172932Smarius	struct pcisel_old pi_sel;	/* device to operate on */
223172932Smarius	int		pi_reg;		/* configuration register to examine */
224172932Smarius	int		pi_width;	/* width (in bytes) of read or write */
225172932Smarius	u_int32_t	pi_data;	/* data to write or result of read */
226172932Smarius};
227172932Smarius
228240981Ssobomax#ifdef COMPAT_FREEBSD32
229240981Ssobomaxstruct pci_conf_old32 {
230240992Sglebius	struct pcisel_old pc_sel;	/* bus+slot+function */
231240992Sglebius	uint8_t		pc_hdr;		/* PCI header type */
232240992Sglebius	uint16_t	pc_subvendor;	/* card vendor ID */
233240992Sglebius	uint16_t	pc_subdevice;	/* card device ID, assigned by
234240992Sglebius					   card vendor */
235240992Sglebius	uint16_t	pc_vendor;	/* chip vendor ID */
236240992Sglebius	uint16_t	pc_device;	/* chip device ID, assigned by
237240992Sglebius					   chip vendor */
238240992Sglebius	uint8_t		pc_class;	/* chip PCI class */
239240992Sglebius	uint8_t		pc_subclass;	/* chip PCI subclass */
240240992Sglebius	uint8_t		pc_progif;	/* chip PCI programming interface */
241240992Sglebius	uint8_t		pc_revid;	/* chip revision ID */
242240992Sglebius	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
243240992Sglebius	uint32_t	pd_unit;	/* device unit number (u_long) */
244240981Ssobomax};
245240981Ssobomax
246240981Ssobomaxstruct pci_match_conf_old32 {
247240992Sglebius	struct pcisel_old pc_sel;	/* bus+slot+function */
248240992Sglebius	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
249240992Sglebius	uint32_t	pd_unit;	/* Unit number (u_long) */
250240992Sglebius	uint16_t	pc_vendor;	/* PCI Vendor ID */
251240992Sglebius	uint16_t	pc_device;	/* PCI Device ID */
252240992Sglebius	uint8_t		pc_class;	/* PCI class */
253240992Sglebius	pci_getconf_flags_old flags;	/* Matching expression */
254240981Ssobomax};
255240981Ssobomax
256240981Ssobomaxstruct pci_conf_io32 {
257240992Sglebius	uint32_t	pat_buf_len;	/* pattern buffer length */
258240992Sglebius	uint32_t	num_patterns;	/* number of patterns */
259240992Sglebius	uint32_t	patterns;	/* pattern buffer
260240992Sglebius					   (struct pci_match_conf_old32 *) */
261240992Sglebius	uint32_t	match_buf_len;	/* match buffer length */
262240992Sglebius	uint32_t	num_matches;	/* number of matches returned */
263240992Sglebius	uint32_t	matches;	/* match buffer
264240992Sglebius					   (struct pci_conf_old32 *) */
265240992Sglebius	uint32_t	offset;		/* offset into device list */
266240992Sglebius	uint32_t	generation;	/* device list generation */
267240992Sglebius	pci_getconf_status status;	/* request status */
268240981Ssobomax};
269240981Ssobomax
270240992Sglebius#define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
271240990Sglebius#endif	/* COMPAT_FREEBSD32 */
272240981Ssobomax
273172932Smarius#define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
274172932Smarius#define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
275172932Smarius#define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
276172932Smarius
277172932Smariusstatic int	pci_conf_match_old(struct pci_match_conf_old *matches,
278172932Smarius		    int num_matches, struct pci_conf *match_buf);
279172932Smarius
28069953Smsmithstatic int
281172932Smariuspci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
282172932Smarius    struct pci_conf *match_buf)
283172932Smarius{
284172932Smarius	int i;
285172932Smarius
286172932Smarius	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
287172932Smarius		return(1);
288172932Smarius
289172932Smarius	for (i = 0; i < num_matches; i++) {
290172932Smarius		if (match_buf->pc_sel.pc_domain != 0)
291172932Smarius			continue;
292172932Smarius
293172932Smarius		/*
294172932Smarius		 * I'm not sure why someone would do this...but...
295172932Smarius		 */
296172932Smarius		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
297172932Smarius			continue;
298172932Smarius
299172932Smarius		/*
300172932Smarius		 * Look at each of the match flags.  If it's set, do the
301172932Smarius		 * comparison.  If the comparison fails, we don't have a
302172932Smarius		 * match, go on to the next item if there is one.
303172932Smarius		 */
304172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
305172932Smarius		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
306172932Smarius			continue;
307172932Smarius
308172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
309172932Smarius		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
310172932Smarius			continue;
311172932Smarius
312172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
313172932Smarius		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
314172932Smarius			continue;
315172932Smarius
316172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
317172932Smarius		 && (match_buf->pc_vendor != matches[i].pc_vendor))
318172932Smarius			continue;
319172932Smarius
320172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
321172932Smarius		 && (match_buf->pc_device != matches[i].pc_device))
322172932Smarius			continue;
323172932Smarius
324172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
325172932Smarius		 && (match_buf->pc_class != matches[i].pc_class))
326172932Smarius			continue;
327172932Smarius
328172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
329172932Smarius		 && (match_buf->pd_unit != matches[i].pd_unit))
330172932Smarius			continue;
331172932Smarius
332172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
333172932Smarius		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
334172932Smarius			     sizeof(match_buf->pd_name)) != 0))
335172932Smarius			continue;
336172932Smarius
337172932Smarius		return(0);
338172932Smarius	}
339172932Smarius
340172932Smarius	return(1);
341172932Smarius}
342172932Smarius
343240990Sglebius#ifdef COMPAT_FREEBSD32
344240981Ssobomaxstatic int
345240981Ssobomaxpci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
346240981Ssobomax    struct pci_conf *match_buf)
347240981Ssobomax{
348240992Sglebius	int i;
349240981Ssobomax
350240992Sglebius	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
351240992Sglebius		return(1);
352240981Ssobomax
353240992Sglebius	for (i = 0; i < num_matches; i++) {
354240992Sglebius		if (match_buf->pc_sel.pc_domain != 0)
355240992Sglebius			continue;
356240981Ssobomax
357240992Sglebius		/*
358240992Sglebius		 * I'm not sure why someone would do this...but...
359240992Sglebius		 */
360240992Sglebius		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
361240992Sglebius			continue;
362240981Ssobomax
363240992Sglebius		/*
364240992Sglebius		 * Look at each of the match flags.  If it's set, do the
365240992Sglebius		 * comparison.  If the comparison fails, we don't have a
366240992Sglebius		 * match, go on to the next item if there is one.
367240992Sglebius		 */
368240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
369240992Sglebius		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
370240992Sglebius			continue;
371240981Ssobomax
372240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
373240992Sglebius		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
374240992Sglebius			continue;
375240981Ssobomax
376240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
377240992Sglebius		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
378240992Sglebius			continue;
379240981Ssobomax
380240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
381240992Sglebius		    (match_buf->pc_vendor != matches[i].pc_vendor))
382240992Sglebius			continue;
383240981Ssobomax
384240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
385240992Sglebius		    (match_buf->pc_device != matches[i].pc_device))
386240992Sglebius			continue;
387240981Ssobomax
388240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
389240992Sglebius		    (match_buf->pc_class != matches[i].pc_class))
390240992Sglebius			continue;
391240981Ssobomax
392240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
393240992Sglebius		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
394240992Sglebius			continue;
395240981Ssobomax
396240992Sglebius		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
397240992Sglebius		    (strncmp(matches[i].pd_name, match_buf->pd_name,
398240992Sglebius		    sizeof(match_buf->pd_name)) != 0))
399240992Sglebius			continue;
400240981Ssobomax
401240992Sglebius		return (0);
402240992Sglebius	}
403240981Ssobomax
404240992Sglebius	return (1);
405240981Ssobomax}
406240990Sglebius#endif	/* COMPAT_FREEBSD32 */
407240990Sglebius#endif	/* PRE7_COMPAT */
408240981Ssobomax
409172932Smariusstatic int
410130585Sphkpci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
41169953Smsmith{
412172932Smarius	device_t pcidev, brdev;
413172932Smarius	void *confdata;
414172932Smarius	const char *name;
415172932Smarius	struct devlist *devlist_head;
416240990Sglebius	struct pci_conf_io *cio = NULL;
417172932Smarius	struct pci_devinfo *dinfo;
41869953Smsmith	struct pci_io *io;
419188018Sjhb	struct pci_bar_io *bio;
420172932Smarius	struct pci_match_conf *pattern_buf;
421220195Sjhb	struct pci_map *pm;
422172932Smarius	size_t confsz, iolen, pbufsz;
423172932Smarius	int error, ionum, i, num_patterns;
424172999Simp#ifdef PRE7_COMPAT
425240981Ssobomax#ifdef COMPAT_FREEBSD32
426240990Sglebius	struct pci_conf_io32 *cio32 = NULL;
427240990Sglebius	struct pci_conf_old32 conf_old32;
428240990Sglebius	struct pci_match_conf_old32 *pattern_buf_old32;
429240981Ssobomax#endif
430172932Smarius	struct pci_conf_old conf_old;
431172932Smarius	struct pci_io iodata;
432172932Smarius	struct pci_io_old *io_old;
433172932Smarius	struct pci_match_conf_old *pattern_buf_old;
43469953Smsmith
435172932Smarius	io_old = NULL;
436172932Smarius
437188018Sjhb	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
438188018Sjhb	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
439172932Smarius		return EPERM;
440172932Smarius#else
441188018Sjhb	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
44269953Smsmith		return EPERM;
443172932Smarius#endif
44469953Smsmith
44569953Smsmith	switch(cmd) {
446172999Simp#ifdef PRE7_COMPAT
447240990Sglebius#ifdef COMPAT_FREEBSD32
448240981Ssobomax       case PCIOCGETCONF_OLD32:
449240981Ssobomax               cio32 = (struct pci_conf_io32 *)data;
450240981Ssobomax               cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
451240981Ssobomax               cio->pat_buf_len = cio32->pat_buf_len;
452240981Ssobomax               cio->num_patterns = cio32->num_patterns;
453240981Ssobomax               cio->patterns = (void *)(uintptr_t)cio32->patterns;
454240981Ssobomax               cio->match_buf_len = cio32->match_buf_len;
455240981Ssobomax               cio->num_matches = cio32->num_matches;
456240981Ssobomax               cio->matches = (void *)(uintptr_t)cio32->matches;
457240981Ssobomax               cio->offset = cio32->offset;
458240981Ssobomax               cio->generation = cio32->generation;
459240981Ssobomax               cio->status = cio32->status;
460240981Ssobomax               cio32->num_matches = 0;
461240990Sglebius               break;
462240990Sglebius#endif
463240990Sglebius	case PCIOCGETCONF_OLD:
464240990Sglebius#endif
465240990Sglebius	case PCIOCGETCONF:
466240990Sglebius		cio = (struct pci_conf_io *)data;
467240990Sglebius	}
468240981Ssobomax
469240990Sglebius	switch(cmd) {
470240990Sglebius#ifdef PRE7_COMPAT
471240990Sglebius#ifdef COMPAT_FREEBSD32
472240990Sglebius	case PCIOCGETCONF_OLD32:
473240990Sglebius		pattern_buf_old32 = NULL;
474240990Sglebius#endif
475172932Smarius	case PCIOCGETCONF_OLD:
476240990Sglebius		pattern_buf_old = NULL;
477172932Smarius#endif
47869953Smsmith	case PCIOCGETCONF:
47969953Smsmith
480172932Smarius		pattern_buf = NULL;
48169953Smsmith		num_patterns = 0;
48269953Smsmith		dinfo = NULL;
48369953Smsmith
484172932Smarius		cio->num_matches = 0;
485172932Smarius
48669953Smsmith		/*
48769953Smsmith		 * If the user specified an offset into the device list,
48869953Smsmith		 * but the list has changed since they last called this
48969953Smsmith		 * ioctl, tell them that the list has changed.  They will
49069953Smsmith		 * have to get the list from the beginning.
49169953Smsmith		 */
49269953Smsmith		if ((cio->offset != 0)
49369953Smsmith		 && (cio->generation != pci_generation)){
49469953Smsmith			cio->status = PCI_GETCONF_LIST_CHANGED;
49569953Smsmith			error = 0;
496240992Sglebius			goto getconfexit;
49769953Smsmith		}
49869953Smsmith
49969953Smsmith		/*
50069953Smsmith		 * Check to see whether the user has asked for an offset
50169953Smsmith		 * past the end of our list.
50269953Smsmith		 */
50369953Smsmith		if (cio->offset >= pci_numdevs) {
50469953Smsmith			cio->status = PCI_GETCONF_LAST_DEVICE;
50569953Smsmith			error = 0;
506240992Sglebius			goto getconfexit;
50769953Smsmith		}
50869953Smsmith
50969953Smsmith		/* get the head of the device queue */
51069953Smsmith		devlist_head = &pci_devq;
51169953Smsmith
51269953Smsmith		/*
51369953Smsmith		 * Determine how much room we have for pci_conf structures.
51469953Smsmith		 * Round the user's buffer size down to the nearest
51569953Smsmith		 * multiple of sizeof(struct pci_conf) in case the user
51669953Smsmith		 * didn't specify a multiple of that size.
51769953Smsmith		 */
518172999Simp#ifdef PRE7_COMPAT
519240981Ssobomax#ifdef COMPAT_FREEBSD32
520240992Sglebius		if (cmd == PCIOCGETCONF_OLD32)
521240992Sglebius			confsz = sizeof(struct pci_conf_old32);
522240992Sglebius		else
523240981Ssobomax#endif
524172932Smarius		if (cmd == PCIOCGETCONF_OLD)
525172932Smarius			confsz = sizeof(struct pci_conf_old);
526172932Smarius		else
527172932Smarius#endif
528172932Smarius			confsz = sizeof(struct pci_conf);
529172932Smarius		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
530172932Smarius		    pci_numdevs * confsz);
53169953Smsmith
53269953Smsmith		/*
53369953Smsmith		 * Since we know that iolen is a multiple of the size of
53469953Smsmith		 * the pciconf union, it's okay to do this.
53569953Smsmith		 */
536172932Smarius		ionum = iolen / confsz;
53769953Smsmith
53869953Smsmith		/*
53969953Smsmith		 * If this test is true, the user wants the pci_conf
54069953Smsmith		 * structures returned to match the supplied entries.
54169953Smsmith		 */
542116704Sjmg		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
54369953Smsmith		 && (cio->pat_buf_len > 0)) {
54469953Smsmith			/*
54569953Smsmith			 * pat_buf_len needs to be:
54669953Smsmith			 * num_patterns * sizeof(struct pci_match_conf)
54769953Smsmith			 * While it is certainly possible the user just
54869953Smsmith			 * allocated a large buffer, but set the number of
54969953Smsmith			 * matches correctly, it is far more likely that
55069953Smsmith			 * their kernel doesn't match the userland utility
55169953Smsmith			 * they're using.  It's also possible that the user
55269953Smsmith			 * forgot to initialize some variables.  Yes, this
55369953Smsmith			 * may be overly picky, but I hazard to guess that
55469953Smsmith			 * it's far more likely to just catch folks that
55569953Smsmith			 * updated their kernel but not their userland.
55669953Smsmith			 */
557172999Simp#ifdef PRE7_COMPAT
558240981Ssobomax#ifdef COMPAT_FREEBSD32
559240992Sglebius			if (cmd == PCIOCGETCONF_OLD32)
560240992Sglebius				pbufsz = sizeof(struct pci_match_conf_old32);
561240992Sglebius			else
562240981Ssobomax#endif
563172932Smarius			if (cmd == PCIOCGETCONF_OLD)
564172932Smarius				pbufsz = sizeof(struct pci_match_conf_old);
565172932Smarius			else
566172932Smarius#endif
567172932Smarius				pbufsz = sizeof(struct pci_match_conf);
568172932Smarius			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
569172932Smarius				/* The user made a mistake, return an error. */
57069953Smsmith				cio->status = PCI_GETCONF_ERROR;
57169953Smsmith				error = EINVAL;
572240992Sglebius				goto getconfexit;
57369953Smsmith			}
57469953Smsmith
57569953Smsmith			/*
57669953Smsmith			 * Allocate a buffer to hold the patterns.
57769953Smsmith			 */
578172999Simp#ifdef PRE7_COMPAT
579240990Sglebius#ifdef COMPAT_FREEBSD32
580240992Sglebius			if (cmd == PCIOCGETCONF_OLD32) {
581240992Sglebius				pattern_buf_old32 = malloc(cio->pat_buf_len,
582240992Sglebius				    M_TEMP, M_WAITOK);
583240992Sglebius				error = copyin(cio->patterns,
584240992Sglebius				    pattern_buf_old32, cio->pat_buf_len);
585240992Sglebius			} else
586240990Sglebius#endif /* COMPAT_FREEBSD32 */
587172932Smarius			if (cmd == PCIOCGETCONF_OLD) {
588172932Smarius				pattern_buf_old = malloc(cio->pat_buf_len,
589172932Smarius				    M_TEMP, M_WAITOK);
590172932Smarius				error = copyin(cio->patterns,
591172932Smarius				    pattern_buf_old, cio->pat_buf_len);
592172932Smarius			} else
593240990Sglebius#endif /* PRE7_COMPAT */
594172932Smarius			{
595172932Smarius				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
596172932Smarius				    M_WAITOK);
597172932Smarius				error = copyin(cio->patterns, pattern_buf,
598172932Smarius				    cio->pat_buf_len);
599172932Smarius			}
600116702Sjmg			if (error != 0) {
601116702Sjmg				error = EINVAL;
602116702Sjmg				goto getconfexit;
603116702Sjmg			}
60469953Smsmith			num_patterns = cio->num_patterns;
60569953Smsmith		} else if ((cio->num_patterns > 0)
60669953Smsmith			|| (cio->pat_buf_len > 0)) {
60769953Smsmith			/*
60869953Smsmith			 * The user made a mistake, spit out an error.
60969953Smsmith			 */
61069953Smsmith			cio->status = PCI_GETCONF_ERROR;
61169953Smsmith			error = EINVAL;
612240981Ssobomax                       goto getconfexit;
613172932Smarius		}
61469953Smsmith
61569953Smsmith		/*
61669953Smsmith		 * Go through the list of devices and copy out the devices
61769953Smsmith		 * that match the user's criteria.
61869953Smsmith		 */
61969953Smsmith		for (cio->num_matches = 0, error = 0, i = 0,
62069953Smsmith		     dinfo = STAILQ_FIRST(devlist_head);
62169953Smsmith		     (dinfo != NULL) && (cio->num_matches < ionum)
622116702Sjmg		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
62369953Smsmith		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
62469953Smsmith
62569953Smsmith			if (i < cio->offset)
62669953Smsmith				continue;
62769953Smsmith
62869953Smsmith			/* Populate pd_name and pd_unit */
62969953Smsmith			name = NULL;
630175368Sjhb			if (dinfo->cfg.dev)
63169953Smsmith				name = device_get_name(dinfo->cfg.dev);
63269953Smsmith			if (name) {
63369953Smsmith				strncpy(dinfo->conf.pd_name, name,
63469953Smsmith					sizeof(dinfo->conf.pd_name));
63569953Smsmith				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
63669953Smsmith				dinfo->conf.pd_unit =
63769953Smsmith					device_get_unit(dinfo->cfg.dev);
638175368Sjhb			} else {
639175368Sjhb				dinfo->conf.pd_name[0] = '\0';
640175368Sjhb				dinfo->conf.pd_unit = 0;
64169953Smsmith			}
64269953Smsmith
643172999Simp#ifdef PRE7_COMPAT
644240990Sglebius			if (
645240990Sglebius#ifdef COMPAT_FREEBSD32
646240990Sglebius			    (cmd == PCIOCGETCONF_OLD32 &&
647240990Sglebius			    (pattern_buf_old32 == NULL ||
648240990Sglebius			    pci_conf_match_old32(pattern_buf_old32,
649240990Sglebius			    num_patterns, &dinfo->conf) == 0)) ||
650240990Sglebius#endif
651240990Sglebius			    (cmd == PCIOCGETCONF_OLD &&
652172932Smarius			    (pattern_buf_old == NULL ||
653172932Smarius			    pci_conf_match_old(pattern_buf_old, num_patterns,
654172932Smarius			    &dinfo->conf) == 0)) ||
655172932Smarius			    (cmd == PCIOCGETCONF &&
656172932Smarius			    (pattern_buf == NULL ||
657172932Smarius			    pci_conf_match(pattern_buf, num_patterns,
658172932Smarius			    &dinfo->conf) == 0))) {
659172932Smarius#else
660172932Smarius			if (pattern_buf == NULL ||
661172932Smarius			    pci_conf_match(pattern_buf, num_patterns,
662172932Smarius			    &dinfo->conf) == 0) {
663172932Smarius#endif
66469953Smsmith				/*
66569953Smsmith				 * If we've filled up the user's buffer,
66669953Smsmith				 * break out at this point.  Since we've
66769953Smsmith				 * got a match here, we'll pick right back
66869953Smsmith				 * up at the matching entry.  We can also
66969953Smsmith				 * tell the user that there are more matches
67069953Smsmith				 * left.
67169953Smsmith				 */
67269953Smsmith				if (cio->num_matches >= ionum)
67369953Smsmith					break;
67469953Smsmith
675172999Simp#ifdef PRE7_COMPAT
676240990Sglebius#ifdef COMPAT_FREEBSD32
677240992Sglebius				if (cmd == PCIOCGETCONF_OLD32) {
678240992Sglebius					conf_old32.pc_sel.pc_bus =
679240992Sglebius					    dinfo->conf.pc_sel.pc_bus;
680240992Sglebius					conf_old32.pc_sel.pc_dev =
681240992Sglebius					    dinfo->conf.pc_sel.pc_dev;
682240992Sglebius					conf_old32.pc_sel.pc_func =
683240992Sglebius					    dinfo->conf.pc_sel.pc_func;
684240992Sglebius					conf_old32.pc_hdr = dinfo->conf.pc_hdr;
685240992Sglebius					conf_old32.pc_subvendor =
686240992Sglebius					    dinfo->conf.pc_subvendor;
687240992Sglebius					conf_old32.pc_subdevice =
688240992Sglebius					    dinfo->conf.pc_subdevice;
689240992Sglebius					conf_old32.pc_vendor =
690240992Sglebius					    dinfo->conf.pc_vendor;
691240992Sglebius					conf_old32.pc_device =
692240992Sglebius					    dinfo->conf.pc_device;
693240992Sglebius					conf_old32.pc_class =
694240992Sglebius					    dinfo->conf.pc_class;
695240992Sglebius					conf_old32.pc_subclass =
696240992Sglebius					    dinfo->conf.pc_subclass;
697240992Sglebius					conf_old32.pc_progif =
698240992Sglebius					    dinfo->conf.pc_progif;
699240992Sglebius					conf_old32.pc_revid =
700240992Sglebius					    dinfo->conf.pc_revid;
701240992Sglebius					strncpy(conf_old32.pd_name,
702240992Sglebius					    dinfo->conf.pd_name,
703240992Sglebius					    sizeof(conf_old32.pd_name));
704240992Sglebius					conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
705240992Sglebius					conf_old32.pd_unit =
706240992Sglebius					    (uint32_t)dinfo->conf.pd_unit;
707240992Sglebius					confdata = &conf_old32;
708240992Sglebius				} else
709240990Sglebius#endif /* COMPAT_FREEBSD32 */
710172932Smarius				if (cmd == PCIOCGETCONF_OLD) {
711172932Smarius					conf_old.pc_sel.pc_bus =
712172932Smarius					    dinfo->conf.pc_sel.pc_bus;
713172932Smarius					conf_old.pc_sel.pc_dev =
714172932Smarius					    dinfo->conf.pc_sel.pc_dev;
715172932Smarius					conf_old.pc_sel.pc_func =
716172932Smarius					    dinfo->conf.pc_sel.pc_func;
717172932Smarius					conf_old.pc_hdr = dinfo->conf.pc_hdr;
718172932Smarius					conf_old.pc_subvendor =
719172932Smarius					    dinfo->conf.pc_subvendor;
720172932Smarius					conf_old.pc_subdevice =
721172932Smarius					    dinfo->conf.pc_subdevice;
722172932Smarius					conf_old.pc_vendor =
723172932Smarius					    dinfo->conf.pc_vendor;
724172932Smarius					conf_old.pc_device =
725172932Smarius					    dinfo->conf.pc_device;
726172932Smarius					conf_old.pc_class =
727172932Smarius					    dinfo->conf.pc_class;
728172932Smarius					conf_old.pc_subclass =
729172932Smarius					    dinfo->conf.pc_subclass;
730172932Smarius					conf_old.pc_progif =
731172932Smarius					    dinfo->conf.pc_progif;
732172932Smarius					conf_old.pc_revid =
733172932Smarius					    dinfo->conf.pc_revid;
734174932Smarius					strncpy(conf_old.pd_name,
735174932Smarius					    dinfo->conf.pd_name,
736174932Smarius					    sizeof(conf_old.pd_name));
737174932Smarius					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
738174932Smarius					conf_old.pd_unit =
739174932Smarius					    dinfo->conf.pd_unit;
740172932Smarius					confdata = &conf_old;
741172932Smarius				} else
742240990Sglebius#endif /* PRE7_COMPAT */
743172932Smarius					confdata = &dinfo->conf;
744172932Smarius				/* Only if we can copy it out do we count it. */
745172932Smarius				if (!(error = copyout(confdata,
746174932Smarius				    (caddr_t)cio->matches +
747174932Smarius				    confsz * cio->num_matches, confsz)))
748116702Sjmg					cio->num_matches++;
74969953Smsmith			}
75069953Smsmith		}
75169953Smsmith
75269953Smsmith		/*
75369953Smsmith		 * Set the pointer into the list, so if the user is getting
75469953Smsmith		 * n records at a time, where n < pci_numdevs,
75569953Smsmith		 */
75669953Smsmith		cio->offset = i;
75769953Smsmith
75869953Smsmith		/*
75969953Smsmith		 * Set the generation, the user will need this if they make
76069953Smsmith		 * another ioctl call with offset != 0.
76169953Smsmith		 */
76269953Smsmith		cio->generation = pci_generation;
763172932Smarius
76469953Smsmith		/*
76569953Smsmith		 * If this is the last device, inform the user so he won't
76669953Smsmith		 * bother asking for more devices.  If dinfo isn't NULL, we
76769953Smsmith		 * know that there are more matches in the list because of
76869953Smsmith		 * the way the traversal is done.
76969953Smsmith		 */
77069953Smsmith		if (dinfo == NULL)
77169953Smsmith			cio->status = PCI_GETCONF_LAST_DEVICE;
77269953Smsmith		else
77369953Smsmith			cio->status = PCI_GETCONF_MORE_DEVS;
77469953Smsmith
775116702Sjmggetconfexit:
776240990Sglebius#ifdef PRE7_COMPAT
777240981Ssobomax#ifdef COMPAT_FREEBSD32
778240992Sglebius		if (cmd == PCIOCGETCONF_OLD32) {
779240992Sglebius			cio32->status = cio->status;
780240992Sglebius			cio32->generation = cio->generation;
781240992Sglebius			cio32->offset = cio->offset;
782240992Sglebius			cio32->num_matches = cio->num_matches;
783240990Sglebius			free(cio, M_TEMP);
784240990Sglebius		}
785240990Sglebius		if (pattern_buf_old32 != NULL)
786240990Sglebius			free(pattern_buf_old32, M_TEMP);
787240981Ssobomax#endif
788172932Smarius		if (pattern_buf_old != NULL)
789172932Smarius			free(pattern_buf_old, M_TEMP);
790172932Smarius#endif
791240990Sglebius		if (pattern_buf != NULL)
792240990Sglebius			free(pattern_buf, M_TEMP);
79369953Smsmith
79469953Smsmith		break;
795121013Sse
796172999Simp#ifdef PRE7_COMPAT
797172932Smarius	case PCIOCREAD_OLD:
798172932Smarius	case PCIOCWRITE_OLD:
799172932Smarius		io_old = (struct pci_io_old *)data;
800172932Smarius		iodata.pi_sel.pc_domain = 0;
801172932Smarius		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
802172932Smarius		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
803172932Smarius		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
804172932Smarius		iodata.pi_reg = io_old->pi_reg;
805172932Smarius		iodata.pi_width = io_old->pi_width;
806172932Smarius		iodata.pi_data = io_old->pi_data;
807172932Smarius		data = (caddr_t)&iodata;
808172932Smarius		/* FALLTHROUGH */
809172932Smarius#endif
81069953Smsmith	case PCIOCREAD:
811121013Sse	case PCIOCWRITE:
81269953Smsmith		io = (struct pci_io *)data;
81369953Smsmith		switch(io->pi_width) {
81469953Smsmith		case 4:
81569953Smsmith		case 2:
81669953Smsmith		case 1:
817197099Savg			/* Make sure register is not negative and aligned. */
818172932Smarius			if (io->pi_reg < 0 ||
819172932Smarius			    io->pi_reg & (io->pi_width - 1)) {
820163055Sru				error = EINVAL;
821163055Sru				break;
822163055Sru			}
82369953Smsmith			/*
82469953Smsmith			 * Assume that the user-level bus number is
825145022Sbms			 * in fact the physical PCI bus number.
826145022Sbms			 * Look up the grandparent, i.e. the bridge device,
827145022Sbms			 * so that we can issue configuration space cycles.
82869953Smsmith			 */
829172394Smarius			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
830172394Smarius			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
831172394Smarius			    io->pi_sel.pc_func);
832145022Sbms			if (pcidev) {
833172932Smarius				brdev = device_get_parent(
834172932Smarius				    device_get_parent(pcidev));
835145022Sbms
836172999Simp#ifdef PRE7_COMPAT
837172932Smarius				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
838172932Smarius#else
839121013Sse				if (cmd == PCIOCWRITE)
840172932Smarius#endif
841145022Sbms					PCIB_WRITE_CONFIG(brdev,
842145022Sbms							  io->pi_sel.pc_bus,
843121013Sse							  io->pi_sel.pc_dev,
844121013Sse							  io->pi_sel.pc_func,
845121013Sse							  io->pi_reg,
846121013Sse							  io->pi_data,
847121013Sse							  io->pi_width);
848172999Simp#ifdef PRE7_COMPAT
849172932Smarius				else if (cmd == PCIOCREAD_OLD)
850172932Smarius					io_old->pi_data =
851172932Smarius						PCIB_READ_CONFIG(brdev,
852172932Smarius							  io->pi_sel.pc_bus,
853172932Smarius							  io->pi_sel.pc_dev,
854172932Smarius							  io->pi_sel.pc_func,
855172932Smarius							  io->pi_reg,
856172932Smarius							  io->pi_width);
857172932Smarius#endif
858121013Sse				else
859121013Sse					io->pi_data =
860145022Sbms						PCIB_READ_CONFIG(brdev,
861145022Sbms							  io->pi_sel.pc_bus,
862121013Sse							  io->pi_sel.pc_dev,
863121013Sse							  io->pi_sel.pc_func,
864121013Sse							  io->pi_reg,
865121013Sse							  io->pi_width);
86669953Smsmith				error = 0;
86769953Smsmith			} else {
868149478Sps#ifdef COMPAT_FREEBSD4
869172932Smarius				if (cmd == PCIOCREAD_OLD) {
870172932Smarius					io_old->pi_data = -1;
871149478Sps					error = 0;
872149478Sps				} else
873149478Sps#endif
874149478Sps					error = ENODEV;
87569953Smsmith			}
87669953Smsmith			break;
87769953Smsmith		default:
878116702Sjmg			error = EINVAL;
87969953Smsmith			break;
88069953Smsmith		}
88169953Smsmith		break;
88269953Smsmith
883188018Sjhb	case PCIOCGETBAR:
884188018Sjhb		bio = (struct pci_bar_io *)data;
885188018Sjhb
886188018Sjhb		/*
887188018Sjhb		 * Assume that the user-level bus number is
888188018Sjhb		 * in fact the physical PCI bus number.
889188018Sjhb		 */
890188018Sjhb		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
891188018Sjhb		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
892188018Sjhb		    bio->pbi_sel.pc_func);
893188018Sjhb		if (pcidev == NULL) {
894188018Sjhb			error = ENODEV;
895188018Sjhb			break;
896188018Sjhb		}
897220195Sjhb		pm = pci_find_bar(pcidev, bio->pbi_reg);
898220195Sjhb		if (pm == NULL) {
899188018Sjhb			error = EINVAL;
900188018Sjhb			break;
901188018Sjhb		}
902220195Sjhb		bio->pbi_base = pm->pm_value;
903220195Sjhb		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
904220195Sjhb		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
905188018Sjhb		error = 0;
906188018Sjhb		break;
907210597Sneel	case PCIOCATTACHED:
908210597Sneel		error = 0;
909210597Sneel		io = (struct pci_io *)data;
910210597Sneel		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
911210597Sneel				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
912210597Sneel		if (pcidev != NULL)
913210597Sneel			io->pi_data = device_is_attached(pcidev);
914210597Sneel		else
915210597Sneel			error = ENODEV;
916210597Sneel		break;
91769953Smsmith	default:
91869953Smsmith		error = ENOTTY;
91969953Smsmith		break;
92069953Smsmith	}
92169953Smsmith
92269953Smsmith	return (error);
92369953Smsmith}
924