Commit 8633328be242677fdedc42052838dd0608e7f342
Committed by
Jesse Barnes
1 parent
2491762cfb
Exists in
master
and in
7 other branches
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; |