Commit 8633328be242677fdedc42052838dd0608e7f342

Authored by Alex Williamson
Committed by Jesse Barnes
1 parent 2491762cfb

PCI: Allow read/write access to sysfs I/O port resources

PCI sysfs resource files currently only allow mmap'ing.  On x86 this
works fine for memory backed BARs, but doesn't work at all for I/O
port backed BARs.  Add read/write to I/O port PCI sysfs resource
files to allow userspace access to these device regions.

Acked-by: Chris Wright <chrisw@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Showing 2 changed files with 73 additions and 2 deletions Side-by-side Diff

Documentation/filesystems/sysfs-pci.txt
... ... @@ -39,7 +39,7 @@
39 39 local_cpus nearby CPU mask (cpumask, ro)
40 40 remove remove device from kernel's list (ascii, wo)
41 41 resource PCI resource host addresses (ascii, ro)
42   - resource0..N PCI resource N, if present (binary, mmap)
  42 + resource0..N PCI resource N, if present (binary, mmap, rw[1])
43 43 resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap)
44 44 rom PCI ROM resource, if present (binary, ro)
45 45 subsystem_device PCI subsystem device (ascii, ro)
46 46  
... ... @@ -54,13 +54,16 @@
54 54 binary - file contains binary data
55 55 cpumask - file contains a cpumask type
56 56  
  57 +[1] rw for RESOURCE_IO (I/O port) regions only
  58 +
57 59 The read only files are informational, writes to them will be ignored, with
58 60 the exception of the 'rom' file. Writable files can be used to perform
59 61 actions on the device (e.g. changing config space, detaching a device).
60 62 mmapable files are available via an mmap of the file at offset 0 and can be
61 63 used to do actual device programming from userspace. Note that some platforms
62 64 don't support mmapping of certain resources, so be sure to check the return
63   -value from any attempted mmap.
  65 +value from any attempted mmap. The most notable of these are I/O port
  66 +resources, which also provide read/write access.
64 67  
65 68 The 'enable' file provides a counter that indicates how many times the device
66 69 has been enabled. If the 'enable' file currently returns '4', and a '1' is
drivers/pci/pci-sysfs.c
... ... @@ -778,6 +778,70 @@
778 778 return pci_mmap_resource(kobj, attr, vma, 1);
779 779 }
780 780  
  781 +static ssize_t
  782 +pci_resource_io(struct file *filp, struct kobject *kobj,
  783 + struct bin_attribute *attr, char *buf,
  784 + loff_t off, size_t count, bool write)
  785 +{
  786 + struct pci_dev *pdev = to_pci_dev(container_of(kobj,
  787 + struct device, kobj));
  788 + struct resource *res = attr->private;
  789 + unsigned long port = off;
  790 + int i;
  791 +
  792 + for (i = 0; i < PCI_ROM_RESOURCE; i++)
  793 + if (res == &pdev->resource[i])
  794 + break;
  795 + if (i >= PCI_ROM_RESOURCE)
  796 + return -ENODEV;
  797 +
  798 + port += pci_resource_start(pdev, i);
  799 +
  800 + if (port > pci_resource_end(pdev, i))
  801 + return 0;
  802 +
  803 + if (port + count - 1 > pci_resource_end(pdev, i))
  804 + return -EINVAL;
  805 +
  806 + switch (count) {
  807 + case 1:
  808 + if (write)
  809 + outb(*(u8 *)buf, port);
  810 + else
  811 + *(u8 *)buf = inb(port);
  812 + return 1;
  813 + case 2:
  814 + if (write)
  815 + outw(*(u16 *)buf, port);
  816 + else
  817 + *(u16 *)buf = inw(port);
  818 + return 2;
  819 + case 4:
  820 + if (write)
  821 + outl(*(u32 *)buf, port);
  822 + else
  823 + *(u32 *)buf = inl(port);
  824 + return 4;
  825 + }
  826 + return -EINVAL;
  827 +}
  828 +
  829 +static ssize_t
  830 +pci_read_resource_io(struct file *filp, struct kobject *kobj,
  831 + struct bin_attribute *attr, char *buf,
  832 + loff_t off, size_t count)
  833 +{
  834 + return pci_resource_io(filp, kobj, attr, buf, off, count, false);
  835 +}
  836 +
  837 +static ssize_t
  838 +pci_write_resource_io(struct file *filp, struct kobject *kobj,
  839 + struct bin_attribute *attr, char *buf,
  840 + loff_t off, size_t count)
  841 +{
  842 + return pci_resource_io(filp, kobj, attr, buf, off, count, true);
  843 +}
  844 +
781 845 /**
782 846 * pci_remove_resource_files - cleanup resource files
783 847 * @pdev: dev to cleanup
... ... @@ -827,6 +891,10 @@
827 891 pdev->res_attr[num] = res_attr;
828 892 sprintf(res_attr_name, "resource%d", num);
829 893 res_attr->mmap = pci_mmap_resource_uc;
  894 + }
  895 + if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
  896 + res_attr->read = pci_read_resource_io;
  897 + res_attr->write = pci_write_resource_io;
830 898 }
831 899 res_attr->attr.name = res_attr_name;
832 900 res_attr->attr.mode = S_IRUSR | S_IWUSR;