diff options
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | include/pciaccess.h | 35 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-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 | 129 | ||||
-rw-r--r-- | src/pciaccess_private.h | 30 | ||||
-rw-r--r-- | src/solx_devfs.c | 64 |
9 files changed, 365 insertions, 221 deletions
diff --git a/configure.ac b/configure.ac index e48a893..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.9.1, [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..3146a35 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) /** diff --git a/src/Makefile.am b/src/Makefile.am index 11e3145..e743dbf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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..1c4e02e 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,8 @@ 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_unmap_region( struct pci_device * dev, - unsigned region ); +static int pci_device_linux_sysfs_map_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 +75,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_generic_unmap_range, .read = pci_device_linux_sysfs_read, .write = pci_device_linux_sysfs_write, @@ -114,6 +118,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 +150,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 ); @@ -471,14 +479,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,68 +493,66 @@ 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; - - - 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; + 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; } - 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; + map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); + if (map->memory == MAP_FAILED) { + err = errno; + map->memory = NULL; } - close( fd ); - return err; -} - + close(fd); -/** - * 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. - * - * \return - * Zero on success or an \c errno value on failure. - * - * \sa pci_device_unmap_region, pci_device_linux_sysfs_map_region - * - * \todo - * Some older 2.6.x kernels don't implement the resourceN files. On those - * systems /dev/mem must be used. On these systems it is also possible that - * \c mmap64 may need to be used. - */ -static int -pci_device_linux_sysfs_unmap_region( struct pci_device * dev, unsigned region ) -{ - int err = 0; - - if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size ) - == -1 ) { - err = errno; +#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) { + 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; } 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/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; } |