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