static int
pci_device_freebsd_unmap_range( struct pci_device *dev,
				struct pci_device_mapping *map )
{
    struct mem_range_desc mrd;
    struct mem_range_op mro;
    int fd;

    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");
	}
    }

    return pci_device_generic_unmap_range(dev, map);
}
Exemple #2
0
static int
pci_device_openbsd_unmap_range(struct pci_device *dev,
    struct pci_device_mapping *map)
{
#if defined(__i386__) || defined(__amd64__)
	struct mem_range_desc mr;
	struct mem_range_op mo;

	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
		mr.mr_base = map->base;
		mr.mr_len = map->size;
		mr.mr_flags = MDF_UNCACHEABLE;
		strlcpy(mr.mr_owner, "pciaccess", sizeof(mr.mr_owner));

		mo.mo_desc = &mr;
		mo.mo_arg[0] = MEMRANGE_SET_REMOVE;

		(void)ioctl(aperturefd, MEMRANGE_SET, &mo);
	}
#endif
	return pci_device_generic_unmap_range(dev, map);
}
/**
 * Map a memory region for a device using the Linux sysfs interface.
 *
 * \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_rrange, pci_device_linux_sysfs_unmap_range
 *
 * \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_map_range(struct pci_device *dev,
                                 struct pci_device_mapping *map)
{
    char name[256];
    int fd;
    int err = 0;
    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

    /* For WC mappings, try sysfs resourceN_wc file first */
    if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) &&
	!pci_device_linux_sysfs_map_range_wc(dev, map))
	    return 0;

    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 | O_CLOEXEC);
    if (fd == -1) {
        return errno;
    }


    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
    if (map->memory == MAP_FAILED) {
        map->memory = NULL;
	close(fd);
	return 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;
    }

    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;*/
	}
	/* KLUDGE ALERT -- rewrite the PTEs to turn off the CD and WT bits */
	mprotect (map->memory, map->size, PROT_NONE);
	err = mprotect (map->memory, map->size, PROT_READ|PROT_WRITE);

	if (err != 0) {
	    fprintf(stderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n",
		    strerror(errno));
	    fprintf(stderr, "remapping without mprotect performance kludge.\n");

	    munmap(map->memory, map->size);
	    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
	    if (map->memory == MAP_FAILED) {
		map->memory = NULL;
		close(fd);
		return errno;
	    }
	}
    }
#endif

    close(fd);

    return 0;
}

/**
 * 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_map_rrange, pci_device_linux_sysfs_map_range
 *
 * \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_range(struct pci_device *dev,
				   struct pci_device_mapping *map)
{
    int err = 0;
#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;
    }

    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;
}

static void pci_device_linux_sysfs_enable(struct pci_device *dev)
{
    char name[256];
    int fd;

    snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable",
	      SYS_BUS_PCI,
	      dev->domain,
	      dev->bus,
	      dev->dev,
	      dev->func );

    fd = open( name, O_RDWR | O_CLOEXEC);
    if (fd == -1)
       return;

    write( fd, "1", 1 );
    close(fd);
}