X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;ds=sidebyside;f=src%2Ffreebsd_pci.c;h=14ec3bc016af5cd221721a8a01315cf720962112;hb=9a13ecb8f1462efaf741fed293e29ba38757ff70;hp=be934a8214c91e351e4f1abc6153e8966743ec2e;hpb=042735df2aa5846fcabfd74c5f73877132728b25;p=android-x86%2Fexternal-libpciaccess.git diff --git a/src/freebsd_pci.c b/src/freebsd_pci.c index be934a8..14ec3bc 100644 --- a/src/freebsd_pci.c +++ b/src/freebsd_pci.c @@ -1,6 +1,8 @@ /* * (C) Copyright Eric Anholt 2006 * (C) Copyright IBM Corporation 2006 + * (C) Copyright Mark Kettenis 2011 + * (C) Copyright Robert Millan 2012 * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -38,10 +40,12 @@ #include #include #include +#include #include #include #include +#include "config.h" #include "pciaccess.h" #include "pciaccess_private.h" @@ -51,12 +55,32 @@ #define PCIS_DISPLAY_3D 0x02 #define PCIS_DISPLAY_OTHER 0x80 +/* Registers taken from pcireg.h */ +#define PCIR_COMMAND 0x04 +#define PCIM_CMD_PORTEN 0x0001 +#define PCIM_CMD_MEMEN 0x0002 +#define PCIR_BIOS 0x30 +#define PCIM_BIOS_ENABLE 0x01 +#define PCIM_BIOS_ADDR_MASK 0xfffff800 + +#define PCIR_BARS 0x10 +#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) +#define PCI_BAR_IO(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) +#define PCI_BAR_MEM(x) (((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE) +#define PCIM_BAR_MEM_TYPE 0x00000006 +#define PCIM_BAR_MEM_64 4 +#define PCIM_BAR_MEM_PREFETCH 0x00000008 +#define PCIM_BAR_SPACE 0x00000001 +#define PCIM_BAR_MEM_SPACE 0 +#define PCIM_BAR_IO_SPACE 1 + /** * FreeBSD private pci_system structure that extends the base pci_system * structure. * * It is initialized once and used as a global, just as pci_system is used. */ +_pci_hidden struct freebsd_pci_system { /* This must be the first entry in the structure, as pci_system_cleanup() * frees pci_sys. @@ -68,10 +92,10 @@ struct freebsd_pci_system { /** * Map a memory region for a device using /dev/mem. - * + * * \param dev Device whose memory region is to be mapped. * \param map Parameters of the mapping that is to be created. - * + * * \return * Zero on success or an \c errno value on failure. */ @@ -79,14 +103,14 @@ static int pci_device_freebsd_map_range(struct pci_device *dev, struct pci_device_mapping *map) { - const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) ? (PROT_READ | PROT_WRITE) : PROT_READ; struct mem_range_desc mrd; struct mem_range_op mro; int fd, err = 0; - fd = open("/dev/mem", O_RDWR); + fd = open("/dev/mem", O_RDWR | O_CLOEXEC); if (fd == -1) return errno; @@ -120,7 +144,7 @@ pci_device_freebsd_map_range(struct pci_device *dev, return err; } -static void +static int pci_device_freebsd_unmap_range( struct pci_device *dev, struct pci_device_mapping *map ) { @@ -128,23 +152,29 @@ pci_device_freebsd_unmap_range( struct pci_device *dev, struct mem_range_op mro; int fd; - fd = open("/dev/mem", O_RDWR); - if (fd != -1) { - mrd.mr_base = map->base; - mrd.mr_len = map->size; - strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); - mrd.mr_flags = MDF_UNCACHEABLE; - mro.mo_desc = &mrd; - mro.mo_arg[0] = MEMRANGE_SET_REMOVE; - - if (ioctl(fd, MEMRANGE_SET, &mro)) { - fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); + if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || + (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) + { + fd = open("/dev/mem", O_RDWR | O_CLOEXEC); + if (fd != -1) { + mrd.mr_base = map->base; + mrd.mr_len = map->size; + strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner)); + mrd.mr_flags = MDF_UNCACHEABLE; + mro.mo_desc = &mrd; + mro.mo_arg[0] = MEMRANGE_SET_REMOVE; + + if (ioctl(fd, MEMRANGE_SET, &mro)) { + fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno)); + } + + close(fd); + } else { + fprintf(stderr, "Failed to open /dev/mem\n"); } - - close(fd); } - pci_device_generic_unmap_range(dev, map); + return pci_device_generic_unmap_range(dev, map); } static int @@ -154,6 +184,9 @@ pci_device_freebsd_read( struct pci_device * dev, void * data, { struct pci_io io; +#if HAVE_PCI_IO_PC_DOMAIN + io.pi_sel.pc_domain = dev->domain; +#endif io.pi_sel.pc_bus = dev->bus; io.pi_sel.pc_dev = dev->dev; io.pi_sel.pc_func = dev->func; @@ -169,7 +202,7 @@ pci_device_freebsd_read( struct pci_device * dev, void * data, io.pi_reg = offset; io.pi_width = toread; - if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) + if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 ) return errno; memcpy(data, &io.pi_data, toread ); @@ -191,6 +224,9 @@ pci_device_freebsd_write( struct pci_device * dev, const void * data, { struct pci_io io; +#if HAVE_PCI_IO_PC_DOMAIN + io.pi_sel.pc_domain = dev->domain; +#endif io.pi_sel.pc_bus = dev->bus; io.pi_sel.pc_dev = dev->dev; io.pi_sel.pc_func = dev->func; @@ -199,11 +235,15 @@ pci_device_freebsd_write( struct pci_device * dev, const void * data, while ( size > 0 ) { int towrite = (size < 4 ? size : 4); + /* Only power of two allowed. */ + if (towrite == 3) + towrite = 2; + io.pi_reg = offset; io.pi_width = towrite; memcpy( &io.pi_data, data, towrite ); - if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) + if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 ) return errno; offset += towrite; @@ -224,8 +264,12 @@ pci_device_freebsd_write( struct pci_device * dev, const void * data, static int pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) { + struct pci_device_private *priv = (struct pci_device_private *) dev; void *bios; - int memfd; + pciaddr_t rom_base; + uint32_t rom; + uint16_t reg; + int pci_rom, memfd; if ( ( dev->device_class & 0x00ffff00 ) != ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) @@ -233,11 +277,29 @@ pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) return ENOSYS; } - memfd = open( "/dev/mem", O_RDONLY ); + if (priv->rom_base == 0) { +#if defined(__amd64__) || defined(__i386__) + rom_base = 0xc0000; + pci_rom = 0; +#else + return ENOSYS; +#endif + } else { + rom_base = priv->rom_base; + pci_rom = 1; + + pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); + pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); + pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); + pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); + } + + printf("Using rom_base = 0x%lx\n", (long)rom_base); + memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); if ( memfd == -1 ) return errno; - bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, 0xc0000 ); + bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); if ( bios == MAP_FAILED ) { close( memfd ); return errno; @@ -248,6 +310,11 @@ pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) munmap( bios, dev->rom_size ); close( memfd ); + if (pci_rom) { + pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); + pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); + } + return 0; } @@ -258,7 +325,7 @@ pci_device_freebsd_get_num_regions( struct pci_device * dev ) { struct pci_device_private *priv = (struct pci_device_private *) dev; - switch (priv->header_type & 0x7f) { + switch (priv->header_type) { case 0: return 6; case 1: @@ -271,6 +338,64 @@ pci_device_freebsd_get_num_regions( struct pci_device * dev ) } } +#ifdef PCIOCGETBAR + +static int +pci_device_freebsd_probe( struct pci_device * dev ) +{ + struct pci_device_private *priv = (struct pci_device_private *) dev; + struct pci_bar_io bar; + uint8_t irq; + int err, i; + +#if HAVE_PCI_IO_PC_DOMAIN + bar.pbi_sel.pc_domain = dev->domain; +#endif + bar.pbi_sel.pc_bus = dev->bus; + bar.pbi_sel.pc_dev = dev->dev; + bar.pbi_sel.pc_func = dev->func; + + + /* Many of the fields were filled in during initial device enumeration. + * At this point, we need to fill in regions, rom_size, and irq. + */ + + err = pci_device_cfg_read_u8( dev, &irq, 60 ); + if (err) + return errno; + dev->irq = irq; + + for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { + bar.pbi_reg = PCIR_BAR(i); + if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 ) + continue; + + if (PCI_BAR_IO(bar.pbi_base)) + dev->regions[i].is_IO = 1; + + if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) + dev->regions[i].is_64 = 1; + + if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) + dev->regions[i].is_prefetchable = 1; + + dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf); + dev->regions[i].size = bar.pbi_length; + } + + /* If it's a VGA device, set up the rom size for read_rom using the + * 0xc0000 mapping. + */ + if ((dev->device_class & 0x00ffff00) == + ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { + dev->rom_size = 64 * 1024; + } + + return 0; +} + +#else + /** Masks out the flag bigs of the base address register value */ static uint32_t get_map_base( uint32_t val ) @@ -285,20 +410,13 @@ get_map_base( uint32_t val ) static int get_test_val_size( uint32_t testval ) { - int size = 1; - if (testval == 0) return 0; /* Mask out the flag bits */ testval = get_map_base( testval ); - while ((testval & 1) == 0) { - size <<= 1; - testval >>= 1; - } - - return size; + return 1 << (ffs(testval) - 1); } /** @@ -314,6 +432,7 @@ pci_device_freebsd_get_region_info( struct pci_device * dev, int region, int bar ) { uint32_t addr, testval; + uint16_t cmd; int err; /* Get the base address */ @@ -321,12 +440,35 @@ pci_device_freebsd_get_region_info( struct pci_device * dev, int region, if (err != 0) return err; + /* + * We are going to be doing evil things to the registers here + * so disable them via the command register first. + */ + err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND ); + if (err != 0) + return err; + + err = pci_device_cfg_write_u16( dev, + cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), + PCIR_COMMAND ); + if (err != 0) + return err; + /* Test write all ones to the register, then restore it. */ err = pci_device_cfg_write_u32( dev, 0xffffffff, bar ); if (err != 0) return err; - pci_device_cfg_read_u32( dev, &testval, bar ); + err = pci_device_cfg_read_u32( dev, &testval, bar ); + if (err != 0) + return err; err = pci_device_cfg_write_u32( dev, addr, bar ); + if (err != 0) + return err; + + /* Restore the command register */ + err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND ); + if (err != 0) + return err; if (addr & 0x01) dev->regions[region].is_IO = 1; @@ -337,6 +479,7 @@ pci_device_freebsd_get_region_info( struct pci_device * dev, int region, /* Set the size */ dev->regions[region].size = get_test_val_size( testval ); + printf("size = 0x%lx\n", (long)dev->regions[region].size); /* Set the base address value */ if (dev->regions[region].is_64) { @@ -359,6 +502,7 @@ static int pci_device_freebsd_probe( struct pci_device * dev ) { struct pci_device_private *priv = (struct pci_device_private *) dev; + uint32_t reg, size; uint8_t irq; int err, i, bar; @@ -371,31 +515,46 @@ pci_device_freebsd_probe( struct pci_device * dev ) return errno; dev->irq = irq; - err = pci_device_cfg_read_u8( dev, &priv->header_type, 0x0e ); - if (err) - return errno; - bar = 0x10; for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { pci_device_freebsd_get_region_info( dev, i, bar ); - if (dev->regions[i].is_64) + if (dev->regions[i].is_64) { bar += 0x08; - else + i++; + } else bar += 0x04; } - /* If it's a VGA device, set up the rom size for read_rom using the - * 0xc0000 mapping. - */ + /* If it's a VGA device, set up the rom size for read_rom */ if ((dev->device_class & 0x00ffff00) == ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { - dev->rom_size = 64 * 1024; + err = pci_device_cfg_read_u32( dev, ®, PCIR_BIOS ); + if (err) + return errno; + + if (reg == 0) { + dev->rom_size = 0x10000; + return 0; + } + + err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS ); + if (err) + return errno; + pci_device_cfg_read_u32( dev, &size, PCIR_BIOS ); + pci_device_cfg_write_u32( dev, reg, PCIR_BIOS ); + + if ((reg & PCIM_BIOS_ADDR_MASK) != 0) { + priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK); + dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK); + } } return 0; } +#endif + static void pci_system_freebsd_destroy(void) { @@ -404,6 +563,138 @@ pci_system_freebsd_destroy(void) freebsd_pci_sys = NULL; } +#if defined(__i386__) || defined(__amd64__) +#include +#endif + +static struct pci_io_handle * +pci_device_freebsd_open_legacy_io(struct pci_io_handle *ret, + struct pci_device *dev, pciaddr_t base, pciaddr_t size) +{ +#if defined(__i386__) || defined(__amd64__) + ret->fd = open("/dev/io", O_RDWR | O_CLOEXEC); + + if (ret->fd < 0) + return NULL; + + ret->base = base; + ret->size = size; + return ret; +#elif defined(PCI_MAGIC_IO_RANGE) + ret->memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, + aperturefd, PCI_MAGIC_IO_RANGE + base); + if (ret->memory == MAP_FAILED) + return NULL; + + ret->base = base; + ret->size = size; + return ret; +#else + return NULL; +#endif +} + +#if defined(__i386__) || defined(__amd64__) +static void +pci_device_freebsd_close_io(struct pci_device *dev, struct pci_io_handle *handle) +{ + if (handle->fd > -1) + close(handle->fd); +} +#endif + +static uint32_t +pci_device_freebsd_read32(struct pci_io_handle *handle, uint32_t reg) +{ +#if defined(__i386__) || defined(__amd64__) + return inl(handle->base + reg); +#else + return *(uint32_t *)((uintptr_t)handle->memory + reg); +#endif +} + +static uint16_t +pci_device_freebsd_read16(struct pci_io_handle *handle, uint32_t reg) +{ +#if defined(__i386__) || defined(__amd64__) + return inw(handle->base + reg); +#else + return *(uint16_t *)((uintptr_t)handle->memory + reg); +#endif +} + +static uint8_t +pci_device_freebsd_read8(struct pci_io_handle *handle, uint32_t reg) +{ +#if defined(__i386__) || defined(__amd64__) + return inb(handle->base + reg); +#else + return *(uint8_t *)((uintptr_t)handle->memory + reg); +#endif +} + +static void +pci_device_freebsd_write32(struct pci_io_handle *handle, uint32_t reg, + uint32_t data) +{ +#if defined(__i386__) || defined(__amd64__) + outl(handle->base + reg, data); +#else + *(uint16_t *)((uintptr_t)handle->memory + reg) = data; +#endif +} + +static void +pci_device_freebsd_write16(struct pci_io_handle *handle, uint32_t reg, + uint16_t data) +{ +#if defined(__i386__) || defined(__amd64__) + outw(handle->base + reg, data); +#else + *(uint8_t *)((uintptr_t)handle->memory + reg) = data; +#endif +} + +static void +pci_device_freebsd_write8(struct pci_io_handle *handle, uint32_t reg, + uint8_t data) +{ +#if defined(__i386__) || defined(__amd64__) + outb(handle->base + reg, data); +#else + *(uint32_t *)((uintptr_t)handle->memory + reg) = data; +#endif +} + +static int +pci_device_freebsd_map_legacy(struct pci_device *dev, pciaddr_t base, + pciaddr_t size, unsigned map_flags, void **addr) +{ + struct pci_device_mapping map; + int err; + + map.base = base; + map.size = size; + map.flags = map_flags; + map.memory = NULL; + err = pci_device_freebsd_map_range(dev, &map); + *addr = map.memory; + + return err; +} + +static int +pci_device_freebsd_unmap_legacy(struct pci_device *dev, void *addr, + pciaddr_t size) +{ + struct pci_device_mapping map; + + map.memory = addr; + map.size = size; + map.flags = 0; + return pci_device_freebsd_unmap_range(dev, &map); +} + static const struct pci_system_methods freebsd_pci_methods = { .destroy = pci_system_freebsd_destroy, .destroy_device = NULL, /* nothing to do for this */ @@ -414,12 +705,24 @@ static const struct pci_system_methods freebsd_pci_methods = { .read = pci_device_freebsd_read, .write = pci_device_freebsd_write, .fill_capabilities = pci_fill_capabilities_generic, + .open_legacy_io = pci_device_freebsd_open_legacy_io, +#if defined(__i386__) || defined(__amd64__) + .close_io = pci_device_freebsd_close_io, +#endif + .read32 = pci_device_freebsd_read32, + .read16 = pci_device_freebsd_read16, + .read8 = pci_device_freebsd_read8, + .write32 = pci_device_freebsd_write32, + .write16 = pci_device_freebsd_write16, + .write8 = pci_device_freebsd_write8, + .map_legacy = pci_device_freebsd_map_legacy, + .unmap_legacy = pci_device_freebsd_unmap_legacy, }; /** * Attempt to access the FreeBSD PCI interface. */ -int +_pci_hidden int pci_system_freebsd_create( void ) { struct pci_conf_io pciconfio; @@ -428,7 +731,7 @@ pci_system_freebsd_create( void ) int i; /* Try to open the PCI device */ - pcidev = open( "/dev/pci", O_RDWR ); + pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC ); if ( pcidev == -1 ) return ENXIO; @@ -467,7 +770,11 @@ pci_system_freebsd_create( void ) for ( i = 0; i < pciconfio.num_matches; i++ ) { struct pci_conf *p = &pciconf[ i ]; - pci_sys->devices[ i ].base.domain = 0; /* XXX */ +#if HAVE_PCI_IO_PC_DOMAIN + pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain; +#else + pci_sys->devices[ i ].base.domain = 0; +#endif pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus; pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev; pci_sys->devices[ i ].base.func = p->pc_sel.pc_func; @@ -475,8 +782,10 @@ pci_system_freebsd_create( void ) pci_sys->devices[ i ].base.device_id = p->pc_device; pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor; pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice; + pci_sys->devices[ i ].base.revision = p->pc_revid; pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 | (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif; + pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f; } return 0;