diff options
Diffstat (limited to 'drivers/pci/proc.c')
| -rw-r--r-- | drivers/pci/proc.c | 162 |
1 files changed, 95 insertions, 67 deletions
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index cdc7836d7e3d..9348a0fb8084 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Procfs interface for the PCI bus. + * Procfs interface for the PCI bus * - * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz> + * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz> */ #include <linux/init.h> @@ -11,23 +12,23 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/capability.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> +#include <linux/security.h> #include <asm/byteorder.h> #include "pci.h" static int proc_initialized; /* = 0 */ -static loff_t -proc_bus_pci_lseek(struct file *file, loff_t off, int whence) +static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) { - struct pci_dev *dev = PDE_DATA(file_inode(file)); + struct pci_dev *dev = pde_data(file_inode(file)); return fixed_size_llseek(file, off, whence, dev->cfg_size); } -static ssize_t -proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) { - struct pci_dev *dev = PDE_DATA(file_inode(file)); + struct pci_dev *dev = pde_data(file_inode(file)); unsigned int pos = *ppos; unsigned int cnt, size; @@ -52,7 +53,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp nbytes = size - pos; cnt = nbytes; - if (!access_ok(VERIFY_WRITE, buf, cnt)) + if (!access_ok(buf, cnt)) return -EINVAL; pci_config_pm_runtime_get(dev); @@ -82,6 +83,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp buf += 4; pos += 4; cnt -= 4; + cond_resched(); } if (cnt >= 2) { @@ -97,9 +99,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp unsigned char val; pci_user_read_config_byte(dev, pos, &val); __put_user(val, buf); - buf++; pos++; - cnt--; } pci_config_pm_runtime_put(dev); @@ -108,14 +108,18 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp return nbytes; } -static ssize_t -proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos) +static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) { struct inode *ino = file_inode(file); - struct pci_dev *dev = PDE_DATA(ino); + struct pci_dev *dev = pde_data(ino); int pos = *ppos; int size = dev->cfg_size; - int cnt; + int cnt, ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; if (pos >= size) return 0; @@ -125,7 +129,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof nbytes = size - pos; cnt = nbytes; - if (!access_ok(VERIFY_READ, buf, cnt)) + if (!access_ok(buf, cnt)) return -EINVAL; pci_config_pm_runtime_get(dev); @@ -170,9 +174,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof unsigned char val; __get_user(val, buf); pci_user_write_config_byte(dev, pos, val); - buf++; pos++; - cnt--; } pci_config_pm_runtime_put(dev); @@ -182,20 +184,26 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof return nbytes; } +#ifdef HAVE_PCI_MMAP struct pci_filp_private { enum pci_mmap_state mmap_state; int write_combine; }; +#endif /* HAVE_PCI_MMAP */ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct pci_dev *dev = PDE_DATA(file_inode(file)); + struct pci_dev *dev = pde_data(file_inode(file)); #ifdef HAVE_PCI_MMAP struct pci_filp_private *fpriv = file->private_data; #endif /* HAVE_PCI_MMAP */ int ret = 0; + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; + switch (cmd) { case PCIIOC_CONTROLLER: ret = pci_domain_nr(dev->bus); @@ -203,6 +211,8 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, #ifdef HAVE_PCI_MMAP case PCIIOC_MMAP_IS_IO: + if (!arch_can_pci_mmap_io()) + return -EINVAL; fpriv->mmap_state = pci_mmap_io; break; @@ -211,18 +221,20 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, break; case PCIIOC_WRITE_COMBINE: - if (arg) - fpriv->write_combine = 1; - else - fpriv->write_combine = 0; - break; - + if (arch_can_pci_mmap_wc()) { + if (arg) + fpriv->write_combine = 1; + else + fpriv->write_combine = 0; + break; + } + /* If arch decided it can't, fall through... */ + fallthrough; #endif /* HAVE_PCI_MMAP */ - default: ret = -EINVAL; break; - }; + } return ret; } @@ -230,25 +242,53 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, #ifdef HAVE_PCI_MMAP static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) { - struct pci_dev *dev = PDE_DATA(file_inode(file)); + struct pci_dev *dev = pde_data(file_inode(file)); struct pci_filp_private *fpriv = file->private_data; - int i, ret; + resource_size_t start, end; + int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM; - if (!capable(CAP_SYS_RAWIO)) + if (!capable(CAP_SYS_RAWIO) || + security_locked_down(LOCKDOWN_PCI_ACCESS)) return -EPERM; + /* Skip devices with non-mappable BARs */ + if (dev->non_mappable_bars) + return -EINVAL; + + if (fpriv->mmap_state == pci_mmap_io) { + if (!arch_can_pci_mmap_io()) + return -EINVAL; + res_bit = IORESOURCE_IO; + } + /* Make sure the caller is mapping a real resource for this device */ - for (i = 0; i < PCI_ROM_RESOURCE; i++) { - if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) + for (i = 0; i < PCI_STD_NUM_BARS; i++) { + if (dev->resource[i].flags & res_bit && + pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) break; } - if (i >= PCI_ROM_RESOURCE) + if (i >= PCI_STD_NUM_BARS) return -ENODEV; - ret = pci_mmap_page_range(dev, vma, - fpriv->mmap_state, - fpriv->write_combine); + if (fpriv->mmap_state == pci_mmap_mem && + fpriv->write_combine) { + if (dev->resource[i].flags & IORESOURCE_PREFETCH) + write_combine = 1; + else + return -EINVAL; + } + + if (dev->resource[i].flags & IORESOURCE_MEM && + iomem_is_exclusive(dev->resource[i].start)) + return -EINVAL; + + pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); + + /* Adjust vm_pgoff to be the offset within the resource */ + vma->vm_pgoff -= start >> PAGE_SHIFT; + ret = pci_mmap_resource_range(dev, i, vma, + fpriv->mmap_state, write_combine); if (ret < 0) return ret; @@ -266,6 +306,7 @@ static int proc_bus_pci_open(struct inode *inode, struct file *file) fpriv->write_combine = 0; file->private_data = fpriv; + file->f_mapping = iomem_get_mapping(); return 0; } @@ -279,19 +320,20 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file) } #endif /* HAVE_PCI_MMAP */ -static const struct file_operations proc_bus_pci_operations = { - .owner = THIS_MODULE, - .llseek = proc_bus_pci_lseek, - .read = proc_bus_pci_read, - .write = proc_bus_pci_write, - .unlocked_ioctl = proc_bus_pci_ioctl, - .compat_ioctl = proc_bus_pci_ioctl, +static const struct proc_ops proc_bus_pci_ops = { + .proc_lseek = proc_bus_pci_lseek, + .proc_read = proc_bus_pci_read, + .proc_write = proc_bus_pci_write, + .proc_ioctl = proc_bus_pci_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = proc_bus_pci_ioctl, +#endif #ifdef HAVE_PCI_MMAP - .open = proc_bus_pci_open, - .release = proc_bus_pci_release, - .mmap = proc_bus_pci_mmap, + .proc_open = proc_bus_pci_open, + .proc_release = proc_bus_pci_release, + .proc_mmap = proc_bus_pci_mmap, #ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA - .get_unmapped_area = get_pci_unmapped_area, + .proc_get_unmapped_area = get_pci_unmapped_area, #endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */ #endif /* HAVE_PCI_MMAP */ }; @@ -360,7 +402,7 @@ static int show_device(struct seq_file *m, void *v) } seq_putc(m, '\t'); if (drv) - seq_printf(m, "%s", drv->name); + seq_puts(m, drv->name); seq_putc(m, '\n'); return 0; } @@ -397,7 +439,7 @@ int pci_proc_attach_device(struct pci_dev *dev) sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir, - &proc_bus_pci_operations, dev); + &proc_bus_pci_ops, dev); if (!e) return -ENOMEM; proc_set_size(e, dev->cfg_size); @@ -413,36 +455,22 @@ int pci_proc_detach_device(struct pci_dev *dev) return 0; } -int pci_proc_detach_bus(struct pci_bus* bus) +int pci_proc_detach_bus(struct pci_bus *bus) { proc_remove(bus->procdir); return 0; } -static int proc_bus_pci_dev_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &proc_bus_pci_devices_op); -} -static const struct file_operations proc_bus_pci_dev_operations = { - .owner = THIS_MODULE, - .open = proc_bus_pci_dev_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - static int __init pci_proc_init(void) { struct pci_dev *dev = NULL; proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); - proc_create("devices", 0, proc_bus_pci_dir, - &proc_bus_pci_dev_operations); + proc_create_seq("devices", 0, proc_bus_pci_dir, + &proc_bus_pci_devices_op); proc_initialized = 1; for_each_pci_dev(dev) pci_proc_attach_device(dev); return 0; } - device_initcall(pci_proc_init); - |
