OSDN Git Service

Merge branch 'master' into vga-arbiter
[android-x86/external-libpciaccess.git] / src / freebsd_pci.c
1 /*
2  * (C) Copyright Eric Anholt 2006
3  * (C) Copyright IBM Corporation 2006
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * on the rights to use, copy, modify, merge, publish, distribute, sub
10  * license, and/or sell copies of the Software, and to permit persons to whom
11  * the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
20  * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25
26 /**
27  * \file freebsd_pci.c
28  *
29  * Access the kernel PCI support using /dev/pci's ioctl and mmap interface.
30  *
31  * \author Eric Anholt <eric@anholt.net>
32  */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/pciio.h>
43 #include <sys/mman.h>
44 #include <sys/memrange.h>
45
46 #include "config.h"
47 #include "pciaccess.h"
48 #include "pciaccess_private.h"
49
50 #define PCIC_DISPLAY    0x03
51 #define PCIS_DISPLAY_VGA        0x00
52 #define PCIS_DISPLAY_XGA        0x01
53 #define PCIS_DISPLAY_3D         0x02
54 #define PCIS_DISPLAY_OTHER      0x80
55
56 /* Registers taken from pcireg.h */
57 #define PCIR_COMMAND    0x04
58 #define PCIM_CMD_PORTEN         0x0001
59 #define PCIM_CMD_MEMEN          0x0002
60 #define PCIR_BIOS       0x30
61 #define PCIM_BIOS_ENABLE        0x01
62 #define PCIM_BIOS_ADDR_MASK     0xfffff800
63
64 #define PCIR_BARS       0x10
65 #define PCIR_BAR(x)             (PCIR_BARS + (x) * 4)
66 #define PCI_BAR_IO(x)           (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE)
67 #define PCI_BAR_MEM(x)          (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE)
68 #define PCIM_BAR_MEM_TYPE       0x00000006
69 #define PCIM_BAR_MEM_64         4
70 #define PCIM_BAR_MEM_PREFETCH   0x00000008
71 #define PCIM_BAR_SPACE          0x00000001
72 #define PCIM_BAR_MEM_SPACE      0
73 #define PCIM_BAR_IO_SPACE       1
74
75 /**
76  * FreeBSD private pci_system structure that extends the base pci_system
77  * structure.
78  *
79  * It is initialized once and used as a global, just as pci_system is used.
80  */
81 _pci_hidden
82 struct freebsd_pci_system {
83     /* This must be the first entry in the structure, as pci_system_cleanup()
84      * frees pci_sys.
85      */
86     struct pci_system pci_sys;
87
88     int pcidev; /**< fd for /dev/pci */
89 } *freebsd_pci_sys;
90
91 /**
92  * Map a memory region for a device using /dev/mem.
93  * 
94  * \param dev   Device whose memory region is to be mapped.
95  * \param map   Parameters of the mapping that is to be created.
96  * 
97  * \return
98  * Zero on success or an \c errno value on failure.
99  */
100 static int
101 pci_device_freebsd_map_range(struct pci_device *dev,
102                              struct pci_device_mapping *map)
103 {
104     const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 
105         ? (PROT_READ | PROT_WRITE) : PROT_READ;
106     struct mem_range_desc mrd;
107     struct mem_range_op mro;
108
109     int fd, err = 0;
110
111     fd = open("/dev/mem", O_RDWR);
112     if (fd == -1)
113         return errno;
114
115     map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base);
116
117     if (map->memory == MAP_FAILED) {
118         err = errno;
119     }
120
121     mrd.mr_base = map->base;
122     mrd.mr_len = map->size;
123     strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner));
124     if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE)
125         mrd.mr_flags = MDF_WRITEBACK;
126     else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
127         mrd.mr_flags = MDF_WRITECOMBINE;
128     else
129         mrd.mr_flags = MDF_UNCACHEABLE;
130     mro.mo_desc = &mrd;
131     mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
132
133     /* No need to set an MTRR if it's the default mode. */
134     if (mrd.mr_flags != MDF_UNCACHEABLE) {
135         if (ioctl(fd, MEMRANGE_SET, &mro)) {
136             fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno));
137         }
138     }
139
140     close(fd);
141
142     return err;
143 }
144
145 static int
146 pci_device_freebsd_unmap_range( struct pci_device *dev,
147                                 struct pci_device_mapping *map )
148 {
149     struct mem_range_desc mrd;
150     struct mem_range_op mro;
151     int fd;
152
153     if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
154         (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE))
155     {
156         fd = open("/dev/mem", O_RDWR);
157         if (fd != -1) {
158             mrd.mr_base = map->base;
159             mrd.mr_len = map->size;
160             strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner));
161             mrd.mr_flags = MDF_UNCACHEABLE;
162             mro.mo_desc = &mrd;
163             mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
164
165             if (ioctl(fd, MEMRANGE_SET, &mro)) {
166                 fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno));
167             }
168
169             close(fd);
170         } else {
171             fprintf(stderr, "Failed to open /dev/mem\n");
172         }
173     }
174
175     return pci_device_generic_unmap_range(dev, map);
176 }
177
178 static int
179 pci_device_freebsd_read( struct pci_device * dev, void * data,
180                          pciaddr_t offset, pciaddr_t size,
181                          pciaddr_t * bytes_read )
182 {
183     struct pci_io io;
184
185 #if HAVE_PCI_IO_PC_DOMAIN
186     io.pi_sel.pc_domain = dev->domain;
187 #endif
188     io.pi_sel.pc_bus = dev->bus;
189     io.pi_sel.pc_dev = dev->dev;
190     io.pi_sel.pc_func = dev->func;
191
192     *bytes_read = 0;
193     while ( size > 0 ) {
194         int toread = (size < 4) ? size : 4;
195
196         /* Only power of two allowed. */
197         if (toread == 3)
198             toread = 2;
199
200         io.pi_reg = offset;
201         io.pi_width = toread;
202
203         if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) 
204             return errno;
205
206         memcpy(data, &io.pi_data, toread );
207
208         offset += toread;
209         data = (char *)data + toread;
210         size -= toread;
211         *bytes_read += toread;
212     }
213
214     return 0;
215 }
216
217
218 static int
219 pci_device_freebsd_write( struct pci_device * dev, const void * data,
220                           pciaddr_t offset, pciaddr_t size,
221                           pciaddr_t * bytes_written )
222 {
223     struct pci_io io;
224
225 #if HAVE_PCI_IO_PC_DOMAIN
226     io.pi_sel.pc_domain = dev->domain;
227 #endif
228     io.pi_sel.pc_bus = dev->bus;
229     io.pi_sel.pc_dev = dev->dev;
230     io.pi_sel.pc_func = dev->func;
231
232     *bytes_written = 0;
233     while ( size > 0 ) {
234         int towrite = (size < 4 ? size : 4);
235
236         /* Only power of two allowed. */
237         if (towrite == 3)
238             towrite = 2;
239
240         io.pi_reg = offset;
241         io.pi_width = towrite;
242         memcpy( &io.pi_data, data, towrite );
243
244         if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) 
245             return errno;
246
247         offset += towrite;
248         data = (char *)data + towrite;
249         size -= towrite;
250         *bytes_written += towrite;
251     }
252
253     return 0;
254 }
255
256 /**
257  * Read a VGA rom using the 0xc0000 mapping.
258  *
259  * This function should be extended to handle access through PCI resources,
260  * which should be more reliable when available.
261  */
262 static int
263 pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer )
264 {
265     struct pci_device_private *priv = (struct pci_device_private *) dev;
266     void *bios;
267     pciaddr_t rom_base;
268     uint32_t rom;
269     uint16_t reg;
270     int pci_rom, memfd;
271
272     if ( ( dev->device_class & 0x00ffff00 ) !=
273          ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) )
274     {
275         return ENOSYS;
276     }
277
278     if (priv->rom_base == 0) {
279 #if defined(__amd64__) || defined(__i386__)
280         rom_base = 0xc0000;
281         pci_rom = 0;
282 #else
283         return ENOSYS;
284 #endif
285     } else {
286         rom_base = priv->rom_base;
287         pci_rom = 1;
288
289         pci_device_cfg_read_u16( dev, &reg, PCIR_COMMAND );
290         pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND );
291         pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS );
292         pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS );
293     }
294
295     printf("Using rom_base = 0x%lx\n", (long)rom_base);
296     memfd = open( "/dev/mem", O_RDONLY );
297     if ( memfd == -1 )
298         return errno;
299
300     bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base );
301     if ( bios == MAP_FAILED ) {
302         close( memfd );
303         return errno;
304     }
305
306     memcpy( buffer, bios, dev->rom_size );
307
308     munmap( bios, dev->rom_size );
309     close( memfd );
310
311     if (pci_rom) {
312         pci_device_cfg_write_u32( dev, PCIR_BIOS, rom );
313         pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg );
314     }
315
316     return 0;
317 }
318
319 /** Returns the number of regions (base address registers) the device has */
320
321 static int
322 pci_device_freebsd_get_num_regions( struct pci_device * dev )
323 {
324     struct pci_device_private *priv = (struct pci_device_private *) dev;
325
326     switch (priv->header_type) {
327     case 0:
328         return 6;
329     case 1:
330         return 2;
331     case 2:
332         return 1;
333     default:
334         printf("unknown header type %02x\n", priv->header_type);
335         return 0;
336     }
337 }
338
339 #ifdef PCIOCGETBAR
340
341 static int
342 pci_device_freebsd_probe( struct pci_device * dev )
343 {
344     struct pci_device_private *priv = (struct pci_device_private *) dev;
345     struct pci_bar_io bar;
346     uint8_t irq;
347     int err, i;
348
349 #if HAVE_PCI_IO_PC_DOMAIN
350     bar.pbi_sel.pc_domain = dev->domain;
351 #endif
352     bar.pbi_sel.pc_bus = dev->bus;
353     bar.pbi_sel.pc_dev = dev->dev;
354     bar.pbi_sel.pc_func = dev->func;
355
356
357     /* Many of the fields were filled in during initial device enumeration.
358      * At this point, we need to fill in regions, rom_size, and irq.
359      */
360
361     err = pci_device_cfg_read_u8( dev, &irq, 60 );
362     if (err)
363         return errno;
364     dev->irq = irq;
365
366     for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) {
367         bar.pbi_reg = PCIR_BAR(i);
368         if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) 
369             continue;
370
371         if (PCI_BAR_IO(bar.pbi_base))
372             dev->regions[i].is_IO = 1;
373
374         if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64)
375             dev->regions[i].is_64 = 1;
376
377         if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
378             dev->regions[i].is_prefetchable = 1;
379
380         dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf);
381         dev->regions[i].size = bar.pbi_length;
382     }
383
384     /* If it's a VGA device, set up the rom size for read_rom using the
385      * 0xc0000 mapping.
386      */
387      if ((dev->device_class & 0x00ffff00) ==
388         ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) {
389              dev->rom_size = 64 * 1024;
390      }
391
392      return 0;
393 }
394
395 #else
396
397 /** Masks out the flag bigs of the base address register value */
398 static uint32_t
399 get_map_base( uint32_t val )
400 {
401     if (val & 0x01)
402         return val & ~0x03;
403     else
404         return val & ~0x0f;
405 }
406
407 /** Returns the size of a region based on the all-ones test value */
408 static int
409 get_test_val_size( uint32_t testval )
410 {
411     if (testval == 0)
412         return 0;
413
414     /* Mask out the flag bits */
415     testval = get_map_base( testval );
416
417     return 1 << (ffs(testval) - 1);
418 }
419
420 /**
421  * Sets the address and size information for the region from config space
422  * registers.
423  *
424  * This would be much better provided by a kernel interface.
425  *
426  * \return 0 on success, or an errno value.
427  */
428 static int
429 pci_device_freebsd_get_region_info( struct pci_device * dev, int region,
430                                     int bar )
431 {
432     uint32_t addr, testval;
433     uint16_t cmd;
434     int err;
435
436     /* Get the base address */
437     err = pci_device_cfg_read_u32( dev, &addr, bar );
438     if (err != 0)
439         return err;
440
441     /*
442      * We are going to be doing evil things to the registers here
443      * so disable them via the command register first. 
444      */
445     err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND );
446     if (err != 0)
447         return err;
448
449     err = pci_device_cfg_write_u16( dev,
450         cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN),
451         PCIR_COMMAND );
452     if (err != 0)
453         return err;
454
455     /* Test write all ones to the register, then restore it. */
456     err = pci_device_cfg_write_u32( dev, 0xffffffff, bar );
457     if (err != 0)
458         return err;
459     err = pci_device_cfg_read_u32( dev, &testval, bar );
460     if (err != 0)
461         return err;
462     err = pci_device_cfg_write_u32( dev, addr, bar );
463     if (err != 0)
464         return err;
465
466     /* Restore the command register */
467     err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND );
468     if (err != 0)
469         return err;
470
471     if (addr & 0x01)
472         dev->regions[region].is_IO = 1;
473     if (addr & 0x04)
474         dev->regions[region].is_64 = 1;
475     if (addr & 0x08)
476         dev->regions[region].is_prefetchable = 1;
477
478     /* Set the size */
479     dev->regions[region].size = get_test_val_size( testval );
480         printf("size = 0x%lx\n", (long)dev->regions[region].size);
481
482     /* Set the base address value */
483     if (dev->regions[region].is_64) {
484         uint32_t top;
485
486         err = pci_device_cfg_read_u32( dev, &top, bar + 4 );
487         if (err != 0)
488             return err;
489
490         dev->regions[region].base_addr = ((uint64_t)top << 32) |
491                                           get_map_base(addr);
492     } else {
493         dev->regions[region].base_addr = get_map_base(addr);
494     }
495
496     return 0;
497 }
498
499 static int
500 pci_device_freebsd_probe( struct pci_device * dev )
501 {
502     struct pci_device_private *priv = (struct pci_device_private *) dev;
503     uint32_t reg, size;
504     uint8_t irq;
505     int err, i, bar;
506
507     /* Many of the fields were filled in during initial device enumeration.
508      * At this point, we need to fill in regions, rom_size, and irq.
509      */
510
511     err = pci_device_cfg_read_u8( dev, &irq, 60 );
512     if (err)
513         return errno;
514     dev->irq = irq;
515
516     bar = 0x10;
517     for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) {
518         pci_device_freebsd_get_region_info( dev, i, bar );
519         if (dev->regions[i].is_64) {
520             bar += 0x08;
521             i++;
522         } else
523             bar += 0x04;
524     }
525
526     /* If it's a VGA device, set up the rom size for read_rom */
527     if ((dev->device_class & 0x00ffff00) ==
528         ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8)))
529     {
530         err = pci_device_cfg_read_u32( dev, &reg, PCIR_BIOS );
531         if (err)
532             return errno;
533
534         if (reg == 0) {
535             dev->rom_size = 0x10000;
536             return 0;
537         }
538
539         err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS );
540         if (err)
541             return errno;
542         pci_device_cfg_read_u32( dev, &size, PCIR_BIOS );
543         pci_device_cfg_write_u32( dev, reg, PCIR_BIOS );
544
545         if ((reg & PCIM_BIOS_ADDR_MASK) != 0) {
546             priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK);
547             dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK);
548         }
549     }
550
551     return 0;
552 }
553
554 #endif
555
556 static void
557 pci_system_freebsd_destroy(void)
558 {
559     close(freebsd_pci_sys->pcidev);
560     free(freebsd_pci_sys->pci_sys.devices);
561     freebsd_pci_sys = NULL;
562 }
563
564 static const struct pci_system_methods freebsd_pci_methods = {
565     .destroy = pci_system_freebsd_destroy,
566     .destroy_device = NULL, /* nothing to do for this */
567     .read_rom = pci_device_freebsd_read_rom,
568     .probe = pci_device_freebsd_probe,
569     .map_range = pci_device_freebsd_map_range,
570     .unmap_range = pci_device_freebsd_unmap_range,
571     .read = pci_device_freebsd_read,
572     .write = pci_device_freebsd_write,
573     .fill_capabilities = pci_fill_capabilities_generic,
574 };
575
576 /**
577  * Attempt to access the FreeBSD PCI interface.
578  */
579 _pci_hidden int
580 pci_system_freebsd_create( void )
581 {
582     struct pci_conf_io pciconfio;
583     struct pci_conf pciconf[255];
584     int pcidev;
585     int i;
586
587     /* Try to open the PCI device */
588     pcidev = open( "/dev/pci", O_RDWR );
589     if ( pcidev == -1 )
590         return ENXIO;
591
592     freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) );
593     if ( freebsd_pci_sys == NULL ) {
594         close( pcidev );
595         return ENOMEM;
596     }
597     pci_sys = &freebsd_pci_sys->pci_sys;
598
599     pci_sys->methods = & freebsd_pci_methods;
600     freebsd_pci_sys->pcidev = pcidev;
601
602     /* Probe the list of devices known by the system */
603     bzero( &pciconfio, sizeof( struct pci_conf_io ) );
604     pciconfio.match_buf_len = sizeof(pciconf);
605     pciconfio.matches = pciconf;
606
607     if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) {
608         free( pci_sys );
609         close( pcidev );
610         return errno;
611     }
612
613     if (pciconfio.status == PCI_GETCONF_ERROR ) {
614         free( pci_sys );
615         close( pcidev );
616         return EINVAL;
617     }
618
619     /* Translate the list of devices into pciaccess's format. */
620     pci_sys->num_devices = pciconfio.num_matches;
621     pci_sys->devices = calloc( pciconfio.num_matches,
622                                sizeof( struct pci_device_private ) );
623
624     for ( i = 0; i < pciconfio.num_matches; i++ ) {
625         struct pci_conf *p = &pciconf[ i ];
626
627 #if HAVE_PCI_IO_PC_DOMAIN
628         pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain;
629 #else
630         pci_sys->devices[ i ].base.domain = 0;
631 #endif
632         pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus;
633         pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev;
634         pci_sys->devices[ i ].base.func = p->pc_sel.pc_func;
635         pci_sys->devices[ i ].base.vendor_id = p->pc_vendor;
636         pci_sys->devices[ i ].base.device_id = p->pc_device;
637         pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor;
638         pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice;
639         pci_sys->devices[ i ].base.revision = p->pc_revid;
640         pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 |
641             (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif;
642         pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f;
643     }
644
645     return 0;
646 }