diff options
author | James Cloos <cloos@jhcloos.com> | 2007-09-03 06:15:58 -0400 |
---|---|---|
committer | James Cloos <cloos@jhcloos.com> | 2007-09-03 06:15:58 -0400 |
commit | 9d1596cba90c8fd273e9d1d5488747cc0f34fdc7 (patch) | |
tree | 2e45baf3a3e2edbfe3f9cf469fd0853d2f49fe2c | |
parent | b1e911784d314fdbd8d938e5fe3671bec128fb61 (diff) | |
parent | 5cf29b06b9b5806056a0b04160b2286eb4158748 (diff) |
Merge branch 'master' of ssh://git.freedesktop.org/git/xorg/lib/libpciaccess
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | include/pciaccess.h | 60 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/common_interface.c | 199 | ||||
-rw-r--r-- | src/common_map.c | 54 | ||||
-rw-r--r-- | src/freebsd_pci.c | 64 | ||||
-rw-r--r-- | src/linux_sysfs.c | 164 | ||||
-rw-r--r-- | src/pciaccess_private.h | 30 | ||||
-rw-r--r-- | src/scanpci.c | 4 | ||||
-rw-r--r-- | src/solx_devfs.c | 64 |
10 files changed, 440 insertions, 212 deletions
diff --git a/configure.ac b/configure.ac index d5c0585..133963d 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ dnl refers to ${prefix}. Thus we have to use `eval' twice. AC_PREREQ([2.57]) -AC_INIT(libpciaccess, 0.8.0, [none yet], libpciaccess) +AC_INIT(libpciaccess, 0.10.0, [none yet], libpciaccess) AM_INIT_AUTOMAKE([dist-bzip2]) AM_MAINTAINER_MODE @@ -87,6 +87,12 @@ AM_CONDITIONAL(LINUX, [test "x$linux" = xyes]) AM_CONDITIONAL(FREEBSD, [test "x$freebsd" = xyes]) AM_CONDITIONAL(SOLARIS, [test "x$solaris" = xyes]) +AC_CHECK_FILE([/usr/include/asm/mtrr.h], + [have_mtrr_h="yes"], [have_mtrr_h="no"]) +if test "x$have_mtrr_h" = xyes; then + AC_DEFINE(HAVE_MTRR, 1, [Use MTRRs on mappings]) +fi + AC_SUBST(PCIACCESS_CFLAGS) AC_SUBST(PCIACCESS_LIBS) diff --git a/include/pciaccess.h b/include/pciaccess.h index 4f383ab..4bd3672 100644 --- a/include/pciaccess.h +++ b/include/pciaccess.h @@ -33,6 +33,12 @@ #include <inttypes.h> +#if __GNUC__ >= 3 +#define __deprecated __attribute__((deprecated)) +#else +#define __deprecated +#endif + typedef uint64_t pciaddr_t; struct pci_device; @@ -42,17 +48,24 @@ struct pci_slot_match; int pci_device_read_rom(struct pci_device *dev, void *buffer); -int pci_device_map_region(struct pci_device *dev, unsigned region, - int write_enable); +int __deprecated pci_device_map_region(struct pci_device *dev, + unsigned region, int write_enable); -int pci_device_unmap_region(struct pci_device *dev, unsigned region); +int __deprecated pci_device_unmap_region(struct pci_device *dev, + unsigned region); -int pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base, - pciaddr_t size, int write_enable, void **addr); +int pci_device_map_range(struct pci_device *dev, pciaddr_t base, + pciaddr_t size, unsigned map_flags, void **addr); -int pci_device_unmap_memory_range(struct pci_device *dev, void *memory, +int pci_device_unmap_range(struct pci_device *dev, void *memory, pciaddr_t size); +int __deprecated pci_device_map_memory_range(struct pci_device *dev, + pciaddr_t base, pciaddr_t size, int write_enable, void **addr); + +int __deprecated pci_device_unmap_memory_range(struct pci_device *dev, + void *memory, pciaddr_t size); + int pci_device_probe(struct pci_device *dev); const struct pci_agp_info *pci_device_get_agp_info(struct pci_device *dev); @@ -111,6 +124,16 @@ int pci_device_cfg_write_u32(struct pci_device *dev, uint32_t data, int pci_device_cfg_write_bits(struct pci_device *dev, uint32_t mask, uint32_t data, pciaddr_t offset); +/** + * \name Mapping flags passed to \c pci_device_map_range + */ +/*@{*/ +#define PCI_DEV_MAP_FLAG_WRITABLE (1U<<0) +#define PCI_DEV_MAP_FLAG_WRITE_COMBINE (1U<<1) +#define PCI_DEV_MAP_FLAG_CACHABLE (1U<<2) +/*@}*/ + + #define PCI_MATCH_ANY (~0) /** @@ -178,10 +201,35 @@ struct pci_slot_match { struct pci_mem_region { /** * When the region is mapped, this is the pointer to the memory. + * + * This field is \b only set when the deprecated \c pci_device_map_region + * interface is used. Use \c pci_device_map_range instead. + * + * \deprecated */ void *memory; + + /** + * Base physical address of the region within its bus / domain. + * + * \warning + * This address is really only useful to other devices in the same + * domain. It's probably \b not the address applications will ever + * use. + * + * \warning + * Most (all?) platform back-ends leave this field unset. + */ pciaddr_t bus_addr; + + + /** + * Base physical address of the region from the CPU's point of view. + * + * This address is typically passed to \c pci_device_map_range to create + * a mapping of the region to the CPU's virtual address space. + */ pciaddr_t base_addr; diff --git a/src/Makefile.am b/src/Makefile.am index 760711c..e743dbf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ AM_CFLAGS = @PCIACCESS_CFLAGS@ lib_LTLIBRARIES = libpciaccess.la if LINUX -OS_SUPPORT = linux_sysfs.c linux_devmem.c +OS_SUPPORT = linux_sysfs.c linux_devmem.c linux_devmem.h endif if FREEBSD @@ -43,6 +43,7 @@ libpciaccess_la_SOURCES = common_bridge.c \ common_interface.c \ common_capability.c \ common_device_name.c \ + common_map.c \ pciaccess_private.h \ $(OS_SUPPORT) @@ -50,7 +51,7 @@ INCLUDES = -I$(top_srcdir)/include libpciaccess_la_LIBADD = @PCIACCESS_LIBS@ -libpciaccess_la_LDFLAGS = -version-number 0:8:0 -no-undefined +libpciaccess_la_LDFLAGS = -version-number 0:10:0 -no-undefined libpciaccessincludedir = $(includedir) libpciaccessinclude_HEADERS = \ diff --git a/src/common_interface.c b/src/common_interface.c index 3b20937..9c29622 100644 --- a/src/common_interface.c +++ b/src/common_interface.c @@ -30,6 +30,7 @@ */ #include <stdlib.h> +#include <string.h> #include <errno.h> #include "pciaccess.h" @@ -131,25 +132,27 @@ pci_device_probe( struct pci_device * dev ) * \return * Zero on success or an \c errno value on failure. * - * \sa pci_device_unmap_region + * \sa pci_device_map_range, pci_device_unmap_range + * \deprecated */ int -pci_device_map_region( struct pci_device * dev, unsigned region, - int write_enable ) +pci_device_map_region(struct pci_device * dev, unsigned region, + int write_enable) { - if ( dev == NULL ) { - return EFAULT; - } + const unsigned map_flags = + (write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0; - if ( (region > 5) || (dev->regions[ region ].size == 0) ) { - return ENOENT; + if ((region > 5) || (dev->regions[region].size == 0)) { + return ENOENT; } - if ( dev->regions[ region ].memory != NULL ) { - return 0; + if (dev->regions[region].memory != NULL) { + return 0; } - return (pci_sys->methods->map)( dev, region, write_enable ); + return pci_device_map_range(dev, dev->regions[region].base_addr, + dev->regions[region].size, map_flags, + &dev->regions[region].memory); } @@ -163,58 +166,90 @@ pci_device_map_region( struct pci_device * dev, unsigned region, * \param dev Device whose memory region is to be mapped. * \param base Base address of the range to be mapped. * \param size Size of the range to be mapped. - * \param write_enable Map for writing (non-zero). + * \param map_flags Flag bits controlling how the mapping is accessed. * \param addr Location to store the mapped address. * * \return * Zero on success or an \c errno value on failure. * - * \sa pci_device_unmap_memory_range, pci_device_map_region + * \sa pci_device_unmap_range */ int -pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base, - pciaddr_t size, int write_enable, - void **addr) +pci_device_map_range(struct pci_device *dev, pciaddr_t base, + pciaddr_t size, unsigned map_flags, + void **addr) { + struct pci_device_private *const devp = + (struct pci_device_private *) dev; + struct pci_device_mapping *mappings; unsigned region; + unsigned i; int err = 0; *addr = NULL; if (dev == NULL) { - return EFAULT; + return EFAULT; } for (region = 0; region < 6; region++) { - const struct pci_mem_region const* r = &dev->regions[region]; + const struct pci_mem_region const* r = &dev->regions[region]; - if (r->size != 0) { - if ((r->base_addr <= base) && ((r->base_addr + r->size) > base)) { - if ((base + size) > (r->base_addr + r->size)) { - return E2BIG; - } + if (r->size != 0) { + if ((r->base_addr <= base) && ((r->base_addr + r->size) > base)) { + if ((base + size) > (r->base_addr + r->size)) { + return E2BIG; + } - break; - } - } + break; + } + } } if (region > 5) { - return ENOENT; + return ENOENT; + } + + /* Make sure that there isn't already a mapping with the same base and + * size. + */ + for (i = 0; i < devp->num_mappings; i++) { + if ((devp->mappings[i].base == base) + && (devp->mappings[i].size == size)) { + return EINVAL; + } } + + mappings = realloc(devp->mappings, + (sizeof(devp->mappings[0]) * (devp->num_mappings + 1))); + if (mappings == NULL) { + return ENOMEM; + } + + mappings[devp->num_mappings].base = base; + mappings[devp->num_mappings].size = size; + mappings[devp->num_mappings].region = region; + mappings[devp->num_mappings].flags = map_flags; + mappings[devp->num_mappings].memory = NULL; + if (dev->regions[region].memory == NULL) { - err = (*pci_sys->methods->map)(dev, region, write_enable); + err = (*pci_sys->methods->map_range)(dev, + &mappings[devp->num_mappings]); } - - if (err == 0) { - const pciaddr_t offset = base - dev->regions[region].base_addr; - *addr = ((uint8_t *)dev->regions[region].memory) + offset; + if (err == 0) { + *addr = mappings[devp->num_mappings].memory; + devp->num_mappings++; + } else { + mappings = realloc(devp->mappings, + (sizeof(devp->mappings[0]) * devp->num_mappings)); } + devp->mappings = mappings; + return err; } @@ -231,24 +266,29 @@ pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base, * \return * Zero on success or an \c errno value on failure. * - * \sa pci_device_map_region + * \sa pci_device_map_range, pci_device_unmap_range + * \deprecated */ int pci_device_unmap_region( struct pci_device * dev, unsigned region ) { - if ( dev == NULL ) { - return EFAULT; + int err; + + if (dev == NULL) { + return EFAULT; } - if ( (region > 5) || (dev->regions[ region ].size == 0) ) { - return ENOENT; + if ((region > 5) || (dev->regions[region].size == 0)) { + return ENOENT; } - if ( dev->regions[ region ].memory == NULL ) { - return 0; + err = pci_device_unmap_range(dev, dev->regions[region].memory, + dev->regions[region].size); + if (!err) { + dev->regions[region].memory = NULL; } - - return (pci_sys->methods->unmap)( dev, region ); + + return err; } @@ -265,41 +305,74 @@ pci_device_unmap_region( struct pci_device * dev, unsigned region ) * \return * Zero on success or an \c errno value on failure. * - * \sa pci_device_map_memory_range, pci_device_unmap_region + * \sa pci_device_map_range, pci_device_unmap_range + * \deprecated */ int pci_device_unmap_memory_range(struct pci_device *dev, void *memory, - pciaddr_t size) + pciaddr_t size) { - unsigned region; + return pci_device_unmap_range(dev, memory, size); +} + + +/** + * Unmap the specified memory range so that it can no longer be accessed by the CPU. + * + * Unmaps the specified memory range that was previously mapped via + * \c pci_device_map_memory_range. + * + * \param dev Device whose memory is to be unmapped. + * \param memory Pointer to the base of the mapped range. + * \param size Size, in bytes, of the range to be unmapped. + * + * \return + * Zero on success or an \c errno value on failure. + * + * \sa pci_device_map_range + */ +int +pci_device_unmap_range(struct pci_device *dev, void *memory, + pciaddr_t size) +{ + struct pci_device_private *const devp = + (struct pci_device_private *) dev; + unsigned i; + int err; if (dev == NULL) { - return EFAULT; + return EFAULT; } - for (region = 0; region < 6; region++) { - const struct pci_mem_region const* r = &dev->regions[region]; - const uint8_t *const mem = r->memory; - - if (r->size != 0) { - if ((mem <= memory) && ((mem + r->size) > memory)) { - if ((memory + size) > (mem + r->size)) { - return E2BIG; - } - - break; - } - } + for (i = 0; i < devp->num_mappings; i++) { + if ((devp->mappings[i].memory == memory) + && (devp->mappings[i].size == size)) { + break; + } } - if (region > 5) { - return ENOENT; + if (i == devp->num_mappings) { + return ENOENT; + } + + + err = (*pci_sys->methods->unmap_range)(dev, &devp->mappings[i]); + if (!err) { + const unsigned entries_to_move = (devp->num_mappings - i) - 1; + + if (entries_to_move > 0) { + (void) memmove(&devp->mappings[i], + &devp->mappings[i + 1], + entries_to_move * sizeof(devp->mappings[0])); + } + + devp->num_mappings--; + devp->mappings = realloc(devp->mappings, + (sizeof(devp->mappings[0]) * devp->num_mappings)); } - return (dev->regions[region].memory != NULL) - ? (*pci_sys->methods->unmap)(dev, region) - : 0; + return err; } diff --git a/src/common_map.c b/src/common_map.c new file mode 100644 index 0000000..a64577b --- /dev/null +++ b/src/common_map.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright IBM Corporation 2007 + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <sys/mman.h> +#include <errno.h> + +#include "pciaccess.h" +#include "pciaccess_private.h" + +/** + * \file common_map.c + * Platform independent memory map routines. + * + * \author Ian Romanick <idr@us.ibm.com> + */ + +/** + * Unmap the specified region using the munmap. + * + * \param dev Device whose memory region is to be mapped. + * \param map Memory mapping that is to be undone. + * + * \return + * Zero on success or an \c errno value on failure. + * + * \sa pci_device_unmap_range + */ +int +pci_device_generic_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map) +{ + return (munmap(map->memory, map->size) == -1) ? errno : 0; +} diff --git a/src/freebsd_pci.c b/src/freebsd_pci.c index e83cc00..dd7635f 100644 --- a/src/freebsd_pci.c +++ b/src/freebsd_pci.c @@ -67,60 +67,34 @@ 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 region Region, on the range [0, 5], that is to be mapped. - * \param write_enable Map for writing (non-zero). - * + * + * \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. */ static int -pci_device_freebsd_map( struct pci_device *dev, unsigned region, - int write_enable ) +pci_device_freebsd_map_range(struct pci_device *dev, + struct pci_device_mapping *map) { - int fd, err = 0, prot; - - fd = open( "/dev/mem", write_enable ? O_RDWR : O_RDONLY ); - if ( fd == -1 ) + const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + ? O_RDWR : O_RDONLY; + int fd, err = 0; + + fd = open("/dev/mem", open_flags); + if (fd == -1) return errno; - prot = write_enable ? (PROT_READ | PROT_WRITE) : PROT_READ; - dev->regions[ region ].memory = mmap( NULL, dev->regions[ region ].size, - prot, MAP_SHARED, fd, - dev->regions[ region ].base_addr); - - if ( dev->regions[ region ].memory == MAP_FAILED ) { - close( fd ); - dev->regions[ region ].memory = NULL; - err = errno; - } - - close( fd ); - - return err; -} - -/** - * Unmap the specified region. - * - * \param dev Device whose memory region is to be unmapped. - * \param region Region, on the range [0, 5], that is to be unmapped. - * - * \return - * Zero on success or an \c errno value on failure. - */ -static int -pci_device_freebsd_unmap( struct pci_device * dev, unsigned region ) -{ - int err = 0; + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base); - if ( munmap( dev->regions[ region ].memory, - dev->regions[ region ].size ) == -1) { + if (map->memory == MAP_FAILED) { err = errno; } - dev->regions[ region ].memory = NULL; + close(fd); return err; } @@ -387,8 +361,8 @@ static const struct pci_system_methods freebsd_pci_methods = { .destroy_device = NULL, /* nothing to do for this */ .read_rom = pci_device_freebsd_read_rom, .probe = pci_device_freebsd_probe, - .map = pci_device_freebsd_map, - .unmap = pci_device_freebsd_unmap, + .map_range = pci_device_freebsd_map_range, + .unmap_range = pci_device_generic_unmap_range, .read = pci_device_freebsd_read, .write = pci_device_freebsd_write, .fill_capabilities = pci_fill_capabilities_generic, diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c index ba5dce2..3671f28 100644 --- a/src/linux_sysfs.c +++ b/src/linux_sysfs.c @@ -44,6 +44,13 @@ #include <dirent.h> #include <errno.h> +#include "config.h" + +#ifdef HAVE_MTRR +#include <asm/mtrr.h> +#include <sys/ioctl.h> +#endif + #include "pciaccess.h" #include "pciaccess_private.h" #include "linux_devmem.h" @@ -53,11 +60,11 @@ static int pci_device_linux_sysfs_read_rom( struct pci_device * dev, static int pci_device_linux_sysfs_probe( struct pci_device * dev ); -static int pci_device_linux_sysfs_map_region( struct pci_device * dev, - unsigned region, int write_enable ); +static int pci_device_linux_sysfs_map_range(struct pci_device *dev, + struct pci_device_mapping *map); -static int pci_device_linux_sysfs_unmap_region( struct pci_device * dev, - unsigned region ); +static int pci_device_linux_sysfs_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map); static int pci_device_linux_sysfs_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ); @@ -71,8 +78,8 @@ static const struct pci_system_methods linux_sysfs_methods = { .destroy_device = NULL, .read_rom = pci_device_linux_sysfs_read_rom, .probe = pci_device_linux_sysfs_probe, - .map = pci_device_linux_sysfs_map_region, - .unmap = pci_device_linux_sysfs_unmap_region, + .map_range = pci_device_linux_sysfs_map_range, + .unmap_range = pci_device_linux_sysfs_unmap_range, .read = pci_device_linux_sysfs_read, .write = pci_device_linux_sysfs_write, @@ -114,6 +121,10 @@ pci_system_linux_sysfs_create( void ) err = errno; } +#ifdef HAVE_MTRR + pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY); +#endif + return err; } @@ -142,7 +153,7 @@ populate_entries( struct pci_system * p ) struct dirent ** devices; int n; int i; - int err; + int err = 0; n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort ); @@ -361,7 +372,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data, pciaddr_t temp_size = size; int err = 0; int fd; - + char *data_bytes = data; if ( bytes_read != NULL ) { *bytes_read = 0; @@ -386,7 +397,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data, while ( temp_size > 0 ) { - const ssize_t bytes = pread64( fd, data, temp_size, offset ); + const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset ); /* If zero bytes were read, then we assume it's the end of the * config file. @@ -398,7 +409,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data, temp_size -= bytes; offset += bytes; - data += bytes; + data_bytes += bytes; } if ( bytes_read != NULL ) { @@ -419,7 +430,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, pciaddr_t temp_size = size; int err = 0; int fd; - + const char *data_bytes = data; if ( bytes_written != NULL ) { *bytes_written = 0; @@ -444,7 +455,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, while ( temp_size > 0 ) { - const ssize_t bytes = pwrite64( fd, data, temp_size, offset ); + const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset ); /* If zero bytes were written, then we assume it's the end of the * config file. @@ -456,7 +467,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, temp_size -= bytes; offset += bytes; - data += bytes; + data_bytes += bytes; } if ( bytes_written != NULL ) { @@ -471,14 +482,13 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, /** * Map a memory region for a device using the Linux sysfs interface. * - * \param dev Device whose memory region is to be mapped. - * \param region Region, on the range [0, 5], that is to be mapped. - * \param write_enable Map for writing (non-zero). + * \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. * - * \sa pci_device_map_region, pci_device_linux_sysfs_unmap_region + * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range * * \todo * Some older 2.6.x kernels don't implement the resourceN files. On those @@ -486,51 +496,80 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, * \c mmap64 may need to be used. */ static int -pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region, - int write_enable ) +pci_device_linux_sysfs_map_range(struct pci_device *dev, + struct pci_device_mapping *map) { char name[256]; int fd; int err = 0; - const int prot = (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ; - + const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + ? O_RDWR : O_RDONLY; + const off_t offset = map->base - dev->regions[map->region].base_addr; +#ifdef HAVE_MTRR + struct mtrr_sentry sentry = { + .base = map->base, + .size = map->size, + .type = MTRR_TYPE_UNCACHABLE + }; +#endif + + snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", + SYS_BUS_PCI, + dev->domain, + dev->bus, + dev->dev, + dev->func, + map->region); + + fd = open(name, open_flags); + if (fd == -1) { + return errno; + } - snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", - SYS_BUS_PCI, - dev->domain, - dev->bus, - dev->dev, - dev->func, - region ); - fd = open( name, (write_enable) ? O_RDWR : O_RDONLY ); - if ( fd == -1 ) { - return errno; + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); + if (map->memory == MAP_FAILED) { + err = errno; + map->memory = NULL; } + close(fd); - dev->regions[ region ].memory = mmap( NULL, dev->regions[ region ].size, - prot, MAP_SHARED, fd, 0 ); - if ( dev->regions[ region ].memory == MAP_FAILED ) { - err = errno; - dev->regions[ region ].memory = NULL; +#ifdef HAVE_MTRR + if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { + sentry.type = MTRR_TYPE_WRBACK; + } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { + sentry.type = MTRR_TYPE_WRCOMB; } - close( fd ); + if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { + if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) { + /* FIXME: Should we report an error in this case? + */ + fprintf(stderr, "error setting MTRR " + "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", + sentry.base, sentry.size, sentry.type, + strerror(errno), errno); +/* err = errno;*/ + } + } +#endif + return err; } - /** - * Unmap the specified region using the Linux sysfs interface. - * - * \param dev Device whose memory region is to be mapped. - * \param region Region, on the range [0, 5], that is to be mapped. - * + * Unmap a memory region for a device using the Linux sysfs interface. + * + * \param dev Device whose memory region is to be unmapped. + * \param map Parameters of the mapping that is to be destroyed. + * * \return * Zero on success or an \c errno value on failure. * - * \sa pci_device_unmap_region, pci_device_linux_sysfs_map_region + * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range * * \todo * Some older 2.6.x kernels don't implement the resourceN files. On those @@ -538,16 +577,41 @@ pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region, * \c mmap64 may need to be used. */ static int -pci_device_linux_sysfs_unmap_region( struct pci_device * dev, unsigned region ) +pci_device_linux_sysfs_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map) { int err = 0; - - if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size ) - == -1 ) { - err = errno; +#ifdef HAVE_MTRR + struct mtrr_sentry sentry = { + .base = map->base, + .size = map->size, + .type = MTRR_TYPE_UNCACHABLE + }; +#endif + + err = pci_device_generic_unmap_range (dev, map); + if (err) + return err; + +#ifdef HAVE_MTRR + if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { + sentry.type = MTRR_TYPE_WRBACK; + } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { + sentry.type = MTRR_TYPE_WRCOMB; } - dev->regions[ region ].memory = NULL; + if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { + if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) { + /* FIXME: Should we report an error in this case? + */ + fprintf(stderr, "error setting MTRR " + "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", + sentry.base, sentry.size, sentry.type, + strerror(errno), errno); +/* err = errno;*/ + } + } +#endif return err; } diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h index afbeb75..9eb6062 100644 --- a/src/pciaccess_private.h +++ b/src/pciaccess_private.h @@ -30,15 +30,20 @@ */ +struct pci_device_mapping; + int pci_fill_capabilities_generic( struct pci_device * dev ); +int pci_device_generic_unmap_range(struct pci_device *dev, + struct pci_device_mapping *map); struct pci_system_methods { void (*destroy)( void ); void (*destroy_device)( struct pci_device * dev ); int (*read_rom)( struct pci_device * dev, void * buffer ); int (*probe)( struct pci_device * dev ); - int (*map)( struct pci_device * dev, unsigned region, int write_enable ); - int (*unmap)( struct pci_device * dev, unsigned region ); + int (*map_range)(struct pci_device *dev, struct pci_device_mapping *map); + int (*unmap_range)(struct pci_device * dev, + struct pci_device_mapping *map); int (*read)(struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ); @@ -49,6 +54,14 @@ struct pci_system_methods { int (*fill_capabilities)( struct pci_device * dev ); }; +struct pci_device_mapping { + pciaddr_t base; + pciaddr_t size; + unsigned region; + unsigned flags; + void *memory; +}; + struct pci_device_private { struct pci_device base; const char * device_string; @@ -76,7 +89,14 @@ struct pci_device_private { struct pci_pcmcia_bridge_info * pcmcia; } bridge; /*@}*/ - + + /** + * \name Mappings active on this device. + */ + /*@{*/ + struct pci_device_mapping *mappings; + unsigned num_mappings; + /*@}*/ }; @@ -98,6 +118,10 @@ struct pci_system { * Array of known devices. */ struct pci_device_private * devices; + +#ifdef HAVE_MTRR + int mtrr_fd; +#endif }; extern struct pci_system * pci_sys; diff --git a/src/scanpci.c b/src/scanpci.c index 2c49d80..eb6e94f 100644 --- a/src/scanpci.c +++ b/src/scanpci.c @@ -128,10 +128,10 @@ print_pci_device( struct pci_device * dev, int verbose ) pci_device_probe( dev ); for ( i = 0 ; i < 6 ; i++ ) { if ( dev->regions[i].base_addr != 0 ) { - printf( " BASE%u 0x%08x addr 0x%08x %s", + printf( " BASE%u 0x%08x SIZE %d %s", i, - 0, (intptr_t) dev->regions[i].base_addr, + (size_t) dev->regions[i].size, (dev->regions[i].is_IO) ? "I/O" : "MEM" ); if ( ! dev->regions[i].is_IO ) { diff --git a/src/solx_devfs.c b/src/solx_devfs.c index 058b604..a500327 100644 --- a/src/solx_devfs.c +++ b/src/solx_devfs.c @@ -144,8 +144,8 @@ static const struct pci_system_methods solx_devfs_methods = { .destroy_device = NULL, .read_rom = pci_device_solx_devfs_read_rom, .probe = pci_device_solx_devfs_probe, - .map = pci_device_solx_devfs_map_region, - .unmap = pci_device_solx_devfs_unmap_region, + .map_range = pci_device_solx_devfs_map_range, + .unmap_range = pci_device_generic_unmap_range, .read = pci_device_solx_devfs_read, .write = pci_device_solx_devfs_write, @@ -836,55 +836,39 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data, } -/* - * Solaris Version +/** + * Map a memory region for a device using /dev/xsvc. + * + * \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. */ static int -pci_device_solx_devfs_map_region( struct pci_device * dev, unsigned region, - int write_enable ) +pci_device_solx_devfs_map_range(struct pci_device *dev, + struct pci_device_mapping *map) { + const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) + ? (PROT_READ | PROT_WRITE) : PROT_READ; + int err; + if (xsvc_fd < 0) { if ((xsvc_fd = open("/dev/xsvc", O_RDWR)) < 0) { - (void) fprintf(stderr, "can not open xsvc driver\n"); - - return (-1); + (void) fprintf(stderr, "can not open xsvc driver\n"); + return errno; } } - dev->regions[region].memory = mmap(NULL, dev->regions[region].size, - (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, - xsvc_fd, dev->regions[region].base_addr); + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, xsvs_fd, + map->base); + if (map->memory == MAP_FAILED) { + err = errno; - if (dev->regions[region].memory == MAP_FAILED) { - dev->regions[region].memory = 0; - (void) fprintf(stderr, "map rom region =%x failed", - dev->regions[region].base_addr); - return (-1); + map->base); } - - /* - * Still used xsvc to do the user space mapping - */ - return (0); -} - - -/* - * Solaris version - */ -static int -pci_device_solx_devfs_unmap_region( struct pci_device * dev, unsigned region ) -{ - int err = 0; - if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size ) - == -1 ) { - err = errno; - } - - dev->regions[ region ].memory = NULL; - - return (err); + return err; } |