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