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