pci_user.c revision 172932
154359Sroberto/*-
254359Sroberto * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
354359Sroberto * All rights reserved.
4290000Sglebius *
554359Sroberto * Redistribution and use in source and binary forms, with or without
654359Sroberto * modification, are permitted provided that the following conditions
754359Sroberto * are met:
854359Sroberto * 1. Redistributions of source code must retain the above copyright
954359Sroberto *    notice unmodified, this list of conditions, and the following
1054359Sroberto *    disclaimer.
1154359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
1254359Sroberto *    notice, this list of conditions and the following disclaimer in the
1354359Sroberto *    documentation and/or other materials provided with the distribution.
1454359Sroberto *
1554359Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1654359Sroberto * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1754359Sroberto * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1854359Sroberto * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1954359Sroberto * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2054359Sroberto * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2154359Sroberto * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2256746Sroberto * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2356746Sroberto * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2454359Sroberto * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2554359Sroberto */
2654359Sroberto
2754359Sroberto#include <sys/cdefs.h>
2856746Sroberto__FBSDID("$FreeBSD: head/sys/dev/pci/pci_user.c 172932 2007-10-24 20:51:44Z marius $");
2956746Sroberto
3054359Sroberto#include "opt_bus.h"	/* XXX trim includes */
3154359Sroberto#include "opt_compat.h"
3254359Sroberto
3354359Sroberto#include <sys/param.h>
3454359Sroberto#include <sys/systm.h>
3554359Sroberto#include <sys/malloc.h>
3654359Sroberto#include <sys/module.h>
3754359Sroberto#include <sys/linker.h>
3854359Sroberto#include <sys/fcntl.h>
3954359Sroberto#include <sys/conf.h>
4054359Sroberto#include <sys/kernel.h>
4154359Sroberto#include <sys/proc.h>
4254359Sroberto#include <sys/queue.h>
4354359Sroberto#include <sys/types.h>
44182007Sroberto
45182007Sroberto#include <vm/vm.h>
4654359Sroberto#include <vm/pmap.h>
4754359Sroberto#include <vm/vm_extern.h>
4854359Sroberto
4954359Sroberto#include <sys/bus.h>
50182007Sroberto#include <machine/bus.h>
51182007Sroberto#include <sys/rman.h>
5254359Sroberto#include <machine/resource.h>
5354359Sroberto
5454359Sroberto#include <sys/pciio.h>
5554359Sroberto#include <dev/pci/pcireg.h>
5654359Sroberto#include <dev/pci/pcivar.h>
5754359Sroberto
5854359Sroberto#include "pcib_if.h"
5954359Sroberto#include "pci_if.h"
60182007Sroberto
61182007Sroberto/*
62182007Sroberto * This is the user interface to PCI configuration space.
63182007Sroberto */
64182007Sroberto
65182007Srobertostatic d_open_t 	pci_open;
6654359Srobertostatic d_close_t	pci_close;
6754359Srobertostatic int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
6854359Sroberto			       struct pci_conf *match_buf);
6954359Srobertostatic d_ioctl_t	pci_ioctl;
7054359Sroberto
7154359Srobertostruct cdevsw pcicdev = {
72290000Sglebius	.d_version =	D_VERSION,
73290000Sglebius	.d_flags =	D_NEEDGIANT,
7454359Sroberto	.d_open =	pci_open,
7554359Sroberto	.d_close =	pci_close,
7654359Sroberto	.d_ioctl =	pci_ioctl,
7754359Sroberto	.d_name =	"pci",
7854359Sroberto};
7954359Sroberto
8056746Srobertostatic int
8154359Srobertopci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
82290000Sglebius{
8354359Sroberto	int error;
8456746Sroberto
8556746Sroberto	if (oflags & FWRITE) {
8656746Sroberto		error = securelevel_gt(td->td_ucred, 0);
8756746Sroberto		if (error)
88132451Sroberto			return (error);
8956746Sroberto	}
9082498Sroberto
9182498Sroberto	return (0);
9282498Sroberto}
9382498Sroberto
94132451Srobertostatic int
95132451Srobertopci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
96106424Sroberto{
97106424Sroberto	return 0;
98132451Sroberto}
99106163Sroberto
100106424Sroberto/*
101106424Sroberto * Match a single pci_conf structure against an array of pci_match_conf
102106424Sroberto * structures.  The first argument, 'matches', is an array of num_matches
103106424Sroberto * pci_match_conf structures.  match_buf is a pointer to the pci_conf
104290000Sglebius * structure that will be compared to every entry in the matches array.
105290000Sglebius * This function returns 1 on failure, 0 on success.
106290000Sglebius */
107290000Sglebiusstatic int
10854359Srobertopci_conf_match(struct pci_match_conf *matches, int num_matches,
10954359Sroberto	       struct pci_conf *match_buf)
11054359Sroberto{
11154359Sroberto	int i;
11254359Sroberto
11354359Sroberto	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
11454359Sroberto		return(1);
11554359Sroberto
11654359Sroberto	for (i = 0; i < num_matches; i++) {
11754359Sroberto		/*
11854359Sroberto		 * I'm not sure why someone would do this...but...
11954359Sroberto		 */
120290000Sglebius		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
12154359Sroberto			continue;
12254359Sroberto
12354359Sroberto		/*
124		 * Look at each of the match flags.  If it's set, do the
125		 * comparison.  If the comparison fails, we don't have a
126		 * match, go on to the next item if there is one.
127		 */
128		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
129		 && (match_buf->pc_sel.pc_domain !=
130		 matches[i].pc_sel.pc_domain))
131			continue;
132
133		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
134		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
135			continue;
136
137		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
138		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
139			continue;
140
141		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
142		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
143			continue;
144
145		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
146		 && (match_buf->pc_vendor != matches[i].pc_vendor))
147			continue;
148
149		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
150		 && (match_buf->pc_device != matches[i].pc_device))
151			continue;
152
153		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
154		 && (match_buf->pc_class != matches[i].pc_class))
155			continue;
156
157		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
158		 && (match_buf->pd_unit != matches[i].pd_unit))
159			continue;
160
161		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
162		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
163			     sizeof(match_buf->pd_name)) != 0))
164			continue;
165
166		return(0);
167	}
168
169	return(1);
170}
171
172#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
173    defined(COMPAT_FREEBSD6)
174
175typedef enum {
176	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
177	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
178	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
179	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
180	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
181	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
182	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
183	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
184	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
185} pci_getconf_flags_old;
186
187struct pcisel_old {
188	u_int8_t	pc_bus;		/* bus number */
189	u_int8_t	pc_dev;		/* device on this bus */
190	u_int8_t	pc_func;	/* function on this device */
191};
192
193struct pci_conf_old {
194	struct pcisel_old pc_sel;	/* bus+slot+function */
195	u_int8_t	pc_hdr;		/* PCI header type */
196	u_int16_t	pc_subvendor;	/* card vendor ID */
197	u_int16_t	pc_subdevice;	/* card device ID, assigned by
198					   card vendor */
199	u_int16_t	pc_vendor;	/* chip vendor ID */
200	u_int16_t	pc_device;	/* chip device ID, assigned by
201					   chip vendor */
202	u_int8_t	pc_class;	/* chip PCI class */
203	u_int8_t	pc_subclass;	/* chip PCI subclass */
204	u_int8_t	pc_progif;	/* chip PCI programming interface */
205	u_int8_t	pc_revid;	/* chip revision ID */
206	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
207	u_long		pd_unit;	/* device unit number */
208};
209
210struct pci_match_conf_old {
211	struct pcisel_old	pc_sel;		/* bus+slot+function */
212	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
213	u_long			pd_unit;	/* Unit number */
214	u_int16_t		pc_vendor;	/* PCI Vendor ID */
215	u_int16_t		pc_device;	/* PCI Device ID */
216	u_int8_t		pc_class;	/* PCI class */
217	pci_getconf_flags	flags;		/* Matching expression */
218};
219
220struct pci_io_old {
221	struct pcisel_old pi_sel;	/* device to operate on */
222	int		pi_reg;		/* configuration register to examine */
223	int		pi_width;	/* width (in bytes) of read or write */
224	u_int32_t	pi_data;	/* data to write or result of read */
225};
226
227#define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
228#define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
229#define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
230
231static int	pci_conf_match_old(struct pci_match_conf_old *matches,
232		    int num_matches, struct pci_conf *match_buf);
233
234static int
235pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
236    struct pci_conf *match_buf)
237{
238	int i;
239
240	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
241		return(1);
242
243	for (i = 0; i < num_matches; i++) {
244		if (match_buf->pc_sel.pc_domain != 0)
245			continue;
246
247		/*
248		 * I'm not sure why someone would do this...but...
249		 */
250		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
251			continue;
252
253		/*
254		 * Look at each of the match flags.  If it's set, do the
255		 * comparison.  If the comparison fails, we don't have a
256		 * match, go on to the next item if there is one.
257		 */
258		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
259		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
260			continue;
261
262		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
263		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
264			continue;
265
266		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
267		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
268			continue;
269
270		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
271		 && (match_buf->pc_vendor != matches[i].pc_vendor))
272			continue;
273
274		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
275		 && (match_buf->pc_device != matches[i].pc_device))
276			continue;
277
278		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
279		 && (match_buf->pc_class != matches[i].pc_class))
280			continue;
281
282		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
283		 && (match_buf->pd_unit != matches[i].pd_unit))
284			continue;
285
286		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
287		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
288			     sizeof(match_buf->pd_name)) != 0))
289			continue;
290
291		return(0);
292	}
293
294	return(1);
295}
296
297#endif
298
299static int
300pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
301{
302	device_t pcidev, brdev;
303	void *confdata;
304	const char *name;
305	struct devlist *devlist_head;
306	struct pci_conf_io *cio;
307	struct pci_devinfo *dinfo;
308	struct pci_io *io;
309	struct pci_match_conf *pattern_buf;
310	size_t confsz, iolen, pbufsz;
311	int error, ionum, i, num_patterns;
312#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
313    defined(COMPAT_FREEBSD6)
314	struct pci_conf_old conf_old;
315	struct pci_io iodata;
316	struct pci_io_old *io_old;
317	struct pci_match_conf_old *pattern_buf_old;
318
319	io_old = NULL;
320	pattern_buf_old = NULL;
321
322	if (!(flag & FWRITE) &&
323	    (cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD))
324		return EPERM;
325#else
326	if (!(flag & FWRITE) && cmd != PCIOCGETCONF)
327		return EPERM;
328#endif
329
330	switch(cmd) {
331#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
332    defined(COMPAT_FREEBSD6)
333	case PCIOCGETCONF_OLD:
334		/* FALLTHROUGH */
335#endif
336	case PCIOCGETCONF:
337		cio = (struct pci_conf_io *)data;
338
339		pattern_buf = NULL;
340		num_patterns = 0;
341		dinfo = NULL;
342
343		cio->num_matches = 0;
344
345		/*
346		 * If the user specified an offset into the device list,
347		 * but the list has changed since they last called this
348		 * ioctl, tell them that the list has changed.  They will
349		 * have to get the list from the beginning.
350		 */
351		if ((cio->offset != 0)
352		 && (cio->generation != pci_generation)){
353			cio->status = PCI_GETCONF_LIST_CHANGED;
354			error = 0;
355			break;
356		}
357
358		/*
359		 * Check to see whether the user has asked for an offset
360		 * past the end of our list.
361		 */
362		if (cio->offset >= pci_numdevs) {
363			cio->status = PCI_GETCONF_LAST_DEVICE;
364			error = 0;
365			break;
366		}
367
368		/* get the head of the device queue */
369		devlist_head = &pci_devq;
370
371		/*
372		 * Determine how much room we have for pci_conf structures.
373		 * Round the user's buffer size down to the nearest
374		 * multiple of sizeof(struct pci_conf) in case the user
375		 * didn't specify a multiple of that size.
376		 */
377#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
378    defined(COMPAT_FREEBSD6)
379		if (cmd == PCIOCGETCONF_OLD)
380			confsz = sizeof(struct pci_conf_old);
381		else
382#endif
383			confsz = sizeof(struct pci_conf);
384		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
385		    pci_numdevs * confsz);
386
387		/*
388		 * Since we know that iolen is a multiple of the size of
389		 * the pciconf union, it's okay to do this.
390		 */
391		ionum = iolen / confsz;
392
393		/*
394		 * If this test is true, the user wants the pci_conf
395		 * structures returned to match the supplied entries.
396		 */
397		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
398		 && (cio->pat_buf_len > 0)) {
399			/*
400			 * pat_buf_len needs to be:
401			 * num_patterns * sizeof(struct pci_match_conf)
402			 * While it is certainly possible the user just
403			 * allocated a large buffer, but set the number of
404			 * matches correctly, it is far more likely that
405			 * their kernel doesn't match the userland utility
406			 * they're using.  It's also possible that the user
407			 * forgot to initialize some variables.  Yes, this
408			 * may be overly picky, but I hazard to guess that
409			 * it's far more likely to just catch folks that
410			 * updated their kernel but not their userland.
411			 */
412#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
413    defined(COMPAT_FREEBSD6)
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#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
430    defined(COMPAT_FREEBSD6)
431			if (cmd == PCIOCGETCONF_OLD) {
432				pattern_buf_old = malloc(cio->pat_buf_len,
433				    M_TEMP, M_WAITOK);
434				error = copyin(cio->patterns,
435				    pattern_buf_old, cio->pat_buf_len);
436			} else
437#endif
438			{
439				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
440				    M_WAITOK);
441				error = copyin(cio->patterns, pattern_buf,
442				    cio->pat_buf_len);
443			}
444			if (error != 0) {
445				error = EINVAL;
446				goto getconfexit;
447			}
448			num_patterns = cio->num_patterns;
449		} else if ((cio->num_patterns > 0)
450			|| (cio->pat_buf_len > 0)) {
451			/*
452			 * The user made a mistake, spit out an error.
453			 */
454			cio->status = PCI_GETCONF_ERROR;
455			error = EINVAL;
456			break;
457		}
458
459		/*
460		 * Go through the list of devices and copy out the devices
461		 * that match the user's criteria.
462		 */
463		for (cio->num_matches = 0, error = 0, i = 0,
464		     dinfo = STAILQ_FIRST(devlist_head);
465		     (dinfo != NULL) && (cio->num_matches < ionum)
466		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
467		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
468
469			if (i < cio->offset)
470				continue;
471
472			/* Populate pd_name and pd_unit */
473			name = NULL;
474			if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0')
475				name = device_get_name(dinfo->cfg.dev);
476			if (name) {
477				strncpy(dinfo->conf.pd_name, name,
478					sizeof(dinfo->conf.pd_name));
479				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
480				dinfo->conf.pd_unit =
481					device_get_unit(dinfo->cfg.dev);
482			}
483
484#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
485    defined(COMPAT_FREEBSD6)
486			if ((cmd == PCIOCGETCONF_OLD &&
487			    (pattern_buf_old == NULL ||
488			    pci_conf_match_old(pattern_buf_old, num_patterns,
489			    &dinfo->conf) == 0)) ||
490			    (cmd == PCIOCGETCONF &&
491			    (pattern_buf == NULL ||
492			    pci_conf_match(pattern_buf, num_patterns,
493			    &dinfo->conf) == 0))) {
494#else
495			if (pattern_buf == NULL ||
496			    pci_conf_match(pattern_buf, num_patterns,
497			    &dinfo->conf) == 0) {
498#endif
499				/*
500				 * If we've filled up the user's buffer,
501				 * break out at this point.  Since we've
502				 * got a match here, we'll pick right back
503				 * up at the matching entry.  We can also
504				 * tell the user that there are more matches
505				 * left.
506				 */
507				if (cio->num_matches >= ionum)
508					break;
509
510#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
511    defined(COMPAT_FREEBSD6)
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					conf_old.pd_name[0] = '\0';
537					conf_old.pd_unit = 0;
538					if (name) {
539						strncpy(conf_old.pd_name, name,
540						    sizeof(conf_old.pd_name));
541						conf_old.
542						    pd_name[PCI_MAXNAMELEN] = 0;
543						conf_old.pd_unit =
544						    dinfo->conf.pd_unit;
545					}
546					confdata = &conf_old;
547				} else
548#endif
549					confdata = &dinfo->conf;
550				/* Only if we can copy it out do we count it. */
551				if (!(error = copyout(confdata,
552				    &cio->matches[cio->num_matches], confsz)))
553					cio->num_matches++;
554			}
555		}
556
557		/*
558		 * Set the pointer into the list, so if the user is getting
559		 * n records at a time, where n < pci_numdevs,
560		 */
561		cio->offset = i;
562
563		/*
564		 * Set the generation, the user will need this if they make
565		 * another ioctl call with offset != 0.
566		 */
567		cio->generation = pci_generation;
568
569		/*
570		 * If this is the last device, inform the user so he won't
571		 * bother asking for more devices.  If dinfo isn't NULL, we
572		 * know that there are more matches in the list because of
573		 * the way the traversal is done.
574		 */
575		if (dinfo == NULL)
576			cio->status = PCI_GETCONF_LAST_DEVICE;
577		else
578			cio->status = PCI_GETCONF_MORE_DEVS;
579
580getconfexit:
581		if (pattern_buf != NULL)
582			free(pattern_buf, M_TEMP);
583#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
584    defined(COMPAT_FREEBSD6)
585		if (pattern_buf_old != NULL)
586			free(pattern_buf_old, M_TEMP);
587#endif
588
589		break;
590
591#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
592    defined(COMPAT_FREEBSD6)
593	case PCIOCREAD_OLD:
594	case PCIOCWRITE_OLD:
595		io_old = (struct pci_io_old *)data;
596		iodata.pi_sel.pc_domain = 0;
597		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
598		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
599		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
600		iodata.pi_reg = io_old->pi_reg;
601		iodata.pi_width = io_old->pi_width;
602		iodata.pi_data = io_old->pi_data;
603		data = (caddr_t)&iodata;
604		/* FALLTHROUGH */
605#endif
606	case PCIOCREAD:
607	case PCIOCWRITE:
608		io = (struct pci_io *)data;
609		switch(io->pi_width) {
610		case 4:
611		case 2:
612		case 1:
613			/* Make sure register is in bounds and aligned. */
614			if (io->pi_reg < 0 ||
615			    io->pi_reg + io->pi_width > PCI_REGMAX + 1 ||
616			    io->pi_reg & (io->pi_width - 1)) {
617				error = EINVAL;
618				break;
619			}
620			/*
621			 * Assume that the user-level bus number is
622			 * in fact the physical PCI bus number.
623			 * Look up the grandparent, i.e. the bridge device,
624			 * so that we can issue configuration space cycles.
625			 */
626			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
627			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
628			    io->pi_sel.pc_func);
629			if (pcidev) {
630				brdev = device_get_parent(
631				    device_get_parent(pcidev));
632
633#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
634    defined(COMPAT_FREEBSD6)
635				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
636#else
637				if (cmd == PCIOCWRITE)
638#endif
639					PCIB_WRITE_CONFIG(brdev,
640							  io->pi_sel.pc_bus,
641							  io->pi_sel.pc_dev,
642							  io->pi_sel.pc_func,
643							  io->pi_reg,
644							  io->pi_data,
645							  io->pi_width);
646#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
647    defined(COMPAT_FREEBSD6)
648				else if (cmd == PCIOCREAD_OLD)
649					io_old->pi_data =
650						PCIB_READ_CONFIG(brdev,
651							  io->pi_sel.pc_bus,
652							  io->pi_sel.pc_dev,
653							  io->pi_sel.pc_func,
654							  io->pi_reg,
655							  io->pi_width);
656#endif
657				else
658					io->pi_data =
659						PCIB_READ_CONFIG(brdev,
660							  io->pi_sel.pc_bus,
661							  io->pi_sel.pc_dev,
662							  io->pi_sel.pc_func,
663							  io->pi_reg,
664							  io->pi_width);
665				error = 0;
666			} else {
667#ifdef COMPAT_FREEBSD4
668				if (cmd == PCIOCREAD_OLD) {
669					io_old->pi_data = -1;
670					error = 0;
671				} else
672#endif
673					error = ENODEV;
674			}
675			break;
676		default:
677			error = EINVAL;
678			break;
679		}
680		break;
681
682	default:
683		error = ENOTTY;
684		break;
685	}
686
687	return (error);
688}
689