pci_user.c revision 240981
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 240981 2012-09-27 04:28:55Z sobomax $");
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 {
230240981Ssobomax       struct pcisel_old pc_sel;       /* bus+slot+function */
231240981Ssobomax       u_int8_t        pc_hdr;         /* PCI header type */
232240981Ssobomax       u_int16_t       pc_subvendor;   /* card vendor ID */
233240981Ssobomax       u_int16_t       pc_subdevice;   /* card device ID, assigned by
234240981Ssobomax                                          card vendor */
235240981Ssobomax       u_int16_t       pc_vendor;      /* chip vendor ID */
236240981Ssobomax       u_int16_t       pc_device;      /* chip device ID, assigned by
237240981Ssobomax                                          chip vendor */
238240981Ssobomax       u_int8_t        pc_class;       /* chip PCI class */
239240981Ssobomax       u_int8_t        pc_subclass;    /* chip PCI subclass */
240240981Ssobomax       u_int8_t        pc_progif;      /* chip PCI programming interface */
241240981Ssobomax       u_int8_t        pc_revid;       /* chip revision ID */
242240981Ssobomax       char            pd_name[PCI_MAXNAMELEN + 1];  /* device name */
243240981Ssobomax       u_int32_t       pd_unit;        /* device unit number (u_long) */
244240981Ssobomax};
245240981Ssobomax
246240981Ssobomaxstruct pci_match_conf_old32 {
247240981Ssobomax       struct pcisel_old       pc_sel;         /* bus+slot+function */
248240981Ssobomax       char                    pd_name[PCI_MAXNAMELEN + 1];  /* device name */
249240981Ssobomax       u_int32_t               pd_unit;        /* Unit number (u_long) */
250240981Ssobomax       u_int16_t               pc_vendor;      /* PCI Vendor ID */
251240981Ssobomax       u_int16_t               pc_device;      /* PCI Device ID */
252240981Ssobomax       u_int8_t                pc_class;       /* PCI class */
253240981Ssobomax       pci_getconf_flags_old   flags;          /* Matching expression */
254240981Ssobomax};
255240981Ssobomax
256240981Ssobomaxstruct pci_conf_io32 {
257240981Ssobomax       u_int32_t               pat_buf_len;    /* pattern buffer length */
258240981Ssobomax       u_int32_t               num_patterns;   /* number of patterns */
259240981Ssobomax       u_int32_t               patterns;       /* pattern buffer (struct pci_match_conf_old32 *) */
260240981Ssobomax       u_int32_t               match_buf_len;  /* match buffer length */
261240981Ssobomax       u_int32_t               num_matches;    /* number of matches returned */
262240981Ssobomax       u_int32_t               matches;        /* match buffer (struct pci_conf_old32 *) */
263240981Ssobomax       u_int32_t               offset;         /* offset into device list */
264240981Ssobomax       u_int32_t               generation;     /* device list generation */
265240981Ssobomax       pci_getconf_status      status;         /* request status */
266240981Ssobomax};
267240981Ssobomax
268240981Ssobomax#define        PCIOCGETCONF_OLD32      _IOWR('p', 1, struct pci_conf_io32)
269240981Ssobomax#endif
270240981Ssobomax
271172932Smarius#define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
272172932Smarius#define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
273172932Smarius#define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
274172932Smarius
275172932Smariusstatic int	pci_conf_match_old(struct pci_match_conf_old *matches,
276172932Smarius		    int num_matches, struct pci_conf *match_buf);
277172932Smarius
27869953Smsmithstatic int
279172932Smariuspci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
280172932Smarius    struct pci_conf *match_buf)
281172932Smarius{
282172932Smarius	int i;
283172932Smarius
284172932Smarius	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
285172932Smarius		return(1);
286172932Smarius
287172932Smarius	for (i = 0; i < num_matches; i++) {
288172932Smarius		if (match_buf->pc_sel.pc_domain != 0)
289172932Smarius			continue;
290172932Smarius
291172932Smarius		/*
292172932Smarius		 * I'm not sure why someone would do this...but...
293172932Smarius		 */
294172932Smarius		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
295172932Smarius			continue;
296172932Smarius
297172932Smarius		/*
298172932Smarius		 * Look at each of the match flags.  If it's set, do the
299172932Smarius		 * comparison.  If the comparison fails, we don't have a
300172932Smarius		 * match, go on to the next item if there is one.
301172932Smarius		 */
302172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
303172932Smarius		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
304172932Smarius			continue;
305172932Smarius
306172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
307172932Smarius		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
308172932Smarius			continue;
309172932Smarius
310172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
311172932Smarius		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
312172932Smarius			continue;
313172932Smarius
314172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
315172932Smarius		 && (match_buf->pc_vendor != matches[i].pc_vendor))
316172932Smarius			continue;
317172932Smarius
318172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
319172932Smarius		 && (match_buf->pc_device != matches[i].pc_device))
320172932Smarius			continue;
321172932Smarius
322172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
323172932Smarius		 && (match_buf->pc_class != matches[i].pc_class))
324172932Smarius			continue;
325172932Smarius
326172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
327172932Smarius		 && (match_buf->pd_unit != matches[i].pd_unit))
328172932Smarius			continue;
329172932Smarius
330172932Smarius		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
331172932Smarius		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
332172932Smarius			     sizeof(match_buf->pd_name)) != 0))
333172932Smarius			continue;
334172932Smarius
335172932Smarius		return(0);
336172932Smarius	}
337172932Smarius
338172932Smarius	return(1);
339172932Smarius}
340172932Smarius
341240981Ssobomaxstatic int
342240981Ssobomaxpci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
343240981Ssobomax    struct pci_conf *match_buf)
344240981Ssobomax{
345240981Ssobomax       int i;
346240981Ssobomax
347240981Ssobomax       if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
348240981Ssobomax               return(1);
349240981Ssobomax
350240981Ssobomax       for (i = 0; i < num_matches; i++) {
351240981Ssobomax               if (match_buf->pc_sel.pc_domain != 0)
352240981Ssobomax                       continue;
353240981Ssobomax
354240981Ssobomax               /*
355240981Ssobomax                * I'm not sure why someone would do this...but...
356240981Ssobomax                */
357240981Ssobomax               if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
358240981Ssobomax                       continue;
359240981Ssobomax
360240981Ssobomax               /*
361240981Ssobomax                * Look at each of the match flags.  If it's set, do the
362240981Ssobomax                * comparison.  If the comparison fails, we don't have a
363240981Ssobomax                * match, go on to the next item if there is one.
364240981Ssobomax                */
365240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
366240981Ssobomax                && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
367240981Ssobomax                       continue;
368240981Ssobomax
369240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
370240981Ssobomax                && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
371240981Ssobomax                       continue;
372240981Ssobomax
373240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
374240981Ssobomax                && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
375240981Ssobomax                       continue;
376240981Ssobomax
377240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
378240981Ssobomax                && (match_buf->pc_vendor != matches[i].pc_vendor))
379240981Ssobomax                       continue;
380240981Ssobomax
381240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
382240981Ssobomax                && (match_buf->pc_device != matches[i].pc_device))
383240981Ssobomax                       continue;
384240981Ssobomax
385240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
386240981Ssobomax                && (match_buf->pc_class != matches[i].pc_class))
387240981Ssobomax                       continue;
388240981Ssobomax
389240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
390240981Ssobomax                && ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
391240981Ssobomax                       continue;
392240981Ssobomax
393240981Ssobomax               if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
394240981Ssobomax                && (strncmp(matches[i].pd_name, match_buf->pd_name,
395240981Ssobomax                            sizeof(match_buf->pd_name)) != 0))
396240981Ssobomax                       continue;
397240981Ssobomax
398240981Ssobomax               return(0);
399240981Ssobomax       }
400240981Ssobomax
401240981Ssobomax       return(1);
402240981Ssobomax}
403240981Ssobomax
404172932Smarius#endif
405172932Smarius
406172932Smariusstatic int
407130585Sphkpci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
40869953Smsmith{
409172932Smarius	device_t pcidev, brdev;
410172932Smarius	void *confdata;
411172932Smarius	const char *name;
412172932Smarius	struct devlist *devlist_head;
413172932Smarius	struct pci_conf_io *cio;
414172932Smarius	struct pci_devinfo *dinfo;
41569953Smsmith	struct pci_io *io;
416188018Sjhb	struct pci_bar_io *bio;
417172932Smarius	struct pci_match_conf *pattern_buf;
418220195Sjhb	struct pci_map *pm;
419172932Smarius	size_t confsz, iolen, pbufsz;
420172932Smarius	int error, ionum, i, num_patterns;
421172999Simp#ifdef PRE7_COMPAT
422240981Ssobomax#ifdef COMPAT_FREEBSD32
423240981Ssobomax       struct pci_conf_io32 *cio32;
424240981Ssobomax#endif
425172932Smarius	struct pci_conf_old conf_old;
426240981Ssobomax       struct pci_conf_old32 conf_old32;
427172932Smarius	struct pci_io iodata;
428172932Smarius	struct pci_io_old *io_old;
429172932Smarius	struct pci_match_conf_old *pattern_buf_old;
430240981Ssobomax       struct pci_match_conf_old32 *pattern_buf_old32;
43169953Smsmith
432240981Ssobomax       cio = NULL;
433240981Ssobomax       cio32 = NULL;
434172932Smarius	io_old = NULL;
435172932Smarius	pattern_buf_old = NULL;
436240981Ssobomax       pattern_buf_old32 = NULL;
437172932Smarius
438188018Sjhb	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
439188018Sjhb	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
440172932Smarius		return EPERM;
441172932Smarius#else
442188018Sjhb	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
44369953Smsmith		return EPERM;
444172932Smarius#endif
44569953Smsmith
44669953Smsmith	switch(cmd) {
447172999Simp#ifdef PRE7_COMPAT
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;
461240981Ssobomax               /* FALLTHROUGH */
462240981Ssobomax
463172932Smarius	case PCIOCGETCONF_OLD:
464172932Smarius		/* FALLTHROUGH */
465172932Smarius#endif
46669953Smsmith	case PCIOCGETCONF:
467240981Ssobomax               if (cio == NULL)
468240981Ssobomax                       cio = (struct pci_conf_io *)data;
46969953Smsmith
470172932Smarius		pattern_buf = NULL;
47169953Smsmith		num_patterns = 0;
47269953Smsmith		dinfo = NULL;
47369953Smsmith
474172932Smarius		cio->num_matches = 0;
475172932Smarius
47669953Smsmith		/*
47769953Smsmith		 * If the user specified an offset into the device list,
47869953Smsmith		 * but the list has changed since they last called this
47969953Smsmith		 * ioctl, tell them that the list has changed.  They will
48069953Smsmith		 * have to get the list from the beginning.
48169953Smsmith		 */
48269953Smsmith		if ((cio->offset != 0)
48369953Smsmith		 && (cio->generation != pci_generation)){
48469953Smsmith			cio->status = PCI_GETCONF_LIST_CHANGED;
48569953Smsmith			error = 0;
486240981Ssobomax                       goto getconfexit;
48769953Smsmith		}
48869953Smsmith
48969953Smsmith		/*
49069953Smsmith		 * Check to see whether the user has asked for an offset
49169953Smsmith		 * past the end of our list.
49269953Smsmith		 */
49369953Smsmith		if (cio->offset >= pci_numdevs) {
49469953Smsmith			cio->status = PCI_GETCONF_LAST_DEVICE;
49569953Smsmith			error = 0;
496240981Ssobomax                       goto getconfexit;
49769953Smsmith		}
49869953Smsmith
49969953Smsmith		/* get the head of the device queue */
50069953Smsmith		devlist_head = &pci_devq;
50169953Smsmith
50269953Smsmith		/*
50369953Smsmith		 * Determine how much room we have for pci_conf structures.
50469953Smsmith		 * Round the user's buffer size down to the nearest
50569953Smsmith		 * multiple of sizeof(struct pci_conf) in case the user
50669953Smsmith		 * didn't specify a multiple of that size.
50769953Smsmith		 */
508172999Simp#ifdef PRE7_COMPAT
509240981Ssobomax#ifdef COMPAT_FREEBSD32
510240981Ssobomax               if (cmd == PCIOCGETCONF_OLD32)
511240981Ssobomax                       confsz = sizeof(struct pci_conf_old32);
512240981Ssobomax               else
513240981Ssobomax#endif
514172932Smarius		if (cmd == PCIOCGETCONF_OLD)
515172932Smarius			confsz = sizeof(struct pci_conf_old);
516172932Smarius		else
517172932Smarius#endif
518172932Smarius			confsz = sizeof(struct pci_conf);
519172932Smarius		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
520172932Smarius		    pci_numdevs * confsz);
52169953Smsmith
52269953Smsmith		/*
52369953Smsmith		 * Since we know that iolen is a multiple of the size of
52469953Smsmith		 * the pciconf union, it's okay to do this.
52569953Smsmith		 */
526172932Smarius		ionum = iolen / confsz;
52769953Smsmith
52869953Smsmith		/*
52969953Smsmith		 * If this test is true, the user wants the pci_conf
53069953Smsmith		 * structures returned to match the supplied entries.
53169953Smsmith		 */
532116704Sjmg		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
53369953Smsmith		 && (cio->pat_buf_len > 0)) {
53469953Smsmith			/*
53569953Smsmith			 * pat_buf_len needs to be:
53669953Smsmith			 * num_patterns * sizeof(struct pci_match_conf)
53769953Smsmith			 * While it is certainly possible the user just
53869953Smsmith			 * allocated a large buffer, but set the number of
53969953Smsmith			 * matches correctly, it is far more likely that
54069953Smsmith			 * their kernel doesn't match the userland utility
54169953Smsmith			 * they're using.  It's also possible that the user
54269953Smsmith			 * forgot to initialize some variables.  Yes, this
54369953Smsmith			 * may be overly picky, but I hazard to guess that
54469953Smsmith			 * it's far more likely to just catch folks that
54569953Smsmith			 * updated their kernel but not their userland.
54669953Smsmith			 */
547172999Simp#ifdef PRE7_COMPAT
548240981Ssobomax#ifdef COMPAT_FREEBSD32
549240981Ssobomax                       if (cmd == PCIOCGETCONF_OLD32)
550240981Ssobomax                               pbufsz = sizeof(struct pci_match_conf_old32);
551240981Ssobomax                       else
552240981Ssobomax#endif
553172932Smarius			if (cmd == PCIOCGETCONF_OLD)
554172932Smarius				pbufsz = sizeof(struct pci_match_conf_old);
555172932Smarius			else
556172932Smarius#endif
557172932Smarius				pbufsz = sizeof(struct pci_match_conf);
558172932Smarius			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
559172932Smarius				/* The user made a mistake, return an error. */
56069953Smsmith				cio->status = PCI_GETCONF_ERROR;
56169953Smsmith				error = EINVAL;
562240981Ssobomax                               goto getconfexit;
56369953Smsmith			}
56469953Smsmith
56569953Smsmith			/*
56669953Smsmith			 * Allocate a buffer to hold the patterns.
56769953Smsmith			 */
568172999Simp#ifdef PRE7_COMPAT
569240981Ssobomax                       if (cmd == PCIOCGETCONF_OLD32) {
570240981Ssobomax                               pattern_buf_old32 = malloc(cio->pat_buf_len,
571240981Ssobomax                                   M_TEMP, M_WAITOK);
572240981Ssobomax                               error = copyin(cio->patterns,
573240981Ssobomax                                   pattern_buf_old32, cio->pat_buf_len);
574240981Ssobomax                       } else
575172932Smarius			if (cmd == PCIOCGETCONF_OLD) {
576172932Smarius				pattern_buf_old = malloc(cio->pat_buf_len,
577172932Smarius				    M_TEMP, M_WAITOK);
578172932Smarius				error = copyin(cio->patterns,
579172932Smarius				    pattern_buf_old, cio->pat_buf_len);
580172932Smarius			} else
581172932Smarius#endif
582172932Smarius			{
583172932Smarius				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
584172932Smarius				    M_WAITOK);
585172932Smarius				error = copyin(cio->patterns, pattern_buf,
586172932Smarius				    cio->pat_buf_len);
587172932Smarius			}
588116702Sjmg			if (error != 0) {
589116702Sjmg				error = EINVAL;
590116702Sjmg				goto getconfexit;
591116702Sjmg			}
59269953Smsmith			num_patterns = cio->num_patterns;
59369953Smsmith		} else if ((cio->num_patterns > 0)
59469953Smsmith			|| (cio->pat_buf_len > 0)) {
59569953Smsmith			/*
59669953Smsmith			 * The user made a mistake, spit out an error.
59769953Smsmith			 */
59869953Smsmith			cio->status = PCI_GETCONF_ERROR;
59969953Smsmith			error = EINVAL;
600240981Ssobomax                       goto getconfexit;
601172932Smarius		}
60269953Smsmith
60369953Smsmith		/*
60469953Smsmith		 * Go through the list of devices and copy out the devices
60569953Smsmith		 * that match the user's criteria.
60669953Smsmith		 */
60769953Smsmith		for (cio->num_matches = 0, error = 0, i = 0,
60869953Smsmith		     dinfo = STAILQ_FIRST(devlist_head);
60969953Smsmith		     (dinfo != NULL) && (cio->num_matches < ionum)
610116702Sjmg		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
61169953Smsmith		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
61269953Smsmith
61369953Smsmith			if (i < cio->offset)
61469953Smsmith				continue;
61569953Smsmith
61669953Smsmith			/* Populate pd_name and pd_unit */
61769953Smsmith			name = NULL;
618175368Sjhb			if (dinfo->cfg.dev)
61969953Smsmith				name = device_get_name(dinfo->cfg.dev);
62069953Smsmith			if (name) {
62169953Smsmith				strncpy(dinfo->conf.pd_name, name,
62269953Smsmith					sizeof(dinfo->conf.pd_name));
62369953Smsmith				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
62469953Smsmith				dinfo->conf.pd_unit =
62569953Smsmith					device_get_unit(dinfo->cfg.dev);
626175368Sjhb			} else {
627175368Sjhb				dinfo->conf.pd_name[0] = '\0';
628175368Sjhb				dinfo->conf.pd_unit = 0;
62969953Smsmith			}
63069953Smsmith
631172999Simp#ifdef PRE7_COMPAT
632240981Ssobomax                       if ((cmd == PCIOCGETCONF_OLD32 &&
633240981Ssobomax                           (pattern_buf_old32 == NULL ||
634240981Ssobomax                           pci_conf_match_old32(pattern_buf_old32,
635240981Ssobomax                           num_patterns, &dinfo->conf) == 0)) ||
636240981Ssobomax                           (cmd == PCIOCGETCONF_OLD &&
637172932Smarius			    (pattern_buf_old == NULL ||
638172932Smarius			    pci_conf_match_old(pattern_buf_old, num_patterns,
639172932Smarius			    &dinfo->conf) == 0)) ||
640172932Smarius			    (cmd == PCIOCGETCONF &&
641172932Smarius			    (pattern_buf == NULL ||
642172932Smarius			    pci_conf_match(pattern_buf, num_patterns,
643172932Smarius			    &dinfo->conf) == 0))) {
644172932Smarius#else
645172932Smarius			if (pattern_buf == NULL ||
646172932Smarius			    pci_conf_match(pattern_buf, num_patterns,
647172932Smarius			    &dinfo->conf) == 0) {
648172932Smarius#endif
64969953Smsmith				/*
65069953Smsmith				 * If we've filled up the user's buffer,
65169953Smsmith				 * break out at this point.  Since we've
65269953Smsmith				 * got a match here, we'll pick right back
65369953Smsmith				 * up at the matching entry.  We can also
65469953Smsmith				 * tell the user that there are more matches
65569953Smsmith				 * left.
65669953Smsmith				 */
65769953Smsmith				if (cio->num_matches >= ionum)
65869953Smsmith					break;
65969953Smsmith
660172999Simp#ifdef PRE7_COMPAT
661240981Ssobomax                               if (cmd == PCIOCGETCONF_OLD32) {
662240981Ssobomax                                       conf_old32.pc_sel.pc_bus =
663240981Ssobomax                                           dinfo->conf.pc_sel.pc_bus;
664240981Ssobomax                                       conf_old32.pc_sel.pc_dev =
665240981Ssobomax                                           dinfo->conf.pc_sel.pc_dev;
666240981Ssobomax                                       conf_old32.pc_sel.pc_func =
667240981Ssobomax                                           dinfo->conf.pc_sel.pc_func;
668240981Ssobomax                                       conf_old32.pc_hdr = dinfo->conf.pc_hdr;
669240981Ssobomax                                       conf_old32.pc_subvendor =
670240981Ssobomax                                           dinfo->conf.pc_subvendor;
671240981Ssobomax                                       conf_old32.pc_subdevice =
672240981Ssobomax                                           dinfo->conf.pc_subdevice;
673240981Ssobomax                                       conf_old32.pc_vendor =
674240981Ssobomax                                           dinfo->conf.pc_vendor;
675240981Ssobomax                                       conf_old32.pc_device =
676240981Ssobomax                                           dinfo->conf.pc_device;
677240981Ssobomax                                       conf_old32.pc_class =
678240981Ssobomax                                           dinfo->conf.pc_class;
679240981Ssobomax                                       conf_old32.pc_subclass =
680240981Ssobomax                                           dinfo->conf.pc_subclass;
681240981Ssobomax                                       conf_old32.pc_progif =
682240981Ssobomax                                           dinfo->conf.pc_progif;
683240981Ssobomax                                       conf_old32.pc_revid =
684240981Ssobomax                                           dinfo->conf.pc_revid;
685240981Ssobomax                                       strncpy(conf_old32.pd_name,
686240981Ssobomax                                           dinfo->conf.pd_name,
687240981Ssobomax                                           sizeof(conf_old32.pd_name));
688240981Ssobomax                                       conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
689240981Ssobomax                                       conf_old32.pd_unit =
690240981Ssobomax                                           (u_int32_t)dinfo->conf.pd_unit;
691240981Ssobomax                                       confdata = &conf_old32;
692240981Ssobomax                               } else
693172932Smarius				if (cmd == PCIOCGETCONF_OLD) {
694172932Smarius					conf_old.pc_sel.pc_bus =
695172932Smarius					    dinfo->conf.pc_sel.pc_bus;
696172932Smarius					conf_old.pc_sel.pc_dev =
697172932Smarius					    dinfo->conf.pc_sel.pc_dev;
698172932Smarius					conf_old.pc_sel.pc_func =
699172932Smarius					    dinfo->conf.pc_sel.pc_func;
700172932Smarius					conf_old.pc_hdr = dinfo->conf.pc_hdr;
701172932Smarius					conf_old.pc_subvendor =
702172932Smarius					    dinfo->conf.pc_subvendor;
703172932Smarius					conf_old.pc_subdevice =
704172932Smarius					    dinfo->conf.pc_subdevice;
705172932Smarius					conf_old.pc_vendor =
706172932Smarius					    dinfo->conf.pc_vendor;
707172932Smarius					conf_old.pc_device =
708172932Smarius					    dinfo->conf.pc_device;
709172932Smarius					conf_old.pc_class =
710172932Smarius					    dinfo->conf.pc_class;
711172932Smarius					conf_old.pc_subclass =
712172932Smarius					    dinfo->conf.pc_subclass;
713172932Smarius					conf_old.pc_progif =
714172932Smarius					    dinfo->conf.pc_progif;
715172932Smarius					conf_old.pc_revid =
716172932Smarius					    dinfo->conf.pc_revid;
717174932Smarius					strncpy(conf_old.pd_name,
718174932Smarius					    dinfo->conf.pd_name,
719174932Smarius					    sizeof(conf_old.pd_name));
720174932Smarius					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
721174932Smarius					conf_old.pd_unit =
722174932Smarius					    dinfo->conf.pd_unit;
723172932Smarius					confdata = &conf_old;
724172932Smarius				} else
725172932Smarius#endif
726172932Smarius					confdata = &dinfo->conf;
727172932Smarius				/* Only if we can copy it out do we count it. */
728172932Smarius				if (!(error = copyout(confdata,
729174932Smarius				    (caddr_t)cio->matches +
730174932Smarius				    confsz * cio->num_matches, confsz)))
731116702Sjmg					cio->num_matches++;
73269953Smsmith			}
73369953Smsmith		}
73469953Smsmith
73569953Smsmith		/*
73669953Smsmith		 * Set the pointer into the list, so if the user is getting
73769953Smsmith		 * n records at a time, where n < pci_numdevs,
73869953Smsmith		 */
73969953Smsmith		cio->offset = i;
74069953Smsmith
74169953Smsmith		/*
74269953Smsmith		 * Set the generation, the user will need this if they make
74369953Smsmith		 * another ioctl call with offset != 0.
74469953Smsmith		 */
74569953Smsmith		cio->generation = pci_generation;
746172932Smarius
74769953Smsmith		/*
74869953Smsmith		 * If this is the last device, inform the user so he won't
74969953Smsmith		 * bother asking for more devices.  If dinfo isn't NULL, we
75069953Smsmith		 * know that there are more matches in the list because of
75169953Smsmith		 * the way the traversal is done.
75269953Smsmith		 */
75369953Smsmith		if (dinfo == NULL)
75469953Smsmith			cio->status = PCI_GETCONF_LAST_DEVICE;
75569953Smsmith		else
75669953Smsmith			cio->status = PCI_GETCONF_MORE_DEVS;
75769953Smsmith
758116702Sjmggetconfexit:
759240981Ssobomax#ifdef COMPAT_FREEBSD32
760240981Ssobomax               if (cmd == PCIOCGETCONF_OLD32) {
761240981Ssobomax                       cio32->status = cio->status;
762240981Ssobomax                       cio32->generation = cio->generation;
763240981Ssobomax                       cio32->offset = cio->offset;
764240981Ssobomax                       cio32->num_matches = cio->num_matches;
765240981Ssobomax                       if (cio != NULL)
766240981Ssobomax                               free(cio, M_TEMP);
767240981Ssobomax               }
768240981Ssobomax#endif
769240981Ssobomax
77069953Smsmith		if (pattern_buf != NULL)
77169953Smsmith			free(pattern_buf, M_TEMP);
772172999Simp#ifdef PRE7_COMPAT
773240981Ssobomax               if (pattern_buf_old32 != NULL)
774240981Ssobomax                       free(pattern_buf_old32, M_TEMP);
775172932Smarius		if (pattern_buf_old != NULL)
776172932Smarius			free(pattern_buf_old, M_TEMP);
777172932Smarius#endif
77869953Smsmith
77969953Smsmith		break;
780121013Sse
781172999Simp#ifdef PRE7_COMPAT
782172932Smarius	case PCIOCREAD_OLD:
783172932Smarius	case PCIOCWRITE_OLD:
784172932Smarius		io_old = (struct pci_io_old *)data;
785172932Smarius		iodata.pi_sel.pc_domain = 0;
786172932Smarius		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
787172932Smarius		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
788172932Smarius		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
789172932Smarius		iodata.pi_reg = io_old->pi_reg;
790172932Smarius		iodata.pi_width = io_old->pi_width;
791172932Smarius		iodata.pi_data = io_old->pi_data;
792172932Smarius		data = (caddr_t)&iodata;
793172932Smarius		/* FALLTHROUGH */
794172932Smarius#endif
79569953Smsmith	case PCIOCREAD:
796121013Sse	case PCIOCWRITE:
79769953Smsmith		io = (struct pci_io *)data;
79869953Smsmith		switch(io->pi_width) {
79969953Smsmith		case 4:
80069953Smsmith		case 2:
80169953Smsmith		case 1:
802197099Savg			/* Make sure register is not negative and aligned. */
803172932Smarius			if (io->pi_reg < 0 ||
804172932Smarius			    io->pi_reg & (io->pi_width - 1)) {
805163055Sru				error = EINVAL;
806163055Sru				break;
807163055Sru			}
80869953Smsmith			/*
80969953Smsmith			 * Assume that the user-level bus number is
810145022Sbms			 * in fact the physical PCI bus number.
811145022Sbms			 * Look up the grandparent, i.e. the bridge device,
812145022Sbms			 * so that we can issue configuration space cycles.
81369953Smsmith			 */
814172394Smarius			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
815172394Smarius			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
816172394Smarius			    io->pi_sel.pc_func);
817145022Sbms			if (pcidev) {
818172932Smarius				brdev = device_get_parent(
819172932Smarius				    device_get_parent(pcidev));
820145022Sbms
821172999Simp#ifdef PRE7_COMPAT
822172932Smarius				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
823172932Smarius#else
824121013Sse				if (cmd == PCIOCWRITE)
825172932Smarius#endif
826145022Sbms					PCIB_WRITE_CONFIG(brdev,
827145022Sbms							  io->pi_sel.pc_bus,
828121013Sse							  io->pi_sel.pc_dev,
829121013Sse							  io->pi_sel.pc_func,
830121013Sse							  io->pi_reg,
831121013Sse							  io->pi_data,
832121013Sse							  io->pi_width);
833172999Simp#ifdef PRE7_COMPAT
834172932Smarius				else if (cmd == PCIOCREAD_OLD)
835172932Smarius					io_old->pi_data =
836172932Smarius						PCIB_READ_CONFIG(brdev,
837172932Smarius							  io->pi_sel.pc_bus,
838172932Smarius							  io->pi_sel.pc_dev,
839172932Smarius							  io->pi_sel.pc_func,
840172932Smarius							  io->pi_reg,
841172932Smarius							  io->pi_width);
842172932Smarius#endif
843121013Sse				else
844121013Sse					io->pi_data =
845145022Sbms						PCIB_READ_CONFIG(brdev,
846145022Sbms							  io->pi_sel.pc_bus,
847121013Sse							  io->pi_sel.pc_dev,
848121013Sse							  io->pi_sel.pc_func,
849121013Sse							  io->pi_reg,
850121013Sse							  io->pi_width);
85169953Smsmith				error = 0;
85269953Smsmith			} else {
853149478Sps#ifdef COMPAT_FREEBSD4
854172932Smarius				if (cmd == PCIOCREAD_OLD) {
855172932Smarius					io_old->pi_data = -1;
856149478Sps					error = 0;
857149478Sps				} else
858149478Sps#endif
859149478Sps					error = ENODEV;
86069953Smsmith			}
86169953Smsmith			break;
86269953Smsmith		default:
863116702Sjmg			error = EINVAL;
86469953Smsmith			break;
86569953Smsmith		}
86669953Smsmith		break;
86769953Smsmith
868188018Sjhb	case PCIOCGETBAR:
869188018Sjhb		bio = (struct pci_bar_io *)data;
870188018Sjhb
871188018Sjhb		/*
872188018Sjhb		 * Assume that the user-level bus number is
873188018Sjhb		 * in fact the physical PCI bus number.
874188018Sjhb		 */
875188018Sjhb		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
876188018Sjhb		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
877188018Sjhb		    bio->pbi_sel.pc_func);
878188018Sjhb		if (pcidev == NULL) {
879188018Sjhb			error = ENODEV;
880188018Sjhb			break;
881188018Sjhb		}
882220195Sjhb		pm = pci_find_bar(pcidev, bio->pbi_reg);
883220195Sjhb		if (pm == NULL) {
884188018Sjhb			error = EINVAL;
885188018Sjhb			break;
886188018Sjhb		}
887220195Sjhb		bio->pbi_base = pm->pm_value;
888220195Sjhb		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
889220195Sjhb		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
890188018Sjhb		error = 0;
891188018Sjhb		break;
892210597Sneel	case PCIOCATTACHED:
893210597Sneel		error = 0;
894210597Sneel		io = (struct pci_io *)data;
895210597Sneel		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
896210597Sneel				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
897210597Sneel		if (pcidev != NULL)
898210597Sneel			io->pi_data = device_is_attached(pcidev);
899210597Sneel		else
900210597Sneel			error = ENODEV;
901210597Sneel		break;
90269953Smsmith	default:
90369953Smsmith		error = ENOTTY;
90469953Smsmith		break;
90569953Smsmith	}
90669953Smsmith
90769953Smsmith	return (error);
90869953Smsmith}
909