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>
This commit is contained in:
parent
2491762cfb
commit
8633328be2
|
@ -39,7 +39,7 @@ files, each with their own function.
|
|||
local_cpus nearby CPU mask (cpumask, ro)
|
||||
remove remove device from kernel's list (ascii, wo)
|
||||
resource PCI resource host addresses (ascii, ro)
|
||||
resource0..N PCI resource N, if present (binary, mmap)
|
||||
resource0..N PCI resource N, if present (binary, mmap, rw[1])
|
||||
resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap)
|
||||
rom PCI ROM resource, if present (binary, ro)
|
||||
subsystem_device PCI subsystem device (ascii, ro)
|
||||
|
@ -54,13 +54,16 @@ files, each with their own function.
|
|||
binary - file contains binary data
|
||||
cpumask - file contains a cpumask type
|
||||
|
||||
[1] rw for RESOURCE_IO (I/O port) regions only
|
||||
|
||||
The read only files are informational, writes to them will be ignored, with
|
||||
the exception of the 'rom' file. Writable files can be used to perform
|
||||
actions on the device (e.g. changing config space, detaching a device).
|
||||
mmapable files are available via an mmap of the file at offset 0 and can be
|
||||
used to do actual device programming from userspace. Note that some platforms
|
||||
don't support mmapping of certain resources, so be sure to check the return
|
||||
value from any attempted mmap.
|
||||
value from any attempted mmap. The most notable of these are I/O port
|
||||
resources, which also provide read/write access.
|
||||
|
||||
The 'enable' file provides a counter that indicates how many times the device
|
||||
has been enabled. If the 'enable' file currently returns '4', and a '1' is
|
||||
|
|
|
@ -778,6 +778,70 @@ pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
|
|||
return pci_mmap_resource(kobj, attr, vma, 1);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
pci_resource_io(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count, bool write)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
|
||||
struct device, kobj));
|
||||
struct resource *res = attr->private;
|
||||
unsigned long port = off;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_ROM_RESOURCE; i++)
|
||||
if (res == &pdev->resource[i])
|
||||
break;
|
||||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
|
||||
port += pci_resource_start(pdev, i);
|
||||
|
||||
if (port > pci_resource_end(pdev, i))
|
||||
return 0;
|
||||
|
||||
if (port + count - 1 > pci_resource_end(pdev, i))
|
||||
return -EINVAL;
|
||||
|
||||
switch (count) {
|
||||
case 1:
|
||||
if (write)
|
||||
outb(*(u8 *)buf, port);
|
||||
else
|
||||
*(u8 *)buf = inb(port);
|
||||
return 1;
|
||||
case 2:
|
||||
if (write)
|
||||
outw(*(u16 *)buf, port);
|
||||
else
|
||||
*(u16 *)buf = inw(port);
|
||||
return 2;
|
||||
case 4:
|
||||
if (write)
|
||||
outl(*(u32 *)buf, port);
|
||||
else
|
||||
*(u32 *)buf = inl(port);
|
||||
return 4;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
pci_read_resource_io(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pci_resource_io(filp, kobj, attr, buf, off, count, false);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
pci_write_resource_io(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pci_resource_io(filp, kobj, attr, buf, off, count, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_remove_resource_files - cleanup resource files
|
||||
* @pdev: dev to cleanup
|
||||
|
@ -828,6 +892,10 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
|
|||
sprintf(res_attr_name, "resource%d", num);
|
||||
res_attr->mmap = pci_mmap_resource_uc;
|
||||
}
|
||||
if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
|
||||
res_attr->read = pci_read_resource_io;
|
||||
res_attr->write = pci_write_resource_io;
|
||||
}
|
||||
res_attr->attr.name = res_attr_name;
|
||||
res_attr->attr.mode = S_IRUSR | S_IWUSR;
|
||||
res_attr->size = pci_resource_len(pdev, num);
|
||||
|
|
Loading…
Reference in New Issue