xref: /freebsd/sys/dev/pci/pci_user.c (revision 6469bdcdb6a5968dc7edfcfb495d427b4bfdb3dd)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_bus.h"	/* XXX trim includes */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/linker.h>
39 #include <sys/fcntl.h>
40 #include <sys/conf.h>
41 #include <sys/kernel.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/types.h>
45 
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48 #include <vm/vm_extern.h>
49 
50 #include <sys/bus.h>
51 #include <machine/bus.h>
52 #include <sys/rman.h>
53 #include <machine/resource.h>
54 
55 #include <sys/pciio.h>
56 #include <dev/pci/pcireg.h>
57 #include <dev/pci/pcivar.h>
58 
59 #include "pcib_if.h"
60 #include "pci_if.h"
61 
62 /*
63  * This is the user interface to PCI configuration space.
64  */
65 
66 static d_open_t 	pci_open;
67 static d_close_t	pci_close;
68 static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
69 			       struct pci_conf *match_buf);
70 static d_ioctl_t	pci_ioctl;
71 
72 struct cdevsw pcicdev = {
73 	.d_version =	D_VERSION,
74 	.d_flags =	D_NEEDGIANT,
75 	.d_open =	pci_open,
76 	.d_close =	pci_close,
77 	.d_ioctl =	pci_ioctl,
78 	.d_name =	"pci",
79 };
80 
81 static int
82 pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
83 {
84 	int error;
85 
86 	if (oflags & FWRITE) {
87 		error = securelevel_gt(td->td_ucred, 0);
88 		if (error)
89 			return (error);
90 	}
91 
92 	return (0);
93 }
94 
95 static int
96 pci_close(struct cdev *dev, int flag, int devtype, struct thread *td)
97 {
98 	return 0;
99 }
100 
101 /*
102  * Match a single pci_conf structure against an array of pci_match_conf
103  * structures.  The first argument, 'matches', is an array of num_matches
104  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
105  * structure that will be compared to every entry in the matches array.
106  * This function returns 1 on failure, 0 on success.
107  */
108 static int
109 pci_conf_match(struct pci_match_conf *matches, int num_matches,
110 	       struct pci_conf *match_buf)
111 {
112 	int i;
113 
114 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
115 		return(1);
116 
117 	for (i = 0; i < num_matches; i++) {
118 		/*
119 		 * I'm not sure why someone would do this...but...
120 		 */
121 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
122 			continue;
123 
124 		/*
125 		 * Look at each of the match flags.  If it's set, do the
126 		 * comparison.  If the comparison fails, we don't have a
127 		 * match, go on to the next item if there is one.
128 		 */
129 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
130 		 && (match_buf->pc_sel.pc_domain !=
131 		 matches[i].pc_sel.pc_domain))
132 			continue;
133 
134 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
135 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
136 			continue;
137 
138 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
139 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
140 			continue;
141 
142 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
143 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
144 			continue;
145 
146 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
147 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
148 			continue;
149 
150 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
151 		 && (match_buf->pc_device != matches[i].pc_device))
152 			continue;
153 
154 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
155 		 && (match_buf->pc_class != matches[i].pc_class))
156 			continue;
157 
158 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
159 		 && (match_buf->pd_unit != matches[i].pd_unit))
160 			continue;
161 
162 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
163 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
164 			     sizeof(match_buf->pd_name)) != 0))
165 			continue;
166 
167 		return(0);
168 	}
169 
170 	return(1);
171 }
172 
173 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
174     defined(COMPAT_FREEBSD6)
175 #define PRE7_COMPAT
176 
177 typedef enum {
178 	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
179 	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
180 	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
181 	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
182 	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
183 	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
184 	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
185 	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
186 	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
187 } pci_getconf_flags_old;
188 
189 struct pcisel_old {
190 	u_int8_t	pc_bus;		/* bus number */
191 	u_int8_t	pc_dev;		/* device on this bus */
192 	u_int8_t	pc_func;	/* function on this device */
193 };
194 
195 struct pci_conf_old {
196 	struct pcisel_old pc_sel;	/* bus+slot+function */
197 	u_int8_t	pc_hdr;		/* PCI header type */
198 	u_int16_t	pc_subvendor;	/* card vendor ID */
199 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
200 					   card vendor */
201 	u_int16_t	pc_vendor;	/* chip vendor ID */
202 	u_int16_t	pc_device;	/* chip device ID, assigned by
203 					   chip vendor */
204 	u_int8_t	pc_class;	/* chip PCI class */
205 	u_int8_t	pc_subclass;	/* chip PCI subclass */
206 	u_int8_t	pc_progif;	/* chip PCI programming interface */
207 	u_int8_t	pc_revid;	/* chip revision ID */
208 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
209 	u_long		pd_unit;	/* device unit number */
210 };
211 
212 struct pci_match_conf_old {
213 	struct pcisel_old	pc_sel;		/* bus+slot+function */
214 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
215 	u_long			pd_unit;	/* Unit number */
216 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
217 	u_int16_t		pc_device;	/* PCI Device ID */
218 	u_int8_t		pc_class;	/* PCI class */
219 	pci_getconf_flags_old	flags;		/* Matching expression */
220 };
221 
222 struct pci_io_old {
223 	struct pcisel_old pi_sel;	/* device to operate on */
224 	int		pi_reg;		/* configuration register to examine */
225 	int		pi_width;	/* width (in bytes) of read or write */
226 	u_int32_t	pi_data;	/* data to write or result of read */
227 };
228 
229 #ifdef COMPAT_FREEBSD32
230 struct pci_conf_old32 {
231 	struct pcisel_old pc_sel;	/* bus+slot+function */
232 	uint8_t		pc_hdr;		/* PCI header type */
233 	uint16_t	pc_subvendor;	/* card vendor ID */
234 	uint16_t	pc_subdevice;	/* card device ID, assigned by
235 					   card vendor */
236 	uint16_t	pc_vendor;	/* chip vendor ID */
237 	uint16_t	pc_device;	/* chip device ID, assigned by
238 					   chip vendor */
239 	uint8_t		pc_class;	/* chip PCI class */
240 	uint8_t		pc_subclass;	/* chip PCI subclass */
241 	uint8_t		pc_progif;	/* chip PCI programming interface */
242 	uint8_t		pc_revid;	/* chip revision ID */
243 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
244 	uint32_t	pd_unit;	/* device unit number (u_long) */
245 };
246 
247 struct pci_match_conf_old32 {
248 	struct pcisel_old pc_sel;	/* bus+slot+function */
249 	char		pd_name[PCI_MAXNAMELEN + 1]; /* device name */
250 	uint32_t	pd_unit;	/* Unit number (u_long) */
251 	uint16_t	pc_vendor;	/* PCI Vendor ID */
252 	uint16_t	pc_device;	/* PCI Device ID */
253 	uint8_t		pc_class;	/* PCI class */
254 	pci_getconf_flags_old flags;	/* Matching expression */
255 };
256 
257 struct pci_conf_io32 {
258 	uint32_t	pat_buf_len;	/* pattern buffer length */
259 	uint32_t	num_patterns;	/* number of patterns */
260 	uint32_t	patterns;	/* pattern buffer
261 					   (struct pci_match_conf_old32 *) */
262 	uint32_t	match_buf_len;	/* match buffer length */
263 	uint32_t	num_matches;	/* number of matches returned */
264 	uint32_t	matches;	/* match buffer
265 					   (struct pci_conf_old32 *) */
266 	uint32_t	offset;		/* offset into device list */
267 	uint32_t	generation;	/* device list generation */
268 	pci_getconf_status status;	/* request status */
269 };
270 
271 #define	PCIOCGETCONF_OLD32	_IOWR('p', 1, struct pci_conf_io32)
272 #endif	/* COMPAT_FREEBSD32 */
273 
274 #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
275 #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
276 #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
277 
278 static int	pci_conf_match_old(struct pci_match_conf_old *matches,
279 		    int num_matches, struct pci_conf *match_buf);
280 
281 static int
282 pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
283     struct pci_conf *match_buf)
284 {
285 	int i;
286 
287 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
288 		return(1);
289 
290 	for (i = 0; i < num_matches; i++) {
291 		if (match_buf->pc_sel.pc_domain != 0)
292 			continue;
293 
294 		/*
295 		 * I'm not sure why someone would do this...but...
296 		 */
297 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
298 			continue;
299 
300 		/*
301 		 * Look at each of the match flags.  If it's set, do the
302 		 * comparison.  If the comparison fails, we don't have a
303 		 * match, go on to the next item if there is one.
304 		 */
305 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
306 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
307 			continue;
308 
309 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
310 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
311 			continue;
312 
313 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
314 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
315 			continue;
316 
317 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
318 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
319 			continue;
320 
321 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
322 		 && (match_buf->pc_device != matches[i].pc_device))
323 			continue;
324 
325 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
326 		 && (match_buf->pc_class != matches[i].pc_class))
327 			continue;
328 
329 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
330 		 && (match_buf->pd_unit != matches[i].pd_unit))
331 			continue;
332 
333 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
334 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
335 			     sizeof(match_buf->pd_name)) != 0))
336 			continue;
337 
338 		return(0);
339 	}
340 
341 	return(1);
342 }
343 
344 #ifdef COMPAT_FREEBSD32
345 static int
346 pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
347     struct pci_conf *match_buf)
348 {
349 	int i;
350 
351 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
352 		return(1);
353 
354 	for (i = 0; i < num_matches; i++) {
355 		if (match_buf->pc_sel.pc_domain != 0)
356 			continue;
357 
358 		/*
359 		 * I'm not sure why someone would do this...but...
360 		 */
361 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
362 			continue;
363 
364 		/*
365 		 * Look at each of the match flags.  If it's set, do the
366 		 * comparison.  If the comparison fails, we don't have a
367 		 * match, go on to the next item if there is one.
368 		 */
369 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
370 		    (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
371 			continue;
372 
373 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
374 		    (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
375 			continue;
376 
377 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
378 		    (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
379 			continue;
380 
381 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
382 		    (match_buf->pc_vendor != matches[i].pc_vendor))
383 			continue;
384 
385 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
386 		    (match_buf->pc_device != matches[i].pc_device))
387 			continue;
388 
389 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
390 		    (match_buf->pc_class != matches[i].pc_class))
391 			continue;
392 
393 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
394 		    ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
395 			continue;
396 
397 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
398 		    (strncmp(matches[i].pd_name, match_buf->pd_name,
399 		    sizeof(match_buf->pd_name)) != 0))
400 			continue;
401 
402 		return (0);
403 	}
404 
405 	return (1);
406 }
407 #endif	/* COMPAT_FREEBSD32 */
408 #endif	/* PRE7_COMPAT */
409 
410 static int
411 pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
412 {
413 	struct pci_vpd_element vpd_element, *vpd_user;
414 	struct pcicfg_vpd *vpd;
415 	size_t len;
416 	int error, i;
417 
418 	vpd = pci_fetch_vpd_list(dev);
419 	if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL)
420 		return (ENXIO);
421 
422 	/*
423 	 * Calculate the amount of space needed in the data buffer.  An
424 	 * identifier element is always present followed by the read-only
425 	 * and read-write keywords.
426 	 */
427 	len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident);
428 	for (i = 0; i < vpd->vpd_rocnt; i++)
429 		len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len;
430 	for (i = 0; i < vpd->vpd_wcnt; i++)
431 		len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len;
432 
433 	if (lvio->plvi_len == 0) {
434 		lvio->plvi_len = len;
435 		return (0);
436 	}
437 	if (lvio->plvi_len < len) {
438 		lvio->plvi_len = len;
439 		return (ENOMEM);
440 	}
441 
442 	/*
443 	 * Copyout the identifier string followed by each keyword and
444 	 * value.
445 	 */
446 	vpd_user = lvio->plvi_data;
447 	vpd_element.pve_keyword[0] = '\0';
448 	vpd_element.pve_keyword[1] = '\0';
449 	vpd_element.pve_flags = PVE_FLAG_IDENT;
450 	vpd_element.pve_datalen = strlen(vpd->vpd_ident);
451 	error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
452 	if (error)
453 		return (error);
454 	error = copyout(vpd->vpd_ident, vpd_user->pve_data,
455 	    strlen(vpd->vpd_ident));
456 	if (error)
457 		return (error);
458 	vpd_user = PVE_NEXT(vpd_user);
459 	vpd_element.pve_flags = 0;
460 	for (i = 0; i < vpd->vpd_rocnt; i++) {
461 		vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
462 		vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1];
463 		vpd_element.pve_datalen = vpd->vpd_ros[i].len;
464 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
465 		if (error)
466 			return (error);
467 		error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data,
468 		    vpd->vpd_ros[i].len);
469 		if (error)
470 			return (error);
471 		vpd_user = PVE_NEXT(vpd_user);
472 	}
473 	vpd_element.pve_flags = PVE_FLAG_RW;
474 	for (i = 0; i < vpd->vpd_wcnt; i++) {
475 		vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0];
476 		vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1];
477 		vpd_element.pve_datalen = vpd->vpd_w[i].len;
478 		error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
479 		if (error)
480 			return (error);
481 		error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data,
482 		    vpd->vpd_w[i].len);
483 		if (error)
484 			return (error);
485 		vpd_user = PVE_NEXT(vpd_user);
486 	}
487 	KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
488 	    ("length mismatch"));
489 	lvio->plvi_len = len;
490 	return (0);
491 }
492 
493 static int
494 pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
495 {
496 	device_t pcidev;
497 	void *confdata;
498 	const char *name;
499 	struct devlist *devlist_head;
500 	struct pci_conf_io *cio = NULL;
501 	struct pci_devinfo *dinfo;
502 	struct pci_io *io;
503 	struct pci_bar_io *bio;
504 	struct pci_list_vpd_io *lvio;
505 	struct pci_match_conf *pattern_buf;
506 	struct pci_map *pm;
507 	size_t confsz, iolen, pbufsz;
508 	int error, ionum, i, num_patterns;
509 #ifdef PRE7_COMPAT
510 #ifdef COMPAT_FREEBSD32
511 	struct pci_conf_io32 *cio32 = NULL;
512 	struct pci_conf_old32 conf_old32;
513 	struct pci_match_conf_old32 *pattern_buf_old32 = NULL;
514 #endif
515 	struct pci_conf_old conf_old;
516 	struct pci_io iodata;
517 	struct pci_io_old *io_old;
518 	struct pci_match_conf_old *pattern_buf_old = NULL;
519 
520 	io_old = NULL;
521 #endif
522 
523 	if (!(flag & FWRITE)) {
524 		switch (cmd) {
525 #ifdef PRE7_COMPAT
526 #ifdef COMPAT_FREEBSD32
527 		case PCIOCGETCONF_OLD32:
528 #endif
529 		case PCIOCGETCONF_OLD:
530 #endif
531 		case PCIOCGETCONF:
532 		case PCIOCGETBAR:
533 		case PCIOCLISTVPD:
534 			break;
535 		default:
536 			return (EPERM);
537 		}
538 	}
539 
540 	switch (cmd) {
541 #ifdef PRE7_COMPAT
542 #ifdef COMPAT_FREEBSD32
543 	case PCIOCGETCONF_OLD32:
544                cio32 = (struct pci_conf_io32 *)data;
545                cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
546                cio->pat_buf_len = cio32->pat_buf_len;
547                cio->num_patterns = cio32->num_patterns;
548                cio->patterns = (void *)(uintptr_t)cio32->patterns;
549                cio->match_buf_len = cio32->match_buf_len;
550                cio->num_matches = cio32->num_matches;
551                cio->matches = (void *)(uintptr_t)cio32->matches;
552                cio->offset = cio32->offset;
553                cio->generation = cio32->generation;
554                cio->status = cio32->status;
555                cio32->num_matches = 0;
556                break;
557 #endif
558 	case PCIOCGETCONF_OLD:
559 #endif
560 	case PCIOCGETCONF:
561 		cio = (struct pci_conf_io *)data;
562 	}
563 
564 	switch (cmd) {
565 #ifdef PRE7_COMPAT
566 #ifdef COMPAT_FREEBSD32
567 	case PCIOCGETCONF_OLD32:
568 #endif
569 	case PCIOCGETCONF_OLD:
570 #endif
571 	case PCIOCGETCONF:
572 
573 		pattern_buf = NULL;
574 		num_patterns = 0;
575 		dinfo = NULL;
576 
577 		cio->num_matches = 0;
578 
579 		/*
580 		 * If the user specified an offset into the device list,
581 		 * but the list has changed since they last called this
582 		 * ioctl, tell them that the list has changed.  They will
583 		 * have to get the list from the beginning.
584 		 */
585 		if ((cio->offset != 0)
586 		 && (cio->generation != pci_generation)){
587 			cio->status = PCI_GETCONF_LIST_CHANGED;
588 			error = 0;
589 			goto getconfexit;
590 		}
591 
592 		/*
593 		 * Check to see whether the user has asked for an offset
594 		 * past the end of our list.
595 		 */
596 		if (cio->offset >= pci_numdevs) {
597 			cio->status = PCI_GETCONF_LAST_DEVICE;
598 			error = 0;
599 			goto getconfexit;
600 		}
601 
602 		/* get the head of the device queue */
603 		devlist_head = &pci_devq;
604 
605 		/*
606 		 * Determine how much room we have for pci_conf structures.
607 		 * Round the user's buffer size down to the nearest
608 		 * multiple of sizeof(struct pci_conf) in case the user
609 		 * didn't specify a multiple of that size.
610 		 */
611 #ifdef PRE7_COMPAT
612 #ifdef COMPAT_FREEBSD32
613 		if (cmd == PCIOCGETCONF_OLD32)
614 			confsz = sizeof(struct pci_conf_old32);
615 		else
616 #endif
617 		if (cmd == PCIOCGETCONF_OLD)
618 			confsz = sizeof(struct pci_conf_old);
619 		else
620 #endif
621 			confsz = sizeof(struct pci_conf);
622 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
623 		    pci_numdevs * confsz);
624 
625 		/*
626 		 * Since we know that iolen is a multiple of the size of
627 		 * the pciconf union, it's okay to do this.
628 		 */
629 		ionum = iolen / confsz;
630 
631 		/*
632 		 * If this test is true, the user wants the pci_conf
633 		 * structures returned to match the supplied entries.
634 		 */
635 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
636 		 && (cio->pat_buf_len > 0)) {
637 			/*
638 			 * pat_buf_len needs to be:
639 			 * num_patterns * sizeof(struct pci_match_conf)
640 			 * While it is certainly possible the user just
641 			 * allocated a large buffer, but set the number of
642 			 * matches correctly, it is far more likely that
643 			 * their kernel doesn't match the userland utility
644 			 * they're using.  It's also possible that the user
645 			 * forgot to initialize some variables.  Yes, this
646 			 * may be overly picky, but I hazard to guess that
647 			 * it's far more likely to just catch folks that
648 			 * updated their kernel but not their userland.
649 			 */
650 #ifdef PRE7_COMPAT
651 #ifdef COMPAT_FREEBSD32
652 			if (cmd == PCIOCGETCONF_OLD32)
653 				pbufsz = sizeof(struct pci_match_conf_old32);
654 			else
655 #endif
656 			if (cmd == PCIOCGETCONF_OLD)
657 				pbufsz = sizeof(struct pci_match_conf_old);
658 			else
659 #endif
660 				pbufsz = sizeof(struct pci_match_conf);
661 			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
662 				/* The user made a mistake, return an error. */
663 				cio->status = PCI_GETCONF_ERROR;
664 				error = EINVAL;
665 				goto getconfexit;
666 			}
667 
668 			/*
669 			 * Allocate a buffer to hold the patterns.
670 			 */
671 #ifdef PRE7_COMPAT
672 #ifdef COMPAT_FREEBSD32
673 			if (cmd == PCIOCGETCONF_OLD32) {
674 				pattern_buf_old32 = malloc(cio->pat_buf_len,
675 				    M_TEMP, M_WAITOK);
676 				error = copyin(cio->patterns,
677 				    pattern_buf_old32, cio->pat_buf_len);
678 			} else
679 #endif /* COMPAT_FREEBSD32 */
680 			if (cmd == PCIOCGETCONF_OLD) {
681 				pattern_buf_old = malloc(cio->pat_buf_len,
682 				    M_TEMP, M_WAITOK);
683 				error = copyin(cio->patterns,
684 				    pattern_buf_old, cio->pat_buf_len);
685 			} else
686 #endif /* PRE7_COMPAT */
687 			{
688 				pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
689 				    M_WAITOK);
690 				error = copyin(cio->patterns, pattern_buf,
691 				    cio->pat_buf_len);
692 			}
693 			if (error != 0) {
694 				error = EINVAL;
695 				goto getconfexit;
696 			}
697 			num_patterns = cio->num_patterns;
698 		} else if ((cio->num_patterns > 0)
699 			|| (cio->pat_buf_len > 0)) {
700 			/*
701 			 * The user made a mistake, spit out an error.
702 			 */
703 			cio->status = PCI_GETCONF_ERROR;
704 			error = EINVAL;
705                        goto getconfexit;
706 		}
707 
708 		/*
709 		 * Go through the list of devices and copy out the devices
710 		 * that match the user's criteria.
711 		 */
712 		for (cio->num_matches = 0, i = 0,
713 				 dinfo = STAILQ_FIRST(devlist_head);
714 		     dinfo != NULL;
715 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
716 
717 			if (i < cio->offset)
718 				continue;
719 
720 			/* Populate pd_name and pd_unit */
721 			name = NULL;
722 			if (dinfo->cfg.dev)
723 				name = device_get_name(dinfo->cfg.dev);
724 			if (name) {
725 				strncpy(dinfo->conf.pd_name, name,
726 					sizeof(dinfo->conf.pd_name));
727 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
728 				dinfo->conf.pd_unit =
729 					device_get_unit(dinfo->cfg.dev);
730 			} else {
731 				dinfo->conf.pd_name[0] = '\0';
732 				dinfo->conf.pd_unit = 0;
733 			}
734 
735 #ifdef PRE7_COMPAT
736 			if (
737 #ifdef COMPAT_FREEBSD32
738 			    (cmd == PCIOCGETCONF_OLD32 &&
739 			    (pattern_buf_old32 == NULL ||
740 			    pci_conf_match_old32(pattern_buf_old32,
741 			    num_patterns, &dinfo->conf) == 0)) ||
742 #endif
743 			    (cmd == PCIOCGETCONF_OLD &&
744 			    (pattern_buf_old == NULL ||
745 			    pci_conf_match_old(pattern_buf_old, num_patterns,
746 			    &dinfo->conf) == 0)) ||
747 			    (cmd == PCIOCGETCONF &&
748 			    (pattern_buf == NULL ||
749 			    pci_conf_match(pattern_buf, num_patterns,
750 			    &dinfo->conf) == 0))) {
751 #else
752 			if (pattern_buf == NULL ||
753 			    pci_conf_match(pattern_buf, num_patterns,
754 			    &dinfo->conf) == 0) {
755 #endif
756 				/*
757 				 * If we've filled up the user's buffer,
758 				 * break out at this point.  Since we've
759 				 * got a match here, we'll pick right back
760 				 * up at the matching entry.  We can also
761 				 * tell the user that there are more matches
762 				 * left.
763 				 */
764 				if (cio->num_matches >= ionum) {
765 					error = 0;
766 					break;
767 				}
768 
769 #ifdef PRE7_COMPAT
770 #ifdef COMPAT_FREEBSD32
771 				if (cmd == PCIOCGETCONF_OLD32) {
772 					memset(&conf_old32, 0,
773 					    sizeof(conf_old32));
774 					conf_old32.pc_sel.pc_bus =
775 					    dinfo->conf.pc_sel.pc_bus;
776 					conf_old32.pc_sel.pc_dev =
777 					    dinfo->conf.pc_sel.pc_dev;
778 					conf_old32.pc_sel.pc_func =
779 					    dinfo->conf.pc_sel.pc_func;
780 					conf_old32.pc_hdr = dinfo->conf.pc_hdr;
781 					conf_old32.pc_subvendor =
782 					    dinfo->conf.pc_subvendor;
783 					conf_old32.pc_subdevice =
784 					    dinfo->conf.pc_subdevice;
785 					conf_old32.pc_vendor =
786 					    dinfo->conf.pc_vendor;
787 					conf_old32.pc_device =
788 					    dinfo->conf.pc_device;
789 					conf_old32.pc_class =
790 					    dinfo->conf.pc_class;
791 					conf_old32.pc_subclass =
792 					    dinfo->conf.pc_subclass;
793 					conf_old32.pc_progif =
794 					    dinfo->conf.pc_progif;
795 					conf_old32.pc_revid =
796 					    dinfo->conf.pc_revid;
797 					strncpy(conf_old32.pd_name,
798 					    dinfo->conf.pd_name,
799 					    sizeof(conf_old32.pd_name));
800 					conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
801 					conf_old32.pd_unit =
802 					    (uint32_t)dinfo->conf.pd_unit;
803 					confdata = &conf_old32;
804 				} else
805 #endif /* COMPAT_FREEBSD32 */
806 				if (cmd == PCIOCGETCONF_OLD) {
807 					memset(&conf_old, 0, sizeof(conf_old));
808 					conf_old.pc_sel.pc_bus =
809 					    dinfo->conf.pc_sel.pc_bus;
810 					conf_old.pc_sel.pc_dev =
811 					    dinfo->conf.pc_sel.pc_dev;
812 					conf_old.pc_sel.pc_func =
813 					    dinfo->conf.pc_sel.pc_func;
814 					conf_old.pc_hdr = dinfo->conf.pc_hdr;
815 					conf_old.pc_subvendor =
816 					    dinfo->conf.pc_subvendor;
817 					conf_old.pc_subdevice =
818 					    dinfo->conf.pc_subdevice;
819 					conf_old.pc_vendor =
820 					    dinfo->conf.pc_vendor;
821 					conf_old.pc_device =
822 					    dinfo->conf.pc_device;
823 					conf_old.pc_class =
824 					    dinfo->conf.pc_class;
825 					conf_old.pc_subclass =
826 					    dinfo->conf.pc_subclass;
827 					conf_old.pc_progif =
828 					    dinfo->conf.pc_progif;
829 					conf_old.pc_revid =
830 					    dinfo->conf.pc_revid;
831 					strncpy(conf_old.pd_name,
832 					    dinfo->conf.pd_name,
833 					    sizeof(conf_old.pd_name));
834 					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
835 					conf_old.pd_unit =
836 					    dinfo->conf.pd_unit;
837 					confdata = &conf_old;
838 				} else
839 #endif /* PRE7_COMPAT */
840 					confdata = &dinfo->conf;
841 				error = copyout(confdata,
842 				    (caddr_t)cio->matches +
843 				    confsz * cio->num_matches, confsz);
844 				if (error)
845 					break;
846 				cio->num_matches++;
847 			}
848 		}
849 
850 		/*
851 		 * Set the pointer into the list, so if the user is getting
852 		 * n records at a time, where n < pci_numdevs,
853 		 */
854 		cio->offset = i;
855 
856 		/*
857 		 * Set the generation, the user will need this if they make
858 		 * another ioctl call with offset != 0.
859 		 */
860 		cio->generation = pci_generation;
861 
862 		/*
863 		 * If this is the last device, inform the user so he won't
864 		 * bother asking for more devices.  If dinfo isn't NULL, we
865 		 * know that there are more matches in the list because of
866 		 * the way the traversal is done.
867 		 */
868 		if (dinfo == NULL)
869 			cio->status = PCI_GETCONF_LAST_DEVICE;
870 		else
871 			cio->status = PCI_GETCONF_MORE_DEVS;
872 
873 getconfexit:
874 #ifdef PRE7_COMPAT
875 #ifdef COMPAT_FREEBSD32
876 		if (cmd == PCIOCGETCONF_OLD32) {
877 			cio32->status = cio->status;
878 			cio32->generation = cio->generation;
879 			cio32->offset = cio->offset;
880 			cio32->num_matches = cio->num_matches;
881 			free(cio, M_TEMP);
882 		}
883 		if (pattern_buf_old32 != NULL)
884 			free(pattern_buf_old32, M_TEMP);
885 #endif
886 		if (pattern_buf_old != NULL)
887 			free(pattern_buf_old, M_TEMP);
888 #endif
889 		if (pattern_buf != NULL)
890 			free(pattern_buf, M_TEMP);
891 
892 		break;
893 
894 #ifdef PRE7_COMPAT
895 	case PCIOCREAD_OLD:
896 	case PCIOCWRITE_OLD:
897 		io_old = (struct pci_io_old *)data;
898 		iodata.pi_sel.pc_domain = 0;
899 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
900 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
901 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
902 		iodata.pi_reg = io_old->pi_reg;
903 		iodata.pi_width = io_old->pi_width;
904 		iodata.pi_data = io_old->pi_data;
905 		data = (caddr_t)&iodata;
906 		/* FALLTHROUGH */
907 #endif
908 	case PCIOCREAD:
909 	case PCIOCWRITE:
910 		io = (struct pci_io *)data;
911 		switch(io->pi_width) {
912 		case 4:
913 		case 2:
914 		case 1:
915 			/* Make sure register is not negative and aligned. */
916 			if (io->pi_reg < 0 ||
917 			    io->pi_reg & (io->pi_width - 1)) {
918 				error = EINVAL;
919 				break;
920 			}
921 			/*
922 			 * Assume that the user-level bus number is
923 			 * in fact the physical PCI bus number.
924 			 * Look up the grandparent, i.e. the bridge device,
925 			 * so that we can issue configuration space cycles.
926 			 */
927 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
928 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
929 			    io->pi_sel.pc_func);
930 			if (pcidev) {
931 #ifdef PRE7_COMPAT
932 				if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD)
933 #else
934 				if (cmd == PCIOCWRITE)
935 #endif
936 					pci_write_config(pcidev,
937 							  io->pi_reg,
938 							  io->pi_data,
939 							  io->pi_width);
940 #ifdef PRE7_COMPAT
941 				else if (cmd == PCIOCREAD_OLD)
942 					io_old->pi_data =
943 						pci_read_config(pcidev,
944 							  io->pi_reg,
945 							  io->pi_width);
946 #endif
947 				else
948 					io->pi_data =
949 						pci_read_config(pcidev,
950 							  io->pi_reg,
951 							  io->pi_width);
952 				error = 0;
953 			} else {
954 #ifdef COMPAT_FREEBSD4
955 				if (cmd == PCIOCREAD_OLD) {
956 					io_old->pi_data = -1;
957 					error = 0;
958 				} else
959 #endif
960 					error = ENODEV;
961 			}
962 			break;
963 		default:
964 			error = EINVAL;
965 			break;
966 		}
967 		break;
968 
969 	case PCIOCGETBAR:
970 		bio = (struct pci_bar_io *)data;
971 
972 		/*
973 		 * Assume that the user-level bus number is
974 		 * in fact the physical PCI bus number.
975 		 */
976 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
977 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
978 		    bio->pbi_sel.pc_func);
979 		if (pcidev == NULL) {
980 			error = ENODEV;
981 			break;
982 		}
983 		pm = pci_find_bar(pcidev, bio->pbi_reg);
984 		if (pm == NULL) {
985 			error = EINVAL;
986 			break;
987 		}
988 		bio->pbi_base = pm->pm_value;
989 		bio->pbi_length = (pci_addr_t)1 << pm->pm_size;
990 		bio->pbi_enabled = pci_bar_enabled(pcidev, pm);
991 		error = 0;
992 		break;
993 	case PCIOCATTACHED:
994 		error = 0;
995 		io = (struct pci_io *)data;
996 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
997 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
998 		if (pcidev != NULL)
999 			io->pi_data = device_is_attached(pcidev);
1000 		else
1001 			error = ENODEV;
1002 		break;
1003 	case PCIOCLISTVPD:
1004 		lvio = (struct pci_list_vpd_io *)data;
1005 
1006 		/*
1007 		 * Assume that the user-level bus number is
1008 		 * in fact the physical PCI bus number.
1009 		 */
1010 		pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain,
1011 		    lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev,
1012 		    lvio->plvi_sel.pc_func);
1013 		if (pcidev == NULL) {
1014 			error = ENODEV;
1015 			break;
1016 		}
1017 		error = pci_list_vpd(pcidev, lvio);
1018 		break;
1019 	default:
1020 		error = ENOTTY;
1021 		break;
1022 	}
1023 
1024 	return (error);
1025 }
1026