pci: Utilize device_getenv_int
[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_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 pcic_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 in bounds and aligned. */
602                         if (io->pi_reg < 0 ||
603                             io->pi_reg + io->pi_width > PCI_REGMAX + 1 ||
604                             io->pi_reg & (io->pi_width - 1)) {
605                                 error = EINVAL;
606                                 break;
607                         }
608                         /*
609                          * Assume that the user-level bus number is
610                          * in fact the physical PCI bus number.
611                          * Look up the grandparent, i.e. the bridge device,
612                          * so that we can issue configuration space cycles.
613                          */
614                         pcidev = pci_find_dbsf(io->pi_sel.pc_domain,
615                             io->pi_sel.pc_bus, io->pi_sel.pc_dev,
616                             io->pi_sel.pc_func);
617                         if (pcidev) {
618                                 brdev = device_get_parent(
619                                     device_get_parent(pcidev));
620
621 #ifdef PRE7_COMPAT
622                                 if (ap->a_cmd == PCIOCWRITE || ap->a_cmd == PCIOCWRITE_OLD)
623 #else
624                                 if (ap->a_cmd == PCIOCWRITE)
625 #endif
626                                         PCIB_WRITE_CONFIG(brdev,
627                                                           io->pi_sel.pc_bus,
628                                                           io->pi_sel.pc_dev,
629                                                           io->pi_sel.pc_func,
630                                                           io->pi_reg,
631                                                           io->pi_data,
632                                                           io->pi_width);
633 #ifdef PRE7_COMPAT
634                                 else if (ap->a_cmd == PCIOCREAD_OLD)
635                                         io_old->pi_data =
636                                                 PCIB_READ_CONFIG(brdev,
637                                                           io->pi_sel.pc_bus,
638                                                           io->pi_sel.pc_dev,
639                                                           io->pi_sel.pc_func,
640                                                           io->pi_reg,
641                                                           io->pi_width);
642 #endif
643                                 else
644                                         io->pi_data =
645                                                 PCIB_READ_CONFIG(brdev,
646                                                           io->pi_sel.pc_bus,
647                                                           io->pi_sel.pc_dev,
648                                                           io->pi_sel.pc_func,
649                                                           io->pi_reg,
650                                                           io->pi_width);
651                                 error = 0;
652                         } else {
653 #ifdef COMPAT_FREEBSD4
654                                 if (cmd == PCIOCREAD_OLD) {
655                                         io_old->pi_data = -1;
656                                         error = 0;
657                                 } else
658 #endif
659                                         error = ENODEV;
660                         }
661                         break;
662                 default:
663                         error = EINVAL;
664                         break;
665                 }
666                 break;
667
668         case PCIOCGETBAR:
669                 bio = (struct pci_bar_io *)ap->a_data;
670
671                 /*
672                  * Assume that the user-level bus number is
673                  * in fact the physical PCI bus number.
674                  */
675                 pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
676                     bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
677                     bio->pbi_sel.pc_func);
678                 if (pcidev == NULL) {
679                         error = ENODEV;
680                         break;
681                 }
682                 dinfo = device_get_ivars(pcidev);
683                 
684                 /*
685                  * Look for a resource list entry matching the requested BAR.
686                  *
687                  * XXX: This will not find BARs that are not initialized, but
688                  * maybe that is ok?
689                  */
690                 rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
691                     bio->pbi_reg);
692                 if (rle == NULL)
693                         rle = resource_list_find(&dinfo->resources,
694                             SYS_RES_IOPORT, bio->pbi_reg);
695                 if (rle == NULL || rle->res == NULL) {
696                         error = EINVAL;
697                         break;
698                 }
699
700                 /*
701                  * Ok, we have a resource for this BAR.  Read the lower
702                  * 32 bits to get any flags.
703                  */
704                 value = pci_read_config(pcidev, bio->pbi_reg, 4);
705                 if (PCI_BAR_MEM(value)) {
706                         if (rle->type != SYS_RES_MEMORY) {
707                                 error = EINVAL;
708                                 break;
709                         }
710                         value &= ~PCIM_BAR_MEM_BASE;
711                 } else {
712                         if (rle->type != SYS_RES_IOPORT) {
713                                 error = EINVAL;
714                                 break;
715                         }
716                         value &= ~PCIM_BAR_IO_BASE;
717                 }
718                 bio->pbi_base = rman_get_start(rle->res) | value;
719                 bio->pbi_length = rman_get_size(rle->res);
720
721                 /*
722                  * Check the command register to determine if this BAR
723                  * is enabled.
724                  */
725                 value = pci_read_config(pcidev, PCIR_COMMAND, 2);
726                 if (rle->type == SYS_RES_MEMORY)
727                         bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0;
728                 else
729                         bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0;
730                 error = 0;
731                 break;
732         case PCIOCATTACHED:
733                 error = 0;
734                 io = (struct pci_io *)ap->a_data;
735                 pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus,
736                                        io->pi_sel.pc_dev, io->pi_sel.pc_func);
737                 if (pcidev != NULL)
738                         io->pi_data = device_is_attached(pcidev);
739                 else
740                         error = ENODEV;
741                 break;
742         default:
743                 error = ENOTTY;
744                 break;
745         }
746
747         return (error);
748 }