xref: /dragonfly/sys/bus/pci/pci_user.c (revision 857fcb57d0e5e7f20e3d0ef58163c718377e363e)
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 <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/malloc.h>
32 #include <sys/module.h>
33 #include <sys/linker.h>
34 #include <sys/fcntl.h>
35 #include <sys/caps.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/queue.h>
40 #include <sys/types.h>
41 
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44 #include <vm/vm_extern.h>
45 
46 #include <sys/bus.h>
47 #include <sys/rman.h>
48 #include <sys/device.h>
49 #include <sys/pciio.h>
50 #include <bus/pci/pcireg.h>
51 #include <bus/pci/pcivar.h>
52 
53 #include "pcib_if.h"
54 #include "pci_if.h"
55 
56 /*
57  * This is the user interface to PCI configuration space.
58  */
59 static struct lwkt_token pci_token = LWKT_TOKEN_INITIALIZER(pci_token);
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, D_MPSAFE },
69           .d_open =       pci_open,
70           .d_close =      pci_close,
71           .d_ioctl =      pci_ioctl,
72 };
73 
74 static int
pci_open(struct dev_open_args * ap)75 pci_open(struct dev_open_args *ap)
76 {
77           int oflags = ap->a_oflags;
78 
79           /*
80            * Disallow access to disk volumes if RESTRICTEDROOT
81            */
82           if (caps_priv_check_self(SYSCAP_RESTRICTEDROOT))
83                     return (EPERM);
84 
85           if (oflags & FWRITE) {
86                     if (securelevel > 0)
87                               return (EPERM);
88           }
89 
90           return (0);
91 }
92 
93 static int
pci_close(struct dev_close_args * ap)94 pci_close(struct dev_close_args *ap)
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
pci_conf_match(struct pci_match_conf * matches,int num_matches,struct pci_conf * match_buf)107 pci_conf_match(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) || defined(__DragonFly__)
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 #define   PCIOCGETCONF_OLD    _IOWR('p', 1, struct pci_conf_io)
228 #define   PCIOCREAD_OLD                 _IOWR('p', 2, struct pci_io_old)
229 #define   PCIOCWRITE_OLD                _IOWR('p', 3, struct pci_io_old)
230 
231 static int          pci_conf_match_old(struct pci_match_conf_old *matches,
232                         int num_matches, struct pci_conf *match_buf);
233 
234 static int
pci_conf_match_old(struct pci_match_conf_old * matches,int num_matches,struct pci_conf * match_buf)235 pci_conf_match_old(struct pci_match_conf_old *matches, int num_matches,
236     struct pci_conf *match_buf)
237 {
238           int i;
239 
240           if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
241                     return(1);
242 
243           for (i = 0; i < num_matches; i++) {
244                     if (match_buf->pc_sel.pc_domain != 0)
245                               continue;
246 
247                     /*
248                      * I'm not sure why someone would do this...but...
249                      */
250                     if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
251                               continue;
252 
253                     /*
254                      * Look at each of the match flags.  If it's set, do the
255                      * comparison.  If the comparison fails, we don't have a
256                      * match, go on to the next item if there is one.
257                      */
258                     if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0)
259                      && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
260                               continue;
261 
262                     if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0)
263                      && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
264                               continue;
265 
266                     if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0)
267                      && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
268                               continue;
269 
270                     if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0)
271                      && (match_buf->pc_vendor != matches[i].pc_vendor))
272                               continue;
273 
274                     if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0)
275                      && (match_buf->pc_device != matches[i].pc_device))
276                               continue;
277 
278                     if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0)
279                      && (match_buf->pc_class != matches[i].pc_class))
280                               continue;
281 
282                     if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0)
283                      && (match_buf->pd_unit != matches[i].pd_unit))
284                               continue;
285 
286                     if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0)
287                      && (strncmp(matches[i].pd_name, match_buf->pd_name,
288                                    sizeof(match_buf->pd_name)) != 0))
289                               continue;
290 
291                     return(0);
292           }
293 
294           return(1);
295 }
296 
297 #endif
298 
299 static int
pci_ioctl(struct dev_ioctl_args * ap)300 pci_ioctl(struct dev_ioctl_args *ap)
301 {
302           device_t pcidev, brdev;
303           void *confdata;
304           const char *name;
305           struct devlist *devlist_head;
306           struct pci_conf_io *cio;
307           struct pci_devinfo *dinfo;
308           struct pci_io *io;
309           struct pci_bar_io *bio;
310           struct pci_match_conf *pattern_buf;
311           struct resource_list_entry *rle;
312           uint32_t value;
313           size_t confsz, iolen, pbufsz;
314           int error, ionum, i, num_patterns;
315 #ifdef PRE7_COMPAT
316           struct pci_conf_old conf_old;
317           struct pci_io iodata;
318           struct pci_io_old *io_old;
319           struct pci_match_conf_old *pattern_buf_old;
320 
321           io_old = NULL;
322           pattern_buf_old = NULL;
323 
324           if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR &&
325               ap->a_cmd != PCIOCGETCONF && ap->a_cmd != PCIOCGETCONF_OLD)
326                     return EPERM;
327 #else
328           if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR && ap->a_cmd != PCIOCGETCONF)
329                     return EPERM;
330 #endif
331 
332           lwkt_gettoken(&pci_token);
333 
334           switch(ap->a_cmd) {
335 #ifdef PRE7_COMPAT
336           case PCIOCGETCONF_OLD:
337                     /* FALLTHROUGH */
338 #endif
339           case PCIOCGETCONF:
340                     cio = (struct pci_conf_io *)ap->a_data;
341 
342                     pattern_buf = NULL;
343                     num_patterns = 0;
344                     dinfo = NULL;
345 
346                     cio->num_matches = 0;
347 
348                     /*
349                      * If the user specified an offset into the device list,
350                      * but the list has changed since they last called this
351                      * ioctl, tell them that the list has changed.  They will
352                      * have to get the list from the beginning.
353                      */
354                     if ((cio->offset != 0)
355                      && (cio->generation != pci_generation)){
356                               cio->status = PCI_GETCONF_LIST_CHANGED;
357                               error = 0;
358                               break;
359                     }
360 
361                     /*
362                      * Check to see whether the user has asked for an offset
363                      * past the end of our list.
364                      */
365                     if (cio->offset >= pci_numdevs) {
366                               cio->status = PCI_GETCONF_LAST_DEVICE;
367                               error = 0;
368                               break;
369                     }
370 
371                     /* get the head of the device queue */
372                     devlist_head = &pci_devq;
373 
374                     /*
375                      * Determine how much room we have for pci_conf structures.
376                      * Round the user's buffer size down to the nearest
377                      * multiple of sizeof(struct pci_conf) in case the user
378                      * didn't specify a multiple of that size.
379                      */
380 #ifdef PRE7_COMPAT
381                     if (ap->a_cmd == PCIOCGETCONF_OLD)
382                               confsz = sizeof(struct pci_conf_old);
383                     else
384 #endif
385                               confsz = sizeof(struct pci_conf);
386                     iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz),
387                         pci_numdevs * confsz);
388 
389                     /*
390                      * Since we know that iolen is a multiple of the size of
391                      * the pciconf union, it's okay to do this.
392                      */
393                     ionum = iolen / confsz;
394 
395                     /*
396                      * If this test is true, the user wants the pci_conf
397                      * structures returned to match the supplied entries.
398                      */
399                     if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
400                      && (cio->pat_buf_len > 0)) {
401                               /*
402                                * pat_buf_len needs to be:
403                                * num_patterns * sizeof(struct pci_match_conf)
404                                * While it is certainly possible the user just
405                                * allocated a large buffer, but set the number of
406                                * matches correctly, it is far more likely that
407                                * their kernel doesn't match the userland utility
408                                * they're using.  It's also possible that the user
409                                * forgot to initialize some variables.  Yes, this
410                                * may be overly picky, but I hazard to guess that
411                                * it's far more likely to just catch folks that
412                                * updated their kernel but not their userland.
413                                */
414 #ifdef PRE7_COMPAT
415                               if (ap->a_cmd == PCIOCGETCONF_OLD)
416                                         pbufsz = sizeof(struct pci_match_conf_old);
417                               else
418 #endif
419                                         pbufsz = sizeof(struct pci_match_conf);
420                               if (cio->num_patterns * pbufsz != cio->pat_buf_len) {
421                                         /* The user made a mistake, return an error. */
422                                         cio->status = PCI_GETCONF_ERROR;
423                                         error = EINVAL;
424                                         break;
425                               }
426 
427                               /*
428                                * Allocate a buffer to hold the patterns.
429                                */
430 #ifdef PRE7_COMPAT
431                               if (ap->a_cmd == PCIOCGETCONF_OLD) {
432                                         pattern_buf_old = kmalloc(cio->pat_buf_len,
433                                             M_TEMP, M_WAITOK);
434                                         error = copyin(cio->patterns,
435                                             pattern_buf_old, cio->pat_buf_len);
436                               } else
437 #endif
438                               {
439                                         pattern_buf = kmalloc(cio->pat_buf_len, M_TEMP,
440                                             M_WAITOK);
441                                         error = copyin(cio->patterns, pattern_buf,
442                                             cio->pat_buf_len);
443                               }
444                               if (error != 0) {
445                                         error = EINVAL;
446                                         goto getconfexit;
447                               }
448                               num_patterns = cio->num_patterns;
449                     } else if ((cio->num_patterns > 0)
450                               || (cio->pat_buf_len > 0)) {
451                               /*
452                                * The user made a mistake, spit out an error.
453                                */
454                               cio->status = PCI_GETCONF_ERROR;
455                               error = EINVAL;
456                               break;
457                     }
458 
459                     /*
460                      * Go through the list of devices and copy out the devices
461                      * that match the user's criteria.
462                      */
463                     for (cio->num_matches = 0, error = 0, i = 0,
464                          dinfo = STAILQ_FIRST(devlist_head);
465                          (dinfo != NULL) && (cio->num_matches < ionum)
466                          && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
467                          dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
468 
469                               if (i < cio->offset)
470                                         continue;
471 
472                               /* Populate pd_name and pd_unit */
473                               name = NULL;
474                               if (dinfo->cfg.dev)
475                                         name = device_get_name(dinfo->cfg.dev);
476                               if (name) {
477                                         strncpy(dinfo->conf.pd_name, name,
478                                                   sizeof(dinfo->conf.pd_name));
479                                         dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
480                                         dinfo->conf.pd_unit =
481                                                   device_get_unit(dinfo->cfg.dev);
482                               } else {
483                                         dinfo->conf.pd_name[0] = '\0';
484                                         dinfo->conf.pd_unit = 0;
485                               }
486 
487 #ifdef PRE7_COMPAT
488                               if ((ap->a_cmd == PCIOCGETCONF_OLD &&
489                                   (pattern_buf_old == NULL ||
490                                   pci_conf_match_old(pattern_buf_old, num_patterns,
491                                   &dinfo->conf) == 0)) ||
492                                   (ap->a_cmd == PCIOCGETCONF &&
493                                   (pattern_buf == NULL ||
494                                   pci_conf_match(pattern_buf, num_patterns,
495                                   &dinfo->conf) == 0))) {
496 #else
497                               if (pattern_buf == NULL ||
498                                   pci_conf_match(pattern_buf, num_patterns,
499                                   &dinfo->conf) == 0) {
500 #endif
501                                         /*
502                                          * If we've filled up the user's buffer,
503                                          * break out at this point.  Since we've
504                                          * got a match here, we'll pick right back
505                                          * up at the matching entry.  We can also
506                                          * tell the user that there are more matches
507                                          * left.
508                                          */
509                                         if (cio->num_matches >= ionum)
510                                                   break;
511 
512 #ifdef PRE7_COMPAT
513                                         if (ap->a_cmd == PCIOCGETCONF_OLD) {
514                                                   conf_old.pc_sel.pc_bus =
515                                                       dinfo->conf.pc_sel.pc_bus;
516                                                   conf_old.pc_sel.pc_dev =
517                                                       dinfo->conf.pc_sel.pc_dev;
518                                                   conf_old.pc_sel.pc_func =
519                                                       dinfo->conf.pc_sel.pc_func;
520                                                   conf_old.pc_hdr = dinfo->conf.pc_hdr;
521                                                   conf_old.pc_subvendor =
522                                                       dinfo->conf.pc_subvendor;
523                                                   conf_old.pc_subdevice =
524                                                       dinfo->conf.pc_subdevice;
525                                                   conf_old.pc_vendor =
526                                                       dinfo->conf.pc_vendor;
527                                                   conf_old.pc_device =
528                                                       dinfo->conf.pc_device;
529                                                   conf_old.pc_class =
530                                                       dinfo->conf.pc_class;
531                                                   conf_old.pc_subclass =
532                                                       dinfo->conf.pc_subclass;
533                                                   conf_old.pc_progif =
534                                                       dinfo->conf.pc_progif;
535                                                   conf_old.pc_revid =
536                                                       dinfo->conf.pc_revid;
537                                                   strncpy(conf_old.pd_name,
538                                                       dinfo->conf.pd_name,
539                                                       sizeof(conf_old.pd_name));
540                                                   conf_old.pd_name[PCI_MAXNAMELEN] = 0;
541                                                   conf_old.pd_unit =
542                                                       dinfo->conf.pd_unit;
543                                                   confdata = &conf_old;
544                                         } else
545 #endif
546                                                   confdata = &dinfo->conf;
547                                         /* Only if we can copy it out do we count it. */
548                                         if (!(error = copyout(confdata,
549                                             (caddr_t)cio->matches +
550                                             confsz * cio->num_matches, confsz)))
551                                                   cio->num_matches++;
552                               }
553                     }
554 
555                     /*
556                      * Set the pointer into the list, so if the user is getting
557                      * n records at a time, where n < pci_numdevs,
558                      */
559                     cio->offset = i;
560 
561                     /*
562                      * Set the generation, the user will need this if they make
563                      * another ioctl call with offset != 0.
564                      */
565                     cio->generation = pci_generation;
566 
567                     /*
568                      * If this is the last device, inform the user so he won't
569                      * bother asking for more devices.  If dinfo isn't NULL, we
570                      * know that there are more matches in the list because of
571                      * the way the traversal is done.
572                      */
573                     if (dinfo == NULL)
574                               cio->status = PCI_GETCONF_LAST_DEVICE;
575                     else
576                               cio->status = PCI_GETCONF_MORE_DEVS;
577 
578 getconfexit:
579                     if (pattern_buf != NULL)
580                               kfree(pattern_buf, M_TEMP);
581 #ifdef PRE7_COMPAT
582                     if (pattern_buf_old != NULL)
583                               kfree(pattern_buf_old, M_TEMP);
584 #endif
585 
586                     break;
587 
588 #ifdef PRE7_COMPAT
589           case PCIOCREAD_OLD:
590           case PCIOCWRITE_OLD:
591                     io_old = (struct pci_io_old *)ap->a_data;
592                     iodata.pi_sel.pc_domain = 0;
593                     iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus;
594                     iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev;
595                     iodata.pi_sel.pc_func = io_old->pi_sel.pc_func;
596                     iodata.pi_reg = io_old->pi_reg;
597                     iodata.pi_width = io_old->pi_width;
598                     iodata.pi_data = io_old->pi_data;
599                     ap->a_data = (caddr_t)&iodata;
600                     /* FALLTHROUGH */
601 #endif
602           case PCIOCREAD:
603           case PCIOCWRITE:
604                     io = (struct pci_io *)ap->a_data;
605                     switch(io->pi_width) {
606                     case 4:
607                     case 2:
608                     case 1:
609                               /* Make sure register is not negative and aligned. */
610                               if (io->pi_reg < 0 ||
611                                   io->pi_reg & (io->pi_width - 1)) {
612                                         error = EINVAL;
613                                         break;
614                               }
615                               /*
616                                * Assume that the user-level bus number is
617                                * in fact the physical PCI bus number.
618                                * Look up the grandparent, i.e. the bridge device,
619                                * so that we can issue configuration space cycles.
620                                */
621                               pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
622                                   io->pi_sel.pc_bus, io->pi_sel.pc_dev,
623                                   io->pi_sel.pc_func);
624                               if (pcidev) {
625                                         brdev = device_get_parent(
626                                             device_get_parent(pcidev));
627 
628 #ifdef PRE7_COMPAT
629                                         if (ap->a_cmd == PCIOCWRITE || ap->a_cmd == PCIOCWRITE_OLD)
630 #else
631                                         if (ap->a_cmd == PCIOCWRITE)
632 #endif
633                                                   PCIB_WRITE_CONFIG(brdev,
634                                                                         io->pi_sel.pc_bus,
635                                                                         io->pi_sel.pc_dev,
636                                                                         io->pi_sel.pc_func,
637                                                                         io->pi_reg,
638                                                                         io->pi_data,
639                                                                         io->pi_width);
640 #ifdef PRE7_COMPAT
641                                         else if (ap->a_cmd == PCIOCREAD_OLD)
642                                                   io_old->pi_data =
643                                                             PCIB_READ_CONFIG(brdev,
644                                                                         io->pi_sel.pc_bus,
645                                                                         io->pi_sel.pc_dev,
646                                                                         io->pi_sel.pc_func,
647                                                                         io->pi_reg,
648                                                                         io->pi_width);
649 #endif
650                                         else
651                                                   io->pi_data =
652                                                             PCIB_READ_CONFIG(brdev,
653                                                                         io->pi_sel.pc_bus,
654                                                                         io->pi_sel.pc_dev,
655                                                                         io->pi_sel.pc_func,
656                                                                         io->pi_reg,
657                                                                         io->pi_width);
658                                         error = 0;
659                               } else {
660 #ifdef COMPAT_FREEBSD4
661                                         if (cmd == PCIOCREAD_OLD) {
662                                                   io_old->pi_data = -1;
663                                                   error = 0;
664                                         } else
665 #endif
666                                                   error = ENODEV;
667                               }
668                               break;
669                     default:
670                               error = EINVAL;
671                               break;
672                     }
673                     break;
674 
675           case PCIOCGETBAR:
676                     bio = (struct pci_bar_io *)ap->a_data;
677 
678                     /*
679                      * Assume that the user-level bus number is
680                      * in fact the physical PCI bus number.
681                      */
682                     pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
683                         bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
684                         bio->pbi_sel.pc_func);
685                     if (pcidev == NULL) {
686                               error = ENODEV;
687                               break;
688                     }
689                     dinfo = device_get_ivars(pcidev);
690 
691                     /*
692                      * Look for a resource list entry matching the requested BAR.
693                      *
694                      * XXX: This will not find BARs that are not initialized, but
695                      * maybe that is ok?
696                      */
697                     rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
698                         bio->pbi_reg);
699                     if (rle == NULL)
700                               rle = resource_list_find(&dinfo->resources,
701                                   SYS_RES_IOPORT, bio->pbi_reg);
702                     if (rle == NULL || rle->res == NULL) {
703                               error = EINVAL;
704                               break;
705                     }
706 
707                     /*
708                      * Ok, we have a resource for this BAR.  Read the lower
709                      * 32 bits to get any flags.
710                      */
711                     value = pci_read_config(pcidev, bio->pbi_reg, 4);
712                     if (PCI_BAR_MEM(value)) {
713                               if (rle->type != SYS_RES_MEMORY) {
714                                         error = EINVAL;
715                                         break;
716                               }
717                               value &= ~PCIM_BAR_MEM_BASE;
718                     } else {
719                               if (rle->type != SYS_RES_IOPORT) {
720                                         error = EINVAL;
721                                         break;
722                               }
723                               value &= ~PCIM_BAR_IO_BASE;
724                     }
725                     bio->pbi_base = rman_get_start(rle->res) | value;
726                     bio->pbi_length = rman_get_size(rle->res);
727 
728                     /*
729                      * Check the command register to determine if this BAR
730                      * is enabled.
731                      */
732                     value = pci_read_config(pcidev, PCIR_COMMAND, 2);
733                     if (rle->type == SYS_RES_MEMORY)
734                               bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0;
735                     else
736                               bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0;
737                     error = 0;
738                     break;
739           case PCIOCATTACHED:
740                     error = 0;
741                     io = (struct pci_io *)ap->a_data;
742                     pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
743                                                io->pi_sel.pc_dev, io->pi_sel.pc_func);
744                     if (pcidev != NULL)
745                               io->pi_data = device_is_attached(pcidev);
746                     else
747                               error = ENODEV;
748                     break;
749           default:
750                     error = ENOTTY;
751                     break;
752           }
753           lwkt_reltoken(&pci_token);
754 
755           return (error);
756 }
757