/*
* (C) Copyright IBM Corporation 2006
* All Rights Reserved.
+ * Copyright 2012 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
#include <dirent.h>
#include <errno.h>
+#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
+#include <sys/io.h>
+#else
+#define inb(x) -1
+#define inw(x) -1
+#define inl(x) -1
+#define outb(x,y) do {} while (0)
+#define outw(x,y) do {} while (0)
+#define outl(x,y) do {} while (0)
+#define iopl(x) -1
+#endif
+
#include "config.h"
#ifdef HAVE_MTRR
/* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
* can be accessed using this interface.
*/
-
+
if ( stat( SYS_BUS_PCI, & st ) == 0 ) {
pci_sys = calloc( 1, sizeof( struct pci_system ) );
if ( pci_sys != NULL ) {
pci_sys->methods = & linux_sysfs_methods;
#ifdef HAVE_MTRR
- pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY);
+ pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY | O_CLOEXEC);
#endif
err = populate_entries(pci_sys);
}
static int
scan_sys_pci_filter( const struct dirent * d )
{
- return !((strcmp( d->d_name, "." ) == 0)
+ return !((strcmp( d->d_name, "." ) == 0)
|| (strcmp( d->d_name, ".." ) == 0));
}
int
populate_entries( struct pci_system * p )
{
- struct dirent ** devices;
+ struct dirent ** devices = NULL;
int n;
int i;
int err = 0;
* parsing issues and non-root users can write to PCI config
* registers, we use a different file in the device's sysfs
* directory called "resource".
- *
+ *
* The resource file contains all of the needed information in
* a format that is consistent across all platforms. Each BAR
* and the expansion ROM have a single line of data containing
dev->bus,
dev->dev,
dev->func );
- fd = open( name, O_RDONLY );
+ fd = open( name, O_RDONLY | O_CLOEXEC);
if ( fd != -1 ) {
char * next;
pciaddr_t low_addr;
dev->regions[i].base_addr = strtoull( next, & next, 16 );
high_addr = strtoull( next, & next, 16 );
flags = strtoull( next, & next, 16 );
-
+
if ( dev->regions[i].base_addr != 0 ) {
- dev->regions[i].size = (high_addr
+ dev->regions[i].size = (high_addr
- dev->regions[i].base_addr) + 1;
dev->regions[i].is_IO = (flags & 0x01);
dev->bus,
dev->dev,
dev->func );
-
- fd = open( name, O_RDWR );
+
+ fd = open( name, O_RDWR | O_CLOEXEC);
if ( fd == -1 ) {
#ifdef LINUX_ROM
/* If reading the ROM using sysfs fails, fall back to the old
total_bytes += bytes;
}
-
+
lseek( fd, 0, SEEK_SET );
write( fd, "0", 1 );
dev->dev,
dev->func );
- fd = open( name, O_RDONLY );
+ fd = open( name, O_RDONLY | O_CLOEXEC);
if ( fd == -1 ) {
return errno;
}
/* If zero bytes were read, then we assume it's the end of the
* config file.
*/
- if ( bytes <= 0 ) {
+ if (bytes == 0)
+ break;
+ if ( bytes < 0 ) {
err = errno;
break;
}
offset += bytes;
data_bytes += bytes;
}
-
+
if ( bytes_read != NULL ) {
*bytes_read = size - temp_size;
}
dev->dev,
dev->func );
- fd = open( name, O_WRONLY );
+ fd = open( name, O_WRONLY | O_CLOEXEC);
if ( fd == -1 ) {
return errno;
}
/* If zero bytes were written, then we assume it's the end of the
* config file.
*/
- if ( bytes <= 0 ) {
+ if ( bytes == 0 )
+ break;
+ if ( bytes < 0 ) {
err = errno;
break;
}
offset += bytes;
data_bytes += bytes;
}
-
+
if ( bytes_written != NULL ) {
*bytes_written = size - temp_size;
}
{
char name[256];
int fd;
- 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;
- const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ 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;
dev->dev,
dev->func,
map->region);
- fd = open(name, open_flags);
+ fd = open(name, open_flags | O_CLOEXEC);
if (fd == -1)
return errno;
/**
* 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.
*
char name[256];
int fd;
int err = 0;
- 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;
- const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ 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
dev->func,
map->region);
- fd = open(name, open_flags);
+ fd = open(name, open_flags | O_CLOEXEC);
if (fd == -1) {
return errno;
}
/**
* 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.
*
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;
dev->bus,
dev->dev,
dev->func );
-
- fd = open( name, O_RDWR );
+
+ fd = open( name, O_RDWR | O_CLOEXEC);
if (fd == -1)
return;
dev->bus,
dev->dev,
dev->func );
-
- fd = open( name, O_RDONLY );
+
+ fd = open( name, O_RDONLY | O_CLOEXEC);
if (fd == -1)
return 0;
dev->bus,
dev->dev,
dev->func );
-
+
ret = stat(name, &dummy);
if (ret < 0)
return 0;
snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
- ret->fd = open(name, O_RDWR);
+ ret->fd = open(name, O_RDWR | O_CLOEXEC);
if (ret->fd < 0)
return NULL;
snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io",
dev->domain, dev->bus);
- ret->fd = open(name, O_RDWR);
+ ret->fd = open(name, O_RDWR | O_CLOEXEC);
if (ret->fd >= 0)
break;
dev = pci_device_get_parent_bridge(dev);
}
- /* If not, /dev/port is the best we can do */
- if (!dev)
- ret->fd = open("/dev/port", O_RDWR);
+ /*
+ * You would think you'd want to use /dev/port here. Don't make that
+ * mistake, /dev/port only does byte-wide i/o cycles which means it
+ * doesn't work. If you think this is stupid, well, you're right.
+ */
- if (ret->fd < 0)
- return NULL;
+ /* If we've no other choice, iopl */
+ if (ret->fd < 0) {
+ if (iopl(3))
+ return NULL;
+ }
ret->base = base;
ret->size = size;
pci_device_linux_sysfs_close_io(struct pci_device *dev,
struct pci_io_handle *handle)
{
- close(handle->fd);
+ if (handle->fd > -1)
+ close(handle->fd);
}
static uint32_t
{
uint32_t ret;
- pread(handle->fd, &ret, 4, port + handle->base);
+ if (handle->fd > -1)
+ pread(handle->fd, &ret, 4, port + handle->base);
+ else
+ ret = inl(port + handle->base);
return ret;
}
{
uint16_t ret;
- pread(handle->fd, &ret, 2, port + handle->base);
+ if (handle->fd > -1)
+ pread(handle->fd, &ret, 2, port + handle->base);
+ else
+ ret = inw(port + handle->base);
return ret;
}
{
uint8_t ret;
- pread(handle->fd, &ret, 1, port + handle->base);
+ if (handle->fd > -1)
+ pread(handle->fd, &ret, 1, port + handle->base);
+ else
+ ret = inb(port + handle->base);
return ret;
}
pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port,
uint32_t data)
{
- pwrite(handle->fd, &data, 4, port + handle->base);
+ if (handle->fd > -1)
+ pwrite(handle->fd, &data, 4, port + handle->base);
+ else
+ outl(data, port + handle->base);
}
static void
pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port,
uint16_t data)
{
- pwrite(handle->fd, &data, 2, port + handle->base);
+ if (handle->fd > -1)
+ pwrite(handle->fd, &data, 2, port + handle->base);
+ else
+ outw(data, port + handle->base);
}
static void
pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port,
uint8_t data)
{
- pwrite(handle->fd, &data, 1, port + handle->base);
+ if (handle->fd > -1)
+ pwrite(handle->fd, &data, 1, port + handle->base);
+ else
+ outb(data, port + handle->base);
+}
+
+static int
+pci_device_linux_sysfs_map_legacy(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags, void **addr)
+{
+ char name[PATH_MAX];
+ int flags = O_RDONLY;
+ int prot = PROT_READ;
+ int fd;
+ int ret=0;
+
+ if (map_flags & PCI_DEV_MAP_FLAG_WRITABLE) {
+ flags = O_RDWR; /* O_RDWR != O_WRONLY | O_RDONLY */;
+ prot |= PROT_WRITE;
+ }
+
+ /* First check if there's a legacy memory method for the device */
+ while (dev) {
+ snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_mem",
+ dev->domain, dev->bus);
+
+ fd = open(name, flags | O_CLOEXEC);
+ if (fd >= 0)
+ break;
+
+ dev = pci_device_get_parent_bridge(dev);
+ }
+
+ /* If not, /dev/mem is the best we can do */
+ if (!dev)
+ fd = open("/dev/mem", flags | O_CLOEXEC);
+
+ if (fd < 0)
+ return errno;
+
+ *addr = mmap(NULL, size, prot, MAP_SHARED, fd, base);
+ if (*addr == MAP_FAILED) {
+ ret = errno;
+ }
+
+ close(fd);
+ return ret;
+}
+
+static int
+pci_device_linux_sysfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size)
+{
+ return munmap(addr, size);
+}
+
+
+static void
+pci_system_linux_destroy(void)
+{
+#ifdef HAVE_MTRR
+ if (pci_sys->mtrr_fd != -1)
+ close(pci_sys->mtrr_fd);
+#endif
}
static const struct pci_system_methods linux_sysfs_methods = {
- .destroy = NULL,
+ .destroy = pci_system_linux_destroy,
.destroy_device = NULL,
.read_rom = pci_device_linux_sysfs_read_rom,
.probe = pci_device_linux_sysfs_probe,
.write32 = pci_device_linux_sysfs_write32,
.write16 = pci_device_linux_sysfs_write16,
.write8 = pci_device_linux_sysfs_write8,
+
+ .map_legacy = pci_device_linux_sysfs_map_legacy,
+ .unmap_legacy = pci_device_linux_sysfs_unmap_legacy,
};