/*
* drivers/pci/pci-sysfs.c
*
* (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2002-2004 IBM Corp.
* (C) Copyright 2003 Matthew Wilcox
* (C) Copyright 2003 Hewlett-Packard
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
*
* File attributes for PCI devices
*
* Modeled after usb's driverfs.c
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/stat.h>
#include <linux/export.h>
#include <linux/topology.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/capability.h>
#include <linux/security.h>
#include <linux/pci-aspm.h>
#include <linux/slab.h>
#include <linux/vgaarb.h>
#include <linux/pm_runtime.h>
#include "pci.h"
static int sysfs_initialized; /* = 0 */
/* show configuration fields */
#define pci_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct pci_dev *pdev; \
\
pdev = to_pci_dev (dev); \
return sprintf (buf, format_string, pdev->field); \
}
pci_config_attr(vendor, "0x%04x\n");
pci_config_attr(device, "0x%04x\n");
pci_config_attr(subsystem_vendor, "0x%04x\n");
pci_config_attr(subsystem_device, "0x%04x\n");
pci_config_attr(class, "0x%06x\n");
pci_config_attr(irq, "%u\n");
static ssize_t broken_parity_status_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf (buf, "%u\n", pdev->broken_parity_status);
}
static ssize_t broken_parity_status_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
pdev->broken_parity_status = !!val;
return count;
}
static ssize_t local_cpus_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct cpumask *mask;
int len;
#ifdef CONFIG_NUMA
mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
cpumask_of_node(dev_to_node(dev));
#else
mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
#endif
len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
buf[len++] = '\n';
buf[len] = '\0';
return len;
}
static ssize_t local_cpulist_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct cpumask *mask;
int len;
#ifdef CONFIG_NUMA
mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
cpumask_of_node(dev_to_node(dev));
#else
mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
#endif
len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
buf[len++] = '\n';
buf[len] = '\0';
return len;
}
/*
* PCI Bus Class Devices
*/
static ssize_t pci_bus_show_cpuaffinity(struct device *dev,
int type,
struct device_attribute *attr,
char *buf)
{
int ret;
const struct cpumask *cpumask;
cpumask = cpumask_of_pcibus(to_pci_bus(dev));
ret = type ?
cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask) :
cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask);
buf[ret++] = '\n';
buf[ret] = '\0';
return ret;
}
static inline ssize_t pci_bus_show_cpumaskaffinity(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return pci_bus_show_cpuaffinity(dev, 0, attr, buf);
}
static inline ssize_t pci_bus_show_cpulistaffinity(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return pci_bus_show_cpuaffinity(dev, 1, attr, buf);
}
/* show resources */
static ssize_t
resource_show(struct device * dev, struct device_attribute *attr, char * buf)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
char * str = buf;
int i;
int max;
resource_size_t start, end;
if (pci_dev->subordinate)
max = DEVICE_COUNT_RESOURCE;
else
max = PCI_BRIDGE_RESOURCES;
for (i = 0; i < max; i++) {
struct resource *res = &pci_dev->resource[i];
pci_resource_to_user(pci_dev, i, res, &start, &end);
str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n",
(unsigned long long)start,
(unsigned long long)end,
(unsigned long long)res->flags);
}
return (str - buf);
}
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n",
pci_dev->vendor, pci_dev->device,
pci_dev->subsystem_vendor, pci_dev->subsystem_device,
(u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
(u8)(pci_dev->class));
}
static ssize_t is_enabled_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;
ssize_t result = strict_strtoul(buf, 0, &val);
if (result < 0)
return result;
/* this can crash the machine when done on the "wrong" device */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!val) {
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
else
result = -EIO;
} else
result = pci_enable_device(pdev);
return result < 0 ? result : count;
}
static ssize_t is_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev;
pdev = to_pci_dev (dev);
return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt));
}
#ifdef CONFIG_NUMA
static ssize_t
numa_node_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf (buf, "%d\n", dev->numa_node);
}
#endif
static ssize_t
dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf (buf, "%d\n", fls64(pdev->dma_mask));
}
static ssize_t
consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask));
}
static ssize_t
msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
if (!pdev->subordinate)
return 0;
return sprintf (buf, "%u\n",
!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
}
static ssize_t
msi_bus_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
/* bad things may happen if the no_msi flag is changed
* while some drivers are loaded */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* Maybe pci devices without subordinate busses shouldn't even have this
* attribute in the first place? */
if (!pdev->subordinate)
return count;
/* Is the flag going to change, or keep the value it already had? */
if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
!!val) {
pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI,"
" bad things could happen\n", val ? "" : " not");
}
return count;
}
static DEFINE_MUTEX(pci_remove_rescan_mutex);
static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
size_t count)
{
unsigned long val;
struct pci_bus *b = NULL;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
while ((b = pci_find_next_bus(b)) != NULL)
pci_rescan_bus(b);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
}
struct bus_attribute pci_bus_attrs[] = {
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
__ATTR_NULL
};
static ssize_t
dev_rescan_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
struct pci_dev *pdev = to_pci_dev(dev);
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
pci_rescan_bus(pdev->bus);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
}
static void remove_callback(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
mutex_lock(&pci_remove_rescan_mutex);
pci_stop_and_remove_bus_device(pdev);
mutex_unlock(&pci_remove_rescan_mutex);
}
static ssize_t
remove_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
int ret = 0;
unsigned long val;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
/* An attribute cannot be unregistered by one of its own methods,
* so we have to use this roundabout approach.
*/
if (val)
ret = device_schedule_callback(dev, remove_callback);
if (ret)
count = ret;
return count;
}
static ssize_t
dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
struct pci_bus *bus = to_pci_bus(dev);
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
pci_rescan_bus_bridge_resize(bus->self);
else
pci_rescan_bus(bus);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
}
#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
static ssize_t d3cold_allowed_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
pdev->d3cold_allowed = !!val;
pm_runtime_resume(dev);
return count;
}
static ssize_t d3cold_allowed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf (buf, "%u\n", pdev->d3cold_allowed);
}
#endif
#ifdef CONFIG_PCI_IOV
static ssize_t sriov_totalvfs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
}
static ssize_t sriov_numvfs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
}
/*
* num_vfs > 0; number of VFs to enable
* num_vfs = 0; disable all VFs
*
* Note: SRIOV spec doesn't allow partial VF
* disable, so it's all or none.
*/
static ssize_t sriov_numvfs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
u16 num_vfs;
ret = kstrtou16(buf, 0, &num_vfs);
if (ret < 0)
return ret;
if (num_vfs > pci_sriov_get_totalvfs(pdev))
return -ERANGE;
if (num_vfs == pdev->sriov->num_VFs)
return count; /* no change */
/* is PF driver loaded w/callback */
if (!pdev->driver || !pdev->driver->sriov_configure) {
dev_info(&pdev->dev, "Driver doesn't support SRIOV configuration via sysfs\n");
return -ENOSYS;
}
if (num_vfs == 0) {
/* disable VFs */
ret = pdev->driver->sriov_configure(pdev, 0);
if (ret < 0)
return ret;
return count;
}
/* enable VFs */
if (pdev->sriov->num_VFs) {
dev_warn(&pdev->dev, "%d VFs already enabled. Disable before enabling %d VFs\n",
pdev->sriov->num_VFs, num_vfs);
return -EBUSY;
}
ret = pdev->driver->sriov_configure(pdev, num_vfs);
if (ret < 0)
return ret;
if (ret != num_vfs)
dev_warn(&pdev->dev, "%d VFs requested; only %d enabled\n",
num_vfs, ret);
return count;
}
static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs);
static struct device_attribute sriov_numvfs_attr =
__ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP),
sriov_numvfs_show, sriov_numvfs_store);
#endif /* CONFIG_PCI_IOV */
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
__ATTR_RO(device),
__ATTR_RO(subsystem_vendor),
__ATTR_RO(subsystem_device),
__ATTR_RO(class),
__ATTR_RO(irq),
__ATTR_RO(local_cpus),
__ATTR_RO(local_cpulist),
__ATTR_RO(modalias),
#ifdef CONFIG_NUMA
__ATTR_RO(numa_node),
#endif
__ATTR_RO(dma_mask_bits),
__ATTR_RO(consistent_dma_mask_bits),
__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store),
__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store),
#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
__ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store),
#endif
__ATTR_NULL,
};
struct device_attribute pcibus_dev_attrs[] = {
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store),
__ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpumaskaffinity, NULL),
__ATTR(cpulistaffinity, S_IRUGO, pci_bus_show_cpulistaffinity, NULL),
__ATTR_NULL,
};
static ssize_t
boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_dev *vga_dev = vga_default_device();
if (vga_dev)
return sprintf(buf, "%u\n", (pdev == vga_dev));
return sprintf(buf, "%u\n",
!!(pdev->resource[PCI_ROM_RESOURCE].flags &
IORESOURCE_ROM_SHADOW));
}
struct device_attribute vga_attr = __ATTR_RO(boot_vga);
static ssize_t
pci_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64;
loff_t init_off = off;
u8 *data = (u8*) buf;
/* Several chips lock up trying to read undefined config space */
if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0) {
size = dev->cfg_size;
} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
size = 128;
}
if (off > size)
return 0;
if (off + count > size) {
size -= off;
count = size;
} else {
size = count;
}
pci_config_pm_runtime_get(dev);
if ((off & 1) && size) {
u8 val;
pci_user_read_config_byte(dev, off, &val);
data[off - init_off] = val;
off++;
size--;
}
if ((off & 3) && size > 2) {
u16 val;
pci_user_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
}
while (size > 3) {
u32 val;
pci_user_read_config_dword(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
data[off - init_off + 2] = (val >> 16) & 0xff;
data[off - init_off + 3] = (val >> 24) & 0xff;
off += 4;
size -= 4;
}
if (size >= 2) {
u16 val;
pci_user_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
}
if (size > 0) {
u8 val;
pci_user_read_config_byte(dev, off, &val);
data[off - init_off] = val;
off++;
--size;
}
pci_config_pm_runtime_put(dev);
return count;
}
static ssize_t
pci_write_config(struct file* filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count;
loff_t init_off = off;
u8 *data = (u8*) buf;