xref: /dragonfly/sys/bus/pci/pci_user.c (revision 0ca59c34)
1 /*-
2  * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/pci/pci_user.c,v 1.22.2.4.2.1 2009/04/15 03:14:26 kensmith Exp $
27  */
28 
29 #include "opt_compat.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/linker.h>
36 #include <sys/fcntl.h>
37 #include <sys/conf.h>
38 #include <sys/kernel.h>
39 #include <sys/proc.h>
40 #include <sys/queue.h>
41 #include <sys/types.h>
42 
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
45 #include <vm/vm_extern.h>
46 
47 #include <sys/bus.h>
48 #include <sys/rman.h>
49 #include <sys/device.h>
50 #include <sys/pciio.h>
51 #include <bus/pci/pcireg.h>
52 #include <bus/pci/pcivar.h>
53 
54 #include "pcib_if.h"
55 #include "pci_if.h"
56 
57 /*
58  * This is the user interface to PCI configuration space.
59  */
60 
61 static d_open_t 	pci_open;
62 static d_close_t	pci_close;
63 static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
64 			       struct pci_conf *match_buf);
65 static d_ioctl_t	pci_ioctl;
66 
67 struct dev_ops pci_ops = {
68 	{ "pci", 0, 0 },
69 	.d_open =       pci_open,
70 	.d_close =      pci_close,
71 	.d_ioctl =      pci_ioctl,
72 };
73 
74 static int
75 pci_open(struct dev_open_args *ap)
76 {
77 	int oflags = ap->a_oflags;
78 
79 	if (oflags & FWRITE) {
80 		if (securelevel > 0)
81 			return (EPERM);
82 	}
83 
84 	return (0);
85 }
86 
87 static int
88 pci_close(struct dev_close_args *ap)
89 {
90 	return 0;
91 }
92 
93 /*
94  * Match a single pci_conf structure against an array of pci_match_conf
95  * structures.  The first argument, 'matches', is an array of num_matches
96  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
97  * structure that will be compared to every entry in the matches array.
98  * This function returns 1 on failure, 0 on success.
99  */
100 static int
101 pci_conf_match(struct pci_match_conf *matches, int num_matches,
102 	       struct pci_conf *match_buf)
103 {
104 	int i;
105 
106 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
107 		return(1);
108 
109 	for (i = 0; i < num_matches; i++) {
110 		/*
111 		 * I'm not sure why someone would do this...but...
112 		 */
113 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
114 			continue;
115 
116 		/*
117 		 * Look at each of the match flags.  If it's set, do the
118 		 * comparison.  If the comparison fails, we don't have a
119 		 * match, go on to the next item if there is one.
120 		 */
121 		if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0)
122 		 && (match_buf->pc_sel.pc_domain !=
123 		 matches[i].pc_sel.pc_domain))
124 			continue;
125 
126 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
127 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
128 			continue;
129 
130 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
131 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
132 			continue;
133 
134 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
135 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
136 			continue;
137 
138 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
139 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
140 			continue;
141 
142 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
143 		 && (match_buf->pc_device != matches[i].pc_device))
144 			continue;
145 
146 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
147 		 && (match_buf->pc_class != matches[i].pc_class))
148 			continue;
149 
150 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
151 		 && (match_buf->pd_unit != matches[i].pd_unit))
152 			continue;
153 
154 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
155 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
156 			     sizeof(match_buf->pd_name)) != 0))
157 			continue;
158 
159 		return(0);
160 	}
161 
162 	return(1);
163 }
164 
165 #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \
166     defined(COMPAT_FREEBSD6) || defined(__DragonFly__)
167 #define PRE7_COMPAT
168 
169 typedef enum {
170 	PCI_GETCONF_NO_MATCH_OLD	= 0x00,
171 	PCI_GETCONF_MATCH_BUS_OLD	= 0x01,
172 	PCI_GETCONF_MATCH_DEV_OLD	= 0x02,
173 	PCI_GETCONF_MATCH_FUNC_OLD	= 0x04,
174 	PCI_GETCONF_MATCH_NAME_OLD	= 0x08,
175 	PCI_GETCONF_MATCH_UNIT_OLD	= 0x10,
176 	PCI_GETCONF_MATCH_VENDOR_OLD	= 0x20,
177 	PCI_GETCONF_MATCH_DEVICE_OLD	= 0x40,
178 	PCI_GETCONF_MATCH_CLASS_OLD	= 0x80
179 } pci_getconf_flags_old;
180 
181 struct pcisel_old {
182 	u_int8_t	pc_bus;		/* bus number */
183 	u_int8_t	pc_dev;		/* device on this bus */
184 	u_int8_t	pc_func;	/* function on this device */
185 };
186 
187 struct pci_conf_old {
188 	struct pcisel_old pc_sel;	/* bus+slot+function */
189 	u_int8_t	pc_hdr;		/* PCI header type */
190 	u_int16_t	pc_subvendor;	/* card vendor ID */
191 	u_int16_t	pc_subdevice;	/* card device ID, assigned by
192 					   card vendor */
193 	u_int16_t	pc_vendor;	/* chip vendor ID */
194 	u_int16_t	pc_device;	/* chip device ID, assigned by
195 					   chip vendor */
196 	u_int8_t	pc_class;	/* chip PCI class */
197 	u_int8_t	pc_subclass;	/* chip PCI subclass */
198 	u_int8_t	pc_progif;	/* chip PCI programming interface */
199 	u_int8_t	pc_revid;	/* chip revision ID */
200 	char		pd_name[PCI_MAXNAMELEN + 1];  /* device name */
201 	u_long		pd_unit;	/* device unit number */
202 };
203 
204 struct pci_match_conf_old {
205 	struct pcisel_old	pc_sel;		/* bus+slot+function */
206 	char			pd_name[PCI_MAXNAMELEN + 1];  /* device name */
207 	u_long			pd_unit;	/* Unit number */
208 	u_int16_t		pc_vendor;	/* PCI Vendor ID */
209 	u_int16_t		pc_device;	/* PCI Device ID */
210 	u_int8_t		pc_class;	/* PCI class */
211 	pci_getconf_flags_old	flags;		/* Matching expression */
212 };
213 
214 struct pci_io_old {
215 	struct pcisel_old pi_sel;	/* device to operate on */
216 	int		pi_reg;		/* configuration register to examine */
217 	int		pi_width;	/* width (in bytes) of read or write */
218 	u_int32_t	pi_data;	/* data to write or result of read */
219 };
220 
221 #define	PCIOCGETCONF_OLD	_IOWR('p', 1, struct pci_conf_io)
222 #define	PCIOCREAD_OLD		_IOWR('p', 2, struct pci_io_old)
223 #define	PCIOCWRITE_OLD		_IOWR('p', 3, struct pci_io_old)
224 
225 static int	pci_conf_match_old(struct pci_match_conf_old *matches,
226 		    int num_matches, struct pci_conf *match_buf);
227 
228 static int
229 pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
230     struct pci_conf *match_buf)
231 {
232 	int i;
233 
234 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
235 		return(1);
236 
237 	for (i = 0; i < num_matches; i++) {
238 		if (match_buf->pc_sel.pc_domain != 0)
239 			continue;
240 
241 		/*
242 		 * I'm not sure why someone would do this...but...
243 		 */
244 		if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
245 			continue;
246 
247 		/*
248 		 * Look at each of the match flags.  If it's set, do the
249 		 * comparison.  If the comparison fails, we don't have a
250 		 * match, go on to the next item if there is one.
251 		 */
252 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
253 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
254 			continue;
255 
256 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
257 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
258 			continue;
259 
260 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
261 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
262 			continue;
263 
264 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
265 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
266 			continue;
267 
268 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
269 		 && (match_buf->pc_device != matches[i].pc_device))
270 			continue;
271 
272 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
273 		 && (match_buf->pc_class != matches[i].pc_class))
274 			continue;
275 
276 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
277 		 && (match_buf->pd_unit != matches[i].pd_unit))
278 			continue;
279 
280 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
281 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
282 			     sizeof(match_buf->pd_name)) != 0))
283 			continue;
284 
285 		return(0);
286 	}
287 
288 	return(1);
289 }
290 
291 #endif
292 
293 static int
294 pci_ioctl(struct dev_ioctl_args *ap)
295 {
296 	device_t pcidev, brdev;
297 	void *confdata;
298 	const char *name;
299 	struct devlist *devlist_head;
300 	struct pci_conf_io *cio;
301 	struct pci_devinfo *dinfo;
302 	struct pci_io *io;
303 	struct pci_bar_io *bio;
304 	struct pci_match_conf *pattern_buf;
305 	struct resource_list_entry *rle;
306 	uint32_t value;
307 	size_t confsz, iolen, pbufsz;
308 	int error, ionum, i, num_patterns;
309 #ifdef PRE7_COMPAT
310 	struct pci_conf_old conf_old;
311 	struct pci_io iodata;
312 	struct pci_io_old *io_old;
313 	struct pci_match_conf_old *pattern_buf_old;
314 
315 	io_old = NULL;
316 	pattern_buf_old = NULL;
317 
318 	if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR &&
319 	    ap->a_cmd != PCIOCGETCONF && ap->a_cmd != PCIOCGETCONF_OLD)
320 		return EPERM;
321 #else
322 	if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR && ap->a_cmd != PCIOCGETCONF)
323 		return EPERM;
324 #endif
325 
326 	switch(ap->a_cmd) {
327 #ifdef PRE7_COMPAT
328 	case PCIOCGETCONF_OLD:
329 		/* FALLTHROUGH */
330 #endif
331 	case PCIOCGETCONF:
332 		cio = (struct pci_conf_io *)ap->a_data;
333 
334 		pattern_buf = NULL;
335 		num_patterns = 0;
336 		dinfo = NULL;
337 
338 		cio->num_matches = 0;
339 
340 		/*
341 		 * If the user specified an offset into the device list,
342 		 * but the list has changed since they last called this
343 		 * ioctl, tell them that the list has changed.  They will
344 		 * have to get the list from the beginning.
345 		 */
346 		if ((cio->offset != 0)
347 		 && (cio->generation != pci_generation)){
348 			cio->status = PCI_GETCONF_LIST_CHANGED;
349 			error = 0;
350 			break;
351 		}
352 
353 		/*
354 		 * Check to see whether the user has asked for an offset
355 		 * past the end of our list.
356 		 */
357 		if (cio->offset >= pci_numdevs) {
358 			cio->status = PCI_GETCONF_LAST_DEVICE;
359 			error = 0;
360 			break;
361 		}
362 
363 		/* get the head of the device queue */
364 		devlist_head = &pci_devq;
365 
366 		/*
367 		 * Determine how much room we have for pci_conf structures.
368 		 * Round the user's buffer size down to the nearest
369 		 * multiple of sizeof(struct pci_conf) in case the user
370 		 * didn't specify a multiple of that size.
371 		 */
372 #ifdef PRE7_COMPAT
373 		if (ap->a_cmd == PCIOCGETCONF_OLD)
374 			confsz = sizeof(struct pci_conf_old);
375 		else
376 #endif
377 			confsz = sizeof(struct pci_conf);
378 		iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
379 		    pci_numdevs * confsz);
380 
381 		/*
382 		 * Since we know that iolen is a multiple of the size of
383 		 * the pciconf union, it's okay to do this.
384 		 */
385 		ionum = iolen / confsz;
386 
387 		/*
388 		 * If this test is true, the user wants the pci_conf
389 		 * structures returned to match the supplied entries.
390 		 */
391 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
392 		 && (cio->pat_buf_len > 0)) {
393 			/*
394 			 * pat_buf_len needs to be:
395 			 * num_patterns * sizeof(struct pci_match_conf)
396 			 * While it is certainly possible the user just
397 			 * allocated a large buffer, but set the number of
398 			 * matches correctly, it is far more likely that
399 			 * their kernel doesn't match the userland utility
400 			 * they're using.  It's also possible that the user
401 			 * forgot to initialize some variables.  Yes, this
402 			 * may be overly picky, but I hazard to guess that
403 			 * it's far more likely to just catch folks that
404 			 * updated their kernel but not their userland.
405 			 */
406 #ifdef PRE7_COMPAT
407 			if (ap->a_cmd == PCIOCGETCONF_OLD)
408 				pbufsz = sizeof(struct pci_match_conf_old);
409 			else
410 #endif
411 				pbufsz = sizeof(struct pci_match_conf);
412 			if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
413 				/* The user made a mistake, return an error. */
414 				cio->status = PCI_GETCONF_ERROR;
415 				error = EINVAL;
416 				break;
417 			}
418 
419 			/*
420 			 * Allocate a buffer to hold the patterns.
421 			 */
422 #ifdef PRE7_COMPAT
423 			if (ap->a_cmd == PCIOCGETCONF_OLD) {
424 				pattern_buf_old = kmalloc(cio->pat_buf_len,
425 				    M_TEMP, M_WAITOK);
426 				error = copyin(cio->patterns,
427 				    pattern_buf_old, cio->pat_buf_len);
428 			} else
429 #endif
430 			{
431 				pattern_buf = kmalloc(cio->pat_buf_len, M_TEMP,
432 				    M_WAITOK);
433 				error = copyin(cio->patterns, pattern_buf,
434 				    cio->pat_buf_len);
435 			}
436 			if (error != 0) {
437 				error = EINVAL;
438 				goto getconfexit;
439 			}
440 			num_patterns = cio->num_patterns;
441 		} else if ((cio->num_patterns > 0)
442 			|| (cio->pat_buf_len > 0)) {
443 			/*
444 			 * The user made a mistake, spit out an error.
445 			 */
446 			cio->status = PCI_GETCONF_ERROR;
447 			error = EINVAL;
448 			break;
449 		}
450 
451 		/*
452 		 * Go through the list of devices and copy out the devices
453 		 * that match the user's criteria.
454 		 */
455 		for (cio->num_matches = 0, error = 0, i = 0,
456 		     dinfo = STAILQ_FIRST(devlist_head);
457 		     (dinfo != NULL) && (cio->num_matches < ionum)
458 		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
459 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
460 
461 			if (i < cio->offset)
462 				continue;
463 
464 			/* Populate pd_name and pd_unit */
465 			name = NULL;
466 			if (dinfo->cfg.dev)
467 				name = device_get_name(dinfo->cfg.dev);
468 			if (name) {
469 				strncpy(dinfo->conf.pd_name, name,
470 					sizeof(dinfo->conf.pd_name));
471 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
472 				dinfo->conf.pd_unit =
473 					device_get_unit(dinfo->cfg.dev);
474 			} else {
475 				dinfo->conf.pd_name[0] = '\0';
476 				dinfo->conf.pd_unit = 0;
477 			}
478 
479 #ifdef PRE7_COMPAT
480 			if ((ap->a_cmd == PCIOCGETCONF_OLD &&
481 			    (pattern_buf_old == NULL ||
482 			    pci_conf_match_old(pattern_buf_old, num_patterns,
483 			    &dinfo->conf) == 0)) ||
484 			    (ap->a_cmd == PCIOCGETCONF &&
485 			    (pattern_buf == NULL ||
486 			    pci_conf_match(pattern_buf, num_patterns,
487 			    &dinfo->conf) == 0))) {
488 #else
489 			if (pattern_buf == NULL ||
490 			    pci_conf_match(pattern_buf, num_patterns,
491 			    &dinfo->conf) == 0) {
492 #endif
493 				/*
494 				 * If we've filled up the user's buffer,
495 				 * break out at this point.  Since we've
496 				 * got a match here, we'll pick right back
497 				 * up at the matching entry.  We can also
498 				 * tell the user that there are more matches
499 				 * left.
500 				 */
501 				if (cio->num_matches >= ionum)
502 					break;
503 
504 #ifdef PRE7_COMPAT
505 				if (ap->a_cmd == PCIOCGETCONF_OLD) {
506 					conf_old.pc_sel.pc_bus =
507 					    dinfo->conf.pc_sel.pc_bus;
508 					conf_old.pc_sel.pc_dev =
509 					    dinfo->conf.pc_sel.pc_dev;
510 					conf_old.pc_sel.pc_func =
511 					    dinfo->conf.pc_sel.pc_func;
512 					conf_old.pc_hdr = dinfo->conf.pc_hdr;
513 					conf_old.pc_subvendor =
514 					    dinfo->conf.pc_subvendor;
515 					conf_old.pc_subdevice =
516 					    dinfo->conf.pc_subdevice;
517 					conf_old.pc_vendor =
518 					    dinfo->conf.pc_vendor;
519 					conf_old.pc_device =
520 					    dinfo->conf.pc_device;
521 					conf_old.pc_class =
522 					    dinfo->conf.pc_class;
523 					conf_old.pc_subclass =
524 					    dinfo->conf.pc_subclass;
525 					conf_old.pc_progif =
526 					    dinfo->conf.pc_progif;
527 					conf_old.pc_revid =
528 					    dinfo->conf.pc_revid;
529 					strncpy(conf_old.pd_name,
530 					    dinfo->conf.pd_name,
531 					    sizeof(conf_old.pd_name));
532 					conf_old.pd_name[PCI_MAXNAMELEN] = 0;
533 					conf_old.pd_unit =
534 					    dinfo->conf.pd_unit;
535 					confdata = &conf_old;
536 				} else
537 #endif
538 					confdata = &dinfo->conf;
539 				/* Only if we can copy it out do we count it. */
540 				if (!(error = copyout(confdata,
541 				    (caddr_t)cio->matches +
542 				    confsz * cio->num_matches, confsz)))
543 					cio->num_matches++;
544 			}
545 		}
546 
547 		/*
548 		 * Set the pointer into the list, so if the user is getting
549 		 * n records at a time, where n < pci_numdevs,
550 		 */
551 		cio->offset = i;
552 
553 		/*
554 		 * Set the generation, the user will need this if they make
555 		 * another ioctl call with offset != 0.
556 		 */
557 		cio->generation = pci_generation;
558 
559 		/*
560 		 * If this is the last device, inform the user so he won't
561 		 * bother asking for more devices.  If dinfo isn't NULL, we
562 		 * know that there are more matches in the list because of
563 		 * the way the traversal is done.
564 		 */
565 		if (dinfo == NULL)
566 			cio->status = PCI_GETCONF_LAST_DEVICE;
567 		else
568 			cio->status = PCI_GETCONF_MORE_DEVS;
569 
570 getconfexit:
571 		if (pattern_buf != NULL)
572 			kfree(pattern_buf, M_TEMP);
573 #ifdef PRE7_COMPAT
574 		if (pattern_buf_old != NULL)
575 			kfree(pattern_buf_old, M_TEMP);
576 #endif
577 
578 		break;
579 
580 #ifdef PRE7_COMPAT
581 	case PCIOCREAD_OLD:
582 	case PCIOCWRITE_OLD:
583 		io_old = (struct pci_io_old *)ap->a_data;
584 		iodata.pi_sel.pc_domain = 0;
585 		iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
586 		iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
587 		iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
588 		iodata.pi_reg = io_old->pi_reg;
589 		iodata.pi_width = io_old->pi_width;
590 		iodata.pi_data = io_old->pi_data;
591 		ap->a_data = (caddr_t)&iodata;
592 		/* FALLTHROUGH */
593 #endif
594 	case PCIOCREAD:
595 	case PCIOCWRITE:
596 		io = (struct pci_io *)ap->a_data;
597 		switch(io->pi_width) {
598 		case 4:
599 		case 2:
600 		case 1:
601 			/* Make sure register is not negative and aligned. */
602 			if (io->pi_reg < 0 ||
603 			    io->pi_reg & (io->pi_width - 1)) {
604 				error = EINVAL;
605 				break;
606 			}
607 			/*
608 			 * Assume that the user-level bus number is
609 			 * in fact the physical PCI bus number.
610 			 * Look up the grandparent, i.e. the bridge device,
611 			 * so that we can issue configuration space cycles.
612 			 */
613 			pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
614 			    io->pi_sel.pc_bus, io->pi_sel.pc_dev,
615 			    io->pi_sel.pc_func);
616 			if (pcidev) {
617 				brdev = device_get_parent(
618 				    device_get_parent(pcidev));
619 
620 #ifdef PRE7_COMPAT
621 				if (ap->a_cmd == PCIOCWRITE || ap->a_cmd == PCIOCWRITE_OLD)
622 #else
623 				if (ap->a_cmd == PCIOCWRITE)
624 #endif
625 					PCIB_WRITE_CONFIG(brdev,
626 							  io->pi_sel.pc_bus,
627 							  io->pi_sel.pc_dev,
628 							  io->pi_sel.pc_func,
629 							  io->pi_reg,
630 							  io->pi_data,
631 							  io->pi_width);
632 #ifdef PRE7_COMPAT
633 				else if (ap->a_cmd == PCIOCREAD_OLD)
634 					io_old->pi_data =
635 						PCIB_READ_CONFIG(brdev,
636 							  io->pi_sel.pc_bus,
637 							  io->pi_sel.pc_dev,
638 							  io->pi_sel.pc_func,
639 							  io->pi_reg,
640 							  io->pi_width);
641 #endif
642 				else
643 					io->pi_data =
644 						PCIB_READ_CONFIG(brdev,
645 							  io->pi_sel.pc_bus,
646 							  io->pi_sel.pc_dev,
647 							  io->pi_sel.pc_func,
648 							  io->pi_reg,
649 							  io->pi_width);
650 				error = 0;
651 			} else {
652 #ifdef COMPAT_FREEBSD4
653 				if (cmd == PCIOCREAD_OLD) {
654 					io_old->pi_data = -1;
655 					error = 0;
656 				} else
657 #endif
658 					error = ENODEV;
659 			}
660 			break;
661 		default:
662 			error = EINVAL;
663 			break;
664 		}
665 		break;
666 
667 	case PCIOCGETBAR:
668 		bio = (struct pci_bar_io *)ap->a_data;
669 
670 		/*
671 		 * Assume that the user-level bus number is
672 		 * in fact the physical PCI bus number.
673 		 */
674 		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
675 		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
676 		    bio->pbi_sel.pc_func);
677 		if (pcidev == NULL) {
678 			error = ENODEV;
679 			break;
680 		}
681 		dinfo = device_get_ivars(pcidev);
682 
683 		/*
684 		 * Look for a resource list entry matching the requested BAR.
685 		 *
686 		 * XXX: This will not find BARs that are not initialized, but
687 		 * maybe that is ok?
688 		 */
689 		rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
690 		    bio->pbi_reg);
691 		if (rle == NULL)
692 			rle = resource_list_find(&dinfo->resources,
693 			    SYS_RES_IOPORT, bio->pbi_reg);
694 		if (rle == NULL || rle->res == NULL) {
695 			error = EINVAL;
696 			break;
697 		}
698 
699 		/*
700 		 * Ok, we have a resource for this BAR.  Read the lower
701 		 * 32 bits to get any flags.
702 		 */
703 		value = pci_read_config(pcidev, bio->pbi_reg, 4);
704 		if (PCI_BAR_MEM(value)) {
705 			if (rle->type != SYS_RES_MEMORY) {
706 				error = EINVAL;
707 				break;
708 			}
709 			value &= ~PCIM_BAR_MEM_BASE;
710 		} else {
711 			if (rle->type != SYS_RES_IOPORT) {
712 				error = EINVAL;
713 				break;
714 			}
715 			value &= ~PCIM_BAR_IO_BASE;
716 		}
717 		bio->pbi_base = rman_get_start(rle->res) | value;
718 		bio->pbi_length = rman_get_size(rle->res);
719 
720 		/*
721 		 * Check the command register to determine if this BAR
722 		 * is enabled.
723 		 */
724 		value = pci_read_config(pcidev, PCIR_COMMAND, 2);
725 		if (rle->type == SYS_RES_MEMORY)
726 			bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0;
727 		else
728 			bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0;
729 		error = 0;
730 		break;
731 	case PCIOCATTACHED:
732 		error = 0;
733 		io = (struct pci_io *)ap->a_data;
734 		pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
735 				       io->pi_sel.pc_dev, io->pi_sel.pc_func);
736 		if (pcidev != NULL)
737 			io->pi_data = device_is_attached(pcidev);
738 		else
739 			error = ENODEV;
740 		break;
741 	default:
742 		error = ENOTTY;
743 		break;
744 	}
745 
746 	return (error);
747 }
748