162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Procfs interface for the PCI bus 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/proc_fs.h> 1362306a36Sopenharmony_ci#include <linux/seq_file.h> 1462306a36Sopenharmony_ci#include <linux/capability.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/security.h> 1762306a36Sopenharmony_ci#include <asm/byteorder.h> 1862306a36Sopenharmony_ci#include "pci.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int proc_initialized; /* = 0 */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct pci_dev *dev = pde_data(file_inode(file)); 2562306a36Sopenharmony_ci return fixed_size_llseek(file, off, whence, dev->cfg_size); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic ssize_t proc_bus_pci_read(struct file *file, char __user *buf, 2962306a36Sopenharmony_ci size_t nbytes, loff_t *ppos) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct pci_dev *dev = pde_data(file_inode(file)); 3262306a36Sopenharmony_ci unsigned int pos = *ppos; 3362306a36Sopenharmony_ci unsigned int cnt, size; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci * Normal users can read only the standardized portion of the 3762306a36Sopenharmony_ci * configuration space as several chips lock up when trying to read 3862306a36Sopenharmony_ci * undefined locations (think of Intel PIIX4 as a typical example). 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) 4262306a36Sopenharmony_ci size = dev->cfg_size; 4362306a36Sopenharmony_ci else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) 4462306a36Sopenharmony_ci size = 128; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci size = 64; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (pos >= size) 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci if (nbytes >= size) 5162306a36Sopenharmony_ci nbytes = size; 5262306a36Sopenharmony_ci if (pos + nbytes > size) 5362306a36Sopenharmony_ci nbytes = size - pos; 5462306a36Sopenharmony_ci cnt = nbytes; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!access_ok(buf, cnt)) 5762306a36Sopenharmony_ci return -EINVAL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pci_config_pm_runtime_get(dev); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if ((pos & 1) && cnt) { 6262306a36Sopenharmony_ci unsigned char val; 6362306a36Sopenharmony_ci pci_user_read_config_byte(dev, pos, &val); 6462306a36Sopenharmony_ci __put_user(val, buf); 6562306a36Sopenharmony_ci buf++; 6662306a36Sopenharmony_ci pos++; 6762306a36Sopenharmony_ci cnt--; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if ((pos & 3) && cnt > 2) { 7162306a36Sopenharmony_ci unsigned short val; 7262306a36Sopenharmony_ci pci_user_read_config_word(dev, pos, &val); 7362306a36Sopenharmony_ci __put_user(cpu_to_le16(val), (__le16 __user *) buf); 7462306a36Sopenharmony_ci buf += 2; 7562306a36Sopenharmony_ci pos += 2; 7662306a36Sopenharmony_ci cnt -= 2; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci while (cnt >= 4) { 8062306a36Sopenharmony_ci unsigned int val; 8162306a36Sopenharmony_ci pci_user_read_config_dword(dev, pos, &val); 8262306a36Sopenharmony_ci __put_user(cpu_to_le32(val), (__le32 __user *) buf); 8362306a36Sopenharmony_ci buf += 4; 8462306a36Sopenharmony_ci pos += 4; 8562306a36Sopenharmony_ci cnt -= 4; 8662306a36Sopenharmony_ci cond_resched(); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (cnt >= 2) { 9062306a36Sopenharmony_ci unsigned short val; 9162306a36Sopenharmony_ci pci_user_read_config_word(dev, pos, &val); 9262306a36Sopenharmony_ci __put_user(cpu_to_le16(val), (__le16 __user *) buf); 9362306a36Sopenharmony_ci buf += 2; 9462306a36Sopenharmony_ci pos += 2; 9562306a36Sopenharmony_ci cnt -= 2; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (cnt) { 9962306a36Sopenharmony_ci unsigned char val; 10062306a36Sopenharmony_ci pci_user_read_config_byte(dev, pos, &val); 10162306a36Sopenharmony_ci __put_user(val, buf); 10262306a36Sopenharmony_ci pos++; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci pci_config_pm_runtime_put(dev); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci *ppos = pos; 10862306a36Sopenharmony_ci return nbytes; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, 11262306a36Sopenharmony_ci size_t nbytes, loff_t *ppos) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct inode *ino = file_inode(file); 11562306a36Sopenharmony_ci struct pci_dev *dev = pde_data(ino); 11662306a36Sopenharmony_ci int pos = *ppos; 11762306a36Sopenharmony_ci int size = dev->cfg_size; 11862306a36Sopenharmony_ci int cnt, ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 12162306a36Sopenharmony_ci if (ret) 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (pos >= size) 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci if (nbytes >= size) 12762306a36Sopenharmony_ci nbytes = size; 12862306a36Sopenharmony_ci if (pos + nbytes > size) 12962306a36Sopenharmony_ci nbytes = size - pos; 13062306a36Sopenharmony_ci cnt = nbytes; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!access_ok(buf, cnt)) 13362306a36Sopenharmony_ci return -EINVAL; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pci_config_pm_runtime_get(dev); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if ((pos & 1) && cnt) { 13862306a36Sopenharmony_ci unsigned char val; 13962306a36Sopenharmony_ci __get_user(val, buf); 14062306a36Sopenharmony_ci pci_user_write_config_byte(dev, pos, val); 14162306a36Sopenharmony_ci buf++; 14262306a36Sopenharmony_ci pos++; 14362306a36Sopenharmony_ci cnt--; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if ((pos & 3) && cnt > 2) { 14762306a36Sopenharmony_ci __le16 val; 14862306a36Sopenharmony_ci __get_user(val, (__le16 __user *) buf); 14962306a36Sopenharmony_ci pci_user_write_config_word(dev, pos, le16_to_cpu(val)); 15062306a36Sopenharmony_ci buf += 2; 15162306a36Sopenharmony_ci pos += 2; 15262306a36Sopenharmony_ci cnt -= 2; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci while (cnt >= 4) { 15662306a36Sopenharmony_ci __le32 val; 15762306a36Sopenharmony_ci __get_user(val, (__le32 __user *) buf); 15862306a36Sopenharmony_ci pci_user_write_config_dword(dev, pos, le32_to_cpu(val)); 15962306a36Sopenharmony_ci buf += 4; 16062306a36Sopenharmony_ci pos += 4; 16162306a36Sopenharmony_ci cnt -= 4; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (cnt >= 2) { 16562306a36Sopenharmony_ci __le16 val; 16662306a36Sopenharmony_ci __get_user(val, (__le16 __user *) buf); 16762306a36Sopenharmony_ci pci_user_write_config_word(dev, pos, le16_to_cpu(val)); 16862306a36Sopenharmony_ci buf += 2; 16962306a36Sopenharmony_ci pos += 2; 17062306a36Sopenharmony_ci cnt -= 2; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (cnt) { 17462306a36Sopenharmony_ci unsigned char val; 17562306a36Sopenharmony_ci __get_user(val, buf); 17662306a36Sopenharmony_ci pci_user_write_config_byte(dev, pos, val); 17762306a36Sopenharmony_ci pos++; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci pci_config_pm_runtime_put(dev); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci *ppos = pos; 18362306a36Sopenharmony_ci i_size_write(ino, dev->cfg_size); 18462306a36Sopenharmony_ci return nbytes; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci#ifdef HAVE_PCI_MMAP 18862306a36Sopenharmony_cistruct pci_filp_private { 18962306a36Sopenharmony_ci enum pci_mmap_state mmap_state; 19062306a36Sopenharmony_ci int write_combine; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, 19562306a36Sopenharmony_ci unsigned long arg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct pci_dev *dev = pde_data(file_inode(file)); 19862306a36Sopenharmony_ci#ifdef HAVE_PCI_MMAP 19962306a36Sopenharmony_ci struct pci_filp_private *fpriv = file->private_data; 20062306a36Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 20162306a36Sopenharmony_ci int ret = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci switch (cmd) { 20862306a36Sopenharmony_ci case PCIIOC_CONTROLLER: 20962306a36Sopenharmony_ci ret = pci_domain_nr(dev->bus); 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci#ifdef HAVE_PCI_MMAP 21362306a36Sopenharmony_ci case PCIIOC_MMAP_IS_IO: 21462306a36Sopenharmony_ci if (!arch_can_pci_mmap_io()) 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci fpriv->mmap_state = pci_mmap_io; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci case PCIIOC_MMAP_IS_MEM: 22062306a36Sopenharmony_ci fpriv->mmap_state = pci_mmap_mem; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci case PCIIOC_WRITE_COMBINE: 22462306a36Sopenharmony_ci if (arch_can_pci_mmap_wc()) { 22562306a36Sopenharmony_ci if (arg) 22662306a36Sopenharmony_ci fpriv->write_combine = 1; 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci fpriv->write_combine = 0; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci /* If arch decided it can't, fall through... */ 23262306a36Sopenharmony_ci fallthrough; 23362306a36Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 23462306a36Sopenharmony_ci default: 23562306a36Sopenharmony_ci ret = -EINVAL; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return ret; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#ifdef HAVE_PCI_MMAP 24362306a36Sopenharmony_cistatic int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct pci_dev *dev = pde_data(file_inode(file)); 24662306a36Sopenharmony_ci struct pci_filp_private *fpriv = file->private_data; 24762306a36Sopenharmony_ci resource_size_t start, end; 24862306a36Sopenharmony_ci int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO) || 25162306a36Sopenharmony_ci security_locked_down(LOCKDOWN_PCI_ACCESS)) 25262306a36Sopenharmony_ci return -EPERM; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (fpriv->mmap_state == pci_mmap_io) { 25562306a36Sopenharmony_ci if (!arch_can_pci_mmap_io()) 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci res_bit = IORESOURCE_IO; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Make sure the caller is mapping a real resource for this device */ 26162306a36Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 26262306a36Sopenharmony_ci if (dev->resource[i].flags & res_bit && 26362306a36Sopenharmony_ci pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (i >= PCI_STD_NUM_BARS) 26862306a36Sopenharmony_ci return -ENODEV; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (fpriv->mmap_state == pci_mmap_mem && 27162306a36Sopenharmony_ci fpriv->write_combine) { 27262306a36Sopenharmony_ci if (dev->resource[i].flags & IORESOURCE_PREFETCH) 27362306a36Sopenharmony_ci write_combine = 1; 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (dev->resource[i].flags & IORESOURCE_MEM && 27962306a36Sopenharmony_ci iomem_is_exclusive(dev->resource[i].start)) 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Adjust vm_pgoff to be the offset within the resource */ 28562306a36Sopenharmony_ci vma->vm_pgoff -= start >> PAGE_SHIFT; 28662306a36Sopenharmony_ci ret = pci_mmap_resource_range(dev, i, vma, 28762306a36Sopenharmony_ci fpriv->mmap_state, write_combine); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int proc_bus_pci_open(struct inode *inode, struct file *file) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!fpriv) 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci fpriv->mmap_state = pci_mmap_io; 30262306a36Sopenharmony_ci fpriv->write_combine = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci file->private_data = fpriv; 30562306a36Sopenharmony_ci file->f_mapping = iomem_get_mapping(); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int proc_bus_pci_release(struct inode *inode, struct file *file) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci kfree(file->private_data); 31362306a36Sopenharmony_ci file->private_data = NULL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic const struct proc_ops proc_bus_pci_ops = { 32062306a36Sopenharmony_ci .proc_lseek = proc_bus_pci_lseek, 32162306a36Sopenharmony_ci .proc_read = proc_bus_pci_read, 32262306a36Sopenharmony_ci .proc_write = proc_bus_pci_write, 32362306a36Sopenharmony_ci .proc_ioctl = proc_bus_pci_ioctl, 32462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 32562306a36Sopenharmony_ci .proc_compat_ioctl = proc_bus_pci_ioctl, 32662306a36Sopenharmony_ci#endif 32762306a36Sopenharmony_ci#ifdef HAVE_PCI_MMAP 32862306a36Sopenharmony_ci .proc_open = proc_bus_pci_open, 32962306a36Sopenharmony_ci .proc_release = proc_bus_pci_release, 33062306a36Sopenharmony_ci .proc_mmap = proc_bus_pci_mmap, 33162306a36Sopenharmony_ci#ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA 33262306a36Sopenharmony_ci .proc_get_unmapped_area = get_pci_unmapped_area, 33362306a36Sopenharmony_ci#endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */ 33462306a36Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* iterator */ 33862306a36Sopenharmony_cistatic void *pci_seq_start(struct seq_file *m, loff_t *pos) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct pci_dev *dev = NULL; 34162306a36Sopenharmony_ci loff_t n = *pos; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for_each_pci_dev(dev) { 34462306a36Sopenharmony_ci if (!n--) 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return dev; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct pci_dev *dev = v; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci (*pos)++; 35562306a36Sopenharmony_ci dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); 35662306a36Sopenharmony_ci return dev; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void pci_seq_stop(struct seq_file *m, void *v) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci if (v) { 36262306a36Sopenharmony_ci struct pci_dev *dev = v; 36362306a36Sopenharmony_ci pci_dev_put(dev); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int show_device(struct seq_file *m, void *v) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci const struct pci_dev *dev = v; 37062306a36Sopenharmony_ci const struct pci_driver *drv; 37162306a36Sopenharmony_ci int i; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (dev == NULL) 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci drv = pci_dev_driver(dev); 37762306a36Sopenharmony_ci seq_printf(m, "%02x%02x\t%04x%04x\t%x", 37862306a36Sopenharmony_ci dev->bus->number, 37962306a36Sopenharmony_ci dev->devfn, 38062306a36Sopenharmony_ci dev->vendor, 38162306a36Sopenharmony_ci dev->device, 38262306a36Sopenharmony_ci dev->irq); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* only print standard and ROM resources to preserve compatibility */ 38562306a36Sopenharmony_ci for (i = 0; i <= PCI_ROM_RESOURCE; i++) { 38662306a36Sopenharmony_ci resource_size_t start, end; 38762306a36Sopenharmony_ci pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 38862306a36Sopenharmony_ci seq_printf(m, "\t%16llx", 38962306a36Sopenharmony_ci (unsigned long long)(start | 39062306a36Sopenharmony_ci (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci for (i = 0; i <= PCI_ROM_RESOURCE; i++) { 39362306a36Sopenharmony_ci resource_size_t start, end; 39462306a36Sopenharmony_ci pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 39562306a36Sopenharmony_ci seq_printf(m, "\t%16llx", 39662306a36Sopenharmony_ci dev->resource[i].start < dev->resource[i].end ? 39762306a36Sopenharmony_ci (unsigned long long)(end - start) + 1 : 0); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci seq_putc(m, '\t'); 40062306a36Sopenharmony_ci if (drv) 40162306a36Sopenharmony_ci seq_puts(m, drv->name); 40262306a36Sopenharmony_ci seq_putc(m, '\n'); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic const struct seq_operations proc_bus_pci_devices_op = { 40762306a36Sopenharmony_ci .start = pci_seq_start, 40862306a36Sopenharmony_ci .next = pci_seq_next, 40962306a36Sopenharmony_ci .stop = pci_seq_stop, 41062306a36Sopenharmony_ci .show = show_device 41162306a36Sopenharmony_ci}; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic struct proc_dir_entry *proc_bus_pci_dir; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciint pci_proc_attach_device(struct pci_dev *dev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct pci_bus *bus = dev->bus; 41862306a36Sopenharmony_ci struct proc_dir_entry *e; 41962306a36Sopenharmony_ci char name[16]; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (!proc_initialized) 42262306a36Sopenharmony_ci return -EACCES; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!bus->procdir) { 42562306a36Sopenharmony_ci if (pci_proc_domain(bus)) { 42662306a36Sopenharmony_ci sprintf(name, "%04x:%02x", pci_domain_nr(bus), 42762306a36Sopenharmony_ci bus->number); 42862306a36Sopenharmony_ci } else { 42962306a36Sopenharmony_ci sprintf(name, "%02x", bus->number); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci bus->procdir = proc_mkdir(name, proc_bus_pci_dir); 43262306a36Sopenharmony_ci if (!bus->procdir) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); 43762306a36Sopenharmony_ci e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir, 43862306a36Sopenharmony_ci &proc_bus_pci_ops, dev); 43962306a36Sopenharmony_ci if (!e) 44062306a36Sopenharmony_ci return -ENOMEM; 44162306a36Sopenharmony_ci proc_set_size(e, dev->cfg_size); 44262306a36Sopenharmony_ci dev->procent = e; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ciint pci_proc_detach_device(struct pci_dev *dev) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci proc_remove(dev->procent); 45062306a36Sopenharmony_ci dev->procent = NULL; 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint pci_proc_detach_bus(struct pci_bus *bus) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci proc_remove(bus->procdir); 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int __init pci_proc_init(void) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct pci_dev *dev = NULL; 46362306a36Sopenharmony_ci proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); 46462306a36Sopenharmony_ci proc_create_seq("devices", 0, proc_bus_pci_dir, 46562306a36Sopenharmony_ci &proc_bus_pci_devices_op); 46662306a36Sopenharmony_ci proc_initialized = 1; 46762306a36Sopenharmony_ci for_each_pci_dev(dev) 46862306a36Sopenharmony_ci pci_proc_attach_device(dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_cidevice_initcall(pci_proc_init); 473