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