pci_user.c revision 210597
155714Skris/*-
2280297Sjkim * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3280297Sjkim * All rights reserved.
4280297Sjkim *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
7238405Sjkim * are met:
855714Skris * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice unmodified, this list of conditions, and the following
1055714Skris *    disclaimer.
1155714Skris * 2. Redistributions in binary form must reproduce the above copyright
1255714Skris *    notice, this list of conditions and the following disclaimer in the
1355714Skris *    documentation and/or other materials provided with the distribution.
14280297Sjkim *
1555714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1655714Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1755714Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1855714Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1955714Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2055714Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2155714Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2255714Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2355714Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2455714Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2555714Skris */
2655714Skris
2755714Skris#include <sys/cdefs.h>
2855714Skris__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 210597 2010-07-29 06:27:41Z neel $");
2955714Skris
3055714Skris#include "opt_bus.h"	/* XXX trim includes */
3155714Skris#include "opt_compat.h"
3255714Skris
3355714Skris#include <sys/param.h>
3455714Skris#include <sys/systm.h>
3555714Skris#include <sys/malloc.h>
3655714Skris#include <sys/module.h>
3755714Skris#include <sys/linker.h>
3855714Skris#include <sys/fcntl.h>
3955714Skris#include <sys/conf.h>
4055714Skris#include <sys/kernel.h>
4155714Skris#include <sys/proc.h>
4255714Skris#include <sys/queue.h>
4355714Skris#include <sys/types.h>
4455714Skris
4555714Skris#include <vm/vm.h>
4655714Skris#include <vm/pmap.h>
4755714Skris#include <vm/vm_extern.h>
4855714Skris
4955714Skris#include <sys/bus.h>
5055714Skris#include <machine/bus.h>
5155714Skris#include <sys/rman.h>
5255714Skris#include <machine/resource.h>
5355714Skris
5455714Skris#include <sys/pciio.h>
5555714Skris#include <dev/pci/pcireg.h>
5655714Skris#include <dev/pci/pcivar.h>
5755714Skris
5855714Skris#include "pcib_if.h"
5955714Skris#include "pci_if.h"
6055714Skris
6155714Skris/*
6255714Skris * This is the user interface to PCI configuration space.
6355714Skris */
6455714Skris
65238405Sjkimstatic d_open_t 	pci_open;
6655714Skrisstatic d_close_t	pci_close;
6755714Skrisstatic int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
6855714Skris			       struct pci_conf *match_buf);
69160814Ssimonstatic d_ioctl_t	pci_ioctl;
7055714Skris
71280297Sjkimstruct cdevsw pcicdev = {
72280297Sjkim	.d_version =	D_VERSION,
73280297Sjkim	.d_flags =	D_NEEDGIANT,
7455714Skris	.d_open =	pci_open,
75280297Sjkim	.d_close =	pci_close,
76280297Sjkim	.d_ioctl =	pci_ioctl,
77238405Sjkim	.d_name =	"pci",
78280297Sjkim};
79280297Sjkim
80280297Sjkimstatic int
81280297Sjkimpci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
8259191Skris{
83280297Sjkim	int error;
84280297Sjkim
85280297Sjkim	if (oflags & FWRITE) {
86280297Sjkim		error = securelevel_gt(td->td_ucred, 0);
87280297Sjkim		if (error)
88280297Sjkim			return (error);
89160814Ssimon	}
90280297Sjkim
91280297Sjkim	return (0);
92280297Sjkim}
93280297Sjkim
94280297Sjkimstatic int
95280297Sjkimpci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
96280297Sjkim{
97280297Sjkim	return 0;
98280297Sjkim}
99160814Ssimon
100280297Sjkim/*
101160814Ssimon * Match a single pci_conf structure against an array of pci_match_conf
102280297Sjkim * structures.  The first argument, 'matches', is an array of num_matches
103280297Sjkim * pci_match_conf structures.  match_buf is a pointer to the pci_conf
104280297Sjkim * structure that will be compared to every entry in the matches array.
10555714Skris * This function returns 1 on failure, 0 on success.
10655714Skris */
10759191Skrisstatic int
10859191Skrispci_conf_match(struct pci_match_conf *matches, int num_matches,
109280297Sjkim	       struct pci_conf *match_buf)
11059191Skris{
11159191Skris	int i;
11255714Skris
11355714Skris	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
11459191Skris		return(1);
11555714Skris
116280297Sjkim	for (i = 0; i < num_matches; i++) {
11759191Skris		/*
118280297Sjkim		 * I'm not sure why someone would do this...but...
119280297Sjkim		 */
120280297Sjkim		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
121280297Sjkim			continue;
122280297Sjkim
12355714Skris		/*
124280297Sjkim		 * Look at each of the match flags.  If it's set, do the
125280297Sjkim		 * comparison.  If the comparison fails, we don't have a
126280297Sjkim		 * match, go on to the next item if there is one.
127280297Sjkim		 */
128280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
129280297Sjkim		 && (match_buf->pc_sel.pc_domain !=
130280297Sjkim		 matches[i].pc_sel.pc_domain))
131280297Sjkim			continue;
132280297Sjkim
133280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
134280297Sjkim		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
135280297Sjkim			continue;
136280297Sjkim
137280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
138280297Sjkim		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
139280297Sjkim			continue;
140280297Sjkim
141280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
142280297Sjkim		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
143280297Sjkim			continue;
144280297Sjkim
145280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
14655714Skris		 && (match_buf->pc_vendor != matches[i].pc_vendor))
14755714Skris			continue;
14855714Skris
14955714Skris		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
150280297Sjkim		 && (match_buf->pc_device != matches[i].pc_device))
15155714Skris			continue;
152280297Sjkim
153280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
154280297Sjkim		 && (match_buf->pc_class != matches[i].pc_class))
155280297Sjkim			continue;
15655714Skris
157280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
158280297Sjkim		 && (match_buf->pd_unit != matches[i].pd_unit))
159280297Sjkim			continue;
160280297Sjkim
161280297Sjkim		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
16255714Skris		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
163280297Sjkim			     sizeof(match_buf->pd_name)) != 0))
164280297Sjkim			continue;
165280297Sjkim
166280297Sjkim		return(0);
16755714Skris	}
16855714Skris
169160814Ssimon	return(1);
170160814Ssimon}
171160814Ssimon
172160814Ssimon#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
173280297Sjkim    defined(COMPAT_FREEBSD6)
174160814Ssimon#define PRE7_COMPAT
175160814Ssimon
176280297Sjkimtypedef enum {
177160814Ssimon	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
178280297Sjkim	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
179160814Ssimon	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
180160814Ssimon	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
181160814Ssimon	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
182280297Sjkim	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
183160814Ssimon	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
184280297Sjkim	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
185160814Ssimon	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
186160814Ssimon} pci_getconf_flags_old;
187160814Ssimon
188160814Ssimonstruct pcisel_old {
189280297Sjkim	u_int8_t	pc_bus;		/* bus number */
190160814Ssimon	u_int8_t	pc_dev;		/* device on this bus */
191160814Ssimon	u_int8_t	pc_func;	/* function on this device */
192160814Ssimon};
193160814Ssimon
194280297Sjkimstruct pci_conf_old {
195160814Ssimon	struct pcisel_old pc_sel;	/* bus+slot+function */
196160814Ssimon	u_int8_t	pc_hdr;		/* PCI header type */
197160814Ssimon	u_int16_t	pc_subvendor;	/* card vendor ID */
198160814Ssimon	u_int16_t	pc_subdevice;	/* card device ID, assigned by
199280297Sjkim					   card vendor */
200280297Sjkim	u_int16_t	pc_vendor;	/* chip vendor ID */
201280297Sjkim	u_int16_t	pc_device;	/* chip device ID, assigned by
202160814Ssimon					   chip vendor */
203160814Ssimon	u_int8_t	pc_class;	/* chip PCI class */
204160814Ssimon	u_int8_t	pc_subclass;	/* chip PCI subclass */
205280297Sjkim	u_int8_t	pc_progif;	/* chip PCI programming interface */
206280297Sjkim	u_int8_t	pc_revid;	/* chip revision ID */
207160814Ssimon	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
208280297Sjkim	u_long		pd_unit;	/* device unit number */
209280297Sjkim};
210280297Sjkim
211160814Ssimonstruct pci_match_conf_old {
212160814Ssimon	struct pcisel_old	pc_sel;		/* bus+slot+function */
213160814Ssimon	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
214280297Sjkim	u_long			pd_unit;	/* Unit number */
215280297Sjkim	u_int16_t		pc_vendor;	/* PCI Vendor ID */
216160814Ssimon	u_int16_t		pc_device;	/* PCI Device ID */
217280297Sjkim	u_int8_t		pc_class;	/* PCI class */
218280297Sjkim	pci_getconf_flags_old	flags;		/* Matching expression */
219280297Sjkim};
220160814Ssimon
221160814Ssimonstruct pci_io_old {
222160814Ssimon	struct pcisel_old pi_sel;	/* device to operate on */
223280297Sjkim	int		pi_reg;		/* configuration register to examine */
224280297Sjkim	int		pi_width;	/* width (in bytes) of read or write */
225160814Ssimon	u_int32_t	pi_data;	/* data to write or result of read */
226280297Sjkim};
227280297Sjkim
228280297Sjkim#define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
229160814Ssimon#define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
230#define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
231
232static int	pci_conf_match_old(struct pci_match_conf_old *matches,
233		    int num_matches, struct pci_conf *match_buf);
234
235static int
236pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
237    struct pci_conf *match_buf)
238{
239	int i;
240
241	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
242		return(1);
243
244	for (i = 0; i < num_matches; i++) {
245		if (match_buf->pc_sel.pc_domain != 0)
246			continue;
247
248		/*
249		 * I'm not sure why someone would do this...but...
250		 */
251		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
252			continue;
253
254		/*
255		 * Look at each of the match flags.  If it's set, do the
256		 * comparison.  If the comparison fails, we don't have a
257		 * match, go on to the next item if there is one.
258		 */
259		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
260		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
261			continue;
262
263		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
264		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
265			continue;
266
267		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
268		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
269			continue;
270
271		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
272		 && (match_buf->pc_vendor != matches[i].pc_vendor))
273			continue;
274
275		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
276		 && (match_buf->pc_device != matches[i].pc_device))
277			continue;
278
279		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
280		 && (match_buf->pc_class != matches[i].pc_class))
281			continue;
282
283		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
284		 && (match_buf->pd_unit != matches[i].pd_unit))
285			continue;
286
287		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
288		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
289			     sizeof(match_buf->pd_name)) != 0))
290			continue;
291
292		return(0);
293	}
294
295	return(1);
296}
297
298#endif
299
300static int
301pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
302{
303	device_t pcidev, brdev;
304	void *confdata;
305	const char *name;
306	struct devlist *devlist_head;
307	struct pci_conf_io *cio;
308	struct pci_devinfo *dinfo;
309	struct pci_io *io;
310	struct pci_bar_io *bio;
311	struct pci_match_conf *pattern_buf;
312	struct resource_list_entry *rle;
313	uint32_t value;
314	size_t confsz, iolen, pbufsz;
315	int error, ionum, i, num_patterns;
316#ifdef PRE7_COMPAT
317	struct pci_conf_old conf_old;
318	struct pci_io iodata;
319	struct pci_io_old *io_old;
320	struct pci_match_conf_old *pattern_buf_old;
321
322	io_old = NULL;
323	pattern_buf_old = NULL;
324
325	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
326	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
327		return EPERM;
328#else
329	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
330		return EPERM;
331#endif
332
333	switch(cmd) {
334#ifdef PRE7_COMPAT
335	case PCIOCGETCONF_OLD:
336		/* FALLTHROUGH */
337#endif
338	case PCIOCGETCONF:
339		cio = (struct pci_conf_io *)data;
340
341		pattern_buf = NULL;
342		num_patterns = 0;
343		dinfo = NULL;
344
345		cio->num_matches = 0;
346
347		/*
348		 * If the user specified an offset into the device list,
349		 * but the list has changed since they last called this
350		 * ioctl, tell them that the list has changed.  They will
351		 * have to get the list from the beginning.
352		 */
353		if ((cio->offset != 0)
354		 && (cio->generation != pci_generation)){
355			cio->status = PCI_GETCONF_LIST_CHANGED;
356			error = 0;
357			break;
358		}
359
360		/*
361		 * Check to see whether the user has asked for an offset
362		 * past the end of our list.
363		 */
364		if (cio->offset >= pci_numdevs) {
365			cio->status = PCI_GETCONF_LAST_DEVICE;
366			error = 0;
367			break;
368		}
369
370		/* get the head of the device queue */
371		devlist_head = &pci_devq;
372
373		/*
374		 * Determine how much room we have for pci_conf structures.
375		 * Round the user's buffer size down to the nearest
376		 * multiple of sizeof(struct pci_conf) in case the user
377		 * didn't specify a multiple of that size.
378		 */
379#ifdef PRE7_COMPAT
380		if (cmd == PCIOCGETCONF_OLD)
381			confsz = sizeof(struct pci_conf_old);
382		else
383#endif
384			confsz = sizeof(struct pci_conf);
385		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
386		    pci_numdevs * confsz);
387
388		/*
389		 * Since we know that iolen is a multiple of the size of
390		 * the pciconf union, it's okay to do this.
391		 */
392		ionum = iolen / confsz;
393
394		/*
395		 * If this test is true, the user wants the pci_conf
396		 * structures returned to match the supplied entries.
397		 */
398		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
399		 && (cio->pat_buf_len > 0)) {
400			/*
401			 * pat_buf_len needs to be:
402			 * num_patterns * sizeof(struct pci_match_conf)
403			 * While it is certainly possible the user just
404			 * allocated a large buffer, but set the number of
405			 * matches correctly, it is far more likely that
406			 * their kernel doesn't match the userland utility
407			 * they're using.  It's also possible that the user
408			 * forgot to initialize some variables.  Yes, this
409			 * may be overly picky, but I hazard to guess that
410			 * it's far more likely to just catch folks that
411			 * updated their kernel but not their userland.
412			 */
413#ifdef PRE7_COMPAT
414			if (cmd == PCIOCGETCONF_OLD)
415				pbufsz = sizeof(struct pci_match_conf_old);
416			else
417#endif
418				pbufsz = sizeof(struct pci_match_conf);
419			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
420				/* The user made a mistake, return an error. */
421				cio->status = PCI_GETCONF_ERROR;
422				error = EINVAL;
423				break;
424			}
425
426			/*
427			 * Allocate a buffer to hold the patterns.
428			 */
429#ifdef PRE7_COMPAT
430			if (cmd == PCIOCGETCONF_OLD) {
431				pattern_buf_old = malloc(cio->pat_buf_len,
432				    M_TEMP, M_WAITOK);
433				error = copyin(cio->patterns,
434				    pattern_buf_old, cio->pat_buf_len);
435			} else
436#endif
437			{
438				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
439				    M_WAITOK);
440				error = copyin(cio->patterns, pattern_buf,
441				    cio->pat_buf_len);
442			}
443			if (error != 0) {
444				error = EINVAL;
445				goto getconfexit;
446			}
447			num_patterns = cio->num_patterns;
448		} else if ((cio->num_patterns > 0)
449			|| (cio->pat_buf_len > 0)) {
450			/*
451			 * The user made a mistake, spit out an error.
452			 */
453			cio->status = PCI_GETCONF_ERROR;
454			error = EINVAL;
455			break;
456		}
457
458		/*
459		 * Go through the list of devices and copy out the devices
460		 * that match the user's criteria.
461		 */
462		for (cio->num_matches = 0, error = 0, i = 0,
463		     dinfo = STAILQ_FIRST(devlist_head);
464		     (dinfo != NULL) && (cio->num_matches < ionum)
465		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
466		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
467
468			if (i < cio->offset)
469				continue;
470
471			/* Populate pd_name and pd_unit */
472			name = NULL;
473			if (dinfo->cfg.dev)
474				name = device_get_name(dinfo->cfg.dev);
475			if (name) {
476				strncpy(dinfo->conf.pd_name, name,
477					sizeof(dinfo->conf.pd_name));
478				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
479				dinfo->conf.pd_unit =
480					device_get_unit(dinfo->cfg.dev);
481			} else {
482				dinfo->conf.pd_name[0] = '\0';
483				dinfo->conf.pd_unit = 0;
484			}
485
486#ifdef PRE7_COMPAT
487			if ((cmd == PCIOCGETCONF_OLD &&
488			    (pattern_buf_old == NULL ||
489			    pci_conf_match_old(pattern_buf_old, num_patterns,
490			    &dinfo->conf) == 0)) ||
491			    (cmd == PCIOCGETCONF &&
492			    (pattern_buf == NULL ||
493			    pci_conf_match(pattern_buf, num_patterns,
494			    &dinfo->conf) == 0))) {
495#else
496			if (pattern_buf == NULL ||
497			    pci_conf_match(pattern_buf, num_patterns,
498			    &dinfo->conf) == 0) {
499#endif
500				/*
501				 * If we've filled up the user's buffer,
502				 * break out at this point.  Since we've
503				 * got a match here, we'll pick right back
504				 * up at the matching entry.  We can also
505				 * tell the user that there are more matches
506				 * left.
507				 */
508				if (cio->num_matches >= ionum)
509					break;
510
511#ifdef PRE7_COMPAT
512				if (cmd == PCIOCGETCONF_OLD) {
513					conf_old.pc_sel.pc_bus =
514					    dinfo->conf.pc_sel.pc_bus;
515					conf_old.pc_sel.pc_dev =
516					    dinfo->conf.pc_sel.pc_dev;
517					conf_old.pc_sel.pc_func =
518					    dinfo->conf.pc_sel.pc_func;
519					conf_old.pc_hdr = dinfo->conf.pc_hdr;
520					conf_old.pc_subvendor =
521					    dinfo->conf.pc_subvendor;
522					conf_old.pc_subdevice =
523					    dinfo->conf.pc_subdevice;
524					conf_old.pc_vendor =
525					    dinfo->conf.pc_vendor;
526					conf_old.pc_device =
527					    dinfo->conf.pc_device;
528					conf_old.pc_class =
529					    dinfo->conf.pc_class;
530					conf_old.pc_subclass =
531					    dinfo->conf.pc_subclass;
532					conf_old.pc_progif =
533					    dinfo->conf.pc_progif;
534					conf_old.pc_revid =
535					    dinfo->conf.pc_revid;
536					strncpy(conf_old.pd_name,
537					    dinfo->conf.pd_name,
538					    sizeof(conf_old.pd_name));
539					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
540					conf_old.pd_unit =
541					    dinfo->conf.pd_unit;
542					confdata = &conf_old;
543				} else
544#endif
545					confdata = &dinfo->conf;
546				/* Only if we can copy it out do we count it. */
547				if (!(error = copyout(confdata,
548				    (caddr_t)cio->matches +
549				    confsz * cio->num_matches, confsz)))
550					cio->num_matches++;
551			}
552		}
553
554		/*
555		 * Set the pointer into the list, so if the user is getting
556		 * n records at a time, where n < pci_numdevs,
557		 */
558		cio->offset = i;
559
560		/*
561		 * Set the generation, the user will need this if they make
562		 * another ioctl call with offset != 0.
563		 */
564		cio->generation = pci_generation;
565
566		/*
567		 * If this is the last device, inform the user so he won't
568		 * bother asking for more devices.  If dinfo isn't NULL, we
569		 * know that there are more matches in the list because of
570		 * the way the traversal is done.
571		 */
572		if (dinfo == NULL)
573			cio->status = PCI_GETCONF_LAST_DEVICE;
574		else
575			cio->status = PCI_GETCONF_MORE_DEVS;
576
577getconfexit:
578		if (pattern_buf != NULL)
579			free(pattern_buf, M_TEMP);
580#ifdef PRE7_COMPAT
581		if (pattern_buf_old != NULL)
582			free(pattern_buf_old, M_TEMP);
583#endif
584
585		break;
586
587#ifdef PRE7_COMPAT
588	case PCIOCREAD_OLD:
589	case PCIOCWRITE_OLD:
590		io_old = (struct pci_io_old *)data;
591		iodata.pi_sel.pc_domain = 0;
592		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
593		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
594		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
595		iodata.pi_reg = io_old->pi_reg;
596		iodata.pi_width = io_old->pi_width;
597		iodata.pi_data = io_old->pi_data;
598		data = (caddr_t)&iodata;
599		/* FALLTHROUGH */
600#endif
601	case PCIOCREAD:
602	case PCIOCWRITE:
603		io = (struct pci_io *)data;
604		switch(io->pi_width) {
605		case 4:
606		case 2:
607		case 1:
608			/* Make sure register is not negative and aligned. */
609			if (io->pi_reg < 0 ||
610			    io->pi_reg & (io->pi_width - 1)) {
611				error = EINVAL;
612				break;
613			}
614			/*
615			 * Assume that the user-level bus number is
616			 * in fact the physical PCI bus number.
617			 * Look up the grandparent, i.e. the bridge device,
618			 * so that we can issue configuration space cycles.
619			 */
620			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
621			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
622			    io->pi_sel.pc_func);
623			if (pcidev) {
624				brdev = device_get_parent(
625				    device_get_parent(pcidev));
626
627#ifdef PRE7_COMPAT
628				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
629#else
630				if (cmd == PCIOCWRITE)
631#endif
632					PCIB_WRITE_CONFIG(brdev,
633							  io->pi_sel.pc_bus,
634							  io->pi_sel.pc_dev,
635							  io->pi_sel.pc_func,
636							  io->pi_reg,
637							  io->pi_data,
638							  io->pi_width);
639#ifdef PRE7_COMPAT
640				else if (cmd == PCIOCREAD_OLD)
641					io_old->pi_data =
642						PCIB_READ_CONFIG(brdev,
643							  io->pi_sel.pc_bus,
644							  io->pi_sel.pc_dev,
645							  io->pi_sel.pc_func,
646							  io->pi_reg,
647							  io->pi_width);
648#endif
649				else
650					io->pi_data =
651						PCIB_READ_CONFIG(brdev,
652							  io->pi_sel.pc_bus,
653							  io->pi_sel.pc_dev,
654							  io->pi_sel.pc_func,
655							  io->pi_reg,
656							  io->pi_width);
657				error = 0;
658			} else {
659#ifdef COMPAT_FREEBSD4
660				if (cmd == PCIOCREAD_OLD) {
661					io_old->pi_data = -1;
662					error = 0;
663				} else
664#endif
665					error = ENODEV;
666			}
667			break;
668		default:
669			error = EINVAL;
670			break;
671		}
672		break;
673
674	case PCIOCGETBAR:
675		bio = (struct pci_bar_io *)data;
676
677		/*
678		 * Assume that the user-level bus number is
679		 * in fact the physical PCI bus number.
680		 */
681		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
682		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
683		    bio->pbi_sel.pc_func);
684		if (pcidev == NULL) {
685			error = ENODEV;
686			break;
687		}
688		dinfo = device_get_ivars(pcidev);
689
690		/*
691		 * Look for a resource list entry matching the requested BAR.
692		 *
693		 * XXX: This will not find BARs that are not initialized, but
694		 * maybe that is ok?
695		 */
696		rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
697		    bio->pbi_reg);
698		if (rle == NULL)
699			rle = resource_list_find(&dinfo->resources,
700			    SYS_RES_IOPORT, bio->pbi_reg);
701		if (rle == NULL || rle->res == NULL) {
702			error = EINVAL;
703			break;
704		}
705
706		/*
707		 * Ok, we have a resource for this BAR.  Read the lower
708		 * 32 bits to get any flags.
709		 */
710		value = pci_read_config(pcidev, bio->pbi_reg, 4);
711		if (PCI_BAR_MEM(value)) {
712			if (rle->type != SYS_RES_MEMORY) {
713				error = EINVAL;
714				break;
715			}
716			value &= ~PCIM_BAR_MEM_BASE;
717		} else {
718			if (rle->type != SYS_RES_IOPORT) {
719				error = EINVAL;
720				break;
721			}
722			value &= ~PCIM_BAR_IO_BASE;
723		}
724		bio->pbi_base = rman_get_start(rle->res) | value;
725		bio->pbi_length = rman_get_size(rle->res);
726
727		/*
728		 * Check the command register to determine if this BAR
729		 * is enabled.
730		 */
731		value = pci_read_config(pcidev, PCIR_COMMAND, 2);
732		if (rle->type == SYS_RES_MEMORY)
733			bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0;
734		else
735			bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0;
736		error = 0;
737		break;
738	case PCIOCATTACHED:
739		error = 0;
740		io = (struct pci_io *)data;
741		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
742				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
743		if (pcidev != NULL)
744			io->pi_data = device_is_attached(pcidev);
745		else
746			error = ENODEV;
747		break;
748	default:
749		error = ENOTTY;
750		break;
751	}
752
753	return (error);
754}
755