18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Procfs interface for the PCI bus 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 138c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 148c2ecf20Sopenharmony_ci#include <linux/capability.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/security.h> 178c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 188c2ecf20Sopenharmony_ci#include "pci.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int proc_initialized; /* = 0 */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct pci_dev *dev = PDE_DATA(file_inode(file)); 258c2ecf20Sopenharmony_ci return fixed_size_llseek(file, off, whence, dev->cfg_size); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic ssize_t proc_bus_pci_read(struct file *file, char __user *buf, 298c2ecf20Sopenharmony_ci size_t nbytes, loff_t *ppos) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct pci_dev *dev = PDE_DATA(file_inode(file)); 328c2ecf20Sopenharmony_ci unsigned int pos = *ppos; 338c2ecf20Sopenharmony_ci unsigned int cnt, size; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * Normal users can read only the standardized portion of the 378c2ecf20Sopenharmony_ci * configuration space as several chips lock up when trying to read 388c2ecf20Sopenharmony_ci * undefined locations (think of Intel PIIX4 as a typical example). 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (capable(CAP_SYS_ADMIN)) 428c2ecf20Sopenharmony_ci size = dev->cfg_size; 438c2ecf20Sopenharmony_ci else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) 448c2ecf20Sopenharmony_ci size = 128; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci size = 64; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (pos >= size) 498c2ecf20Sopenharmony_ci return 0; 508c2ecf20Sopenharmony_ci if (nbytes >= size) 518c2ecf20Sopenharmony_ci nbytes = size; 528c2ecf20Sopenharmony_ci if (pos + nbytes > size) 538c2ecf20Sopenharmony_ci nbytes = size - pos; 548c2ecf20Sopenharmony_ci cnt = nbytes; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (!access_ok(buf, cnt)) 578c2ecf20Sopenharmony_ci return -EINVAL; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci pci_config_pm_runtime_get(dev); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if ((pos & 1) && cnt) { 628c2ecf20Sopenharmony_ci unsigned char val; 638c2ecf20Sopenharmony_ci pci_user_read_config_byte(dev, pos, &val); 648c2ecf20Sopenharmony_ci __put_user(val, buf); 658c2ecf20Sopenharmony_ci buf++; 668c2ecf20Sopenharmony_ci pos++; 678c2ecf20Sopenharmony_ci cnt--; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if ((pos & 3) && cnt > 2) { 718c2ecf20Sopenharmony_ci unsigned short val; 728c2ecf20Sopenharmony_ci pci_user_read_config_word(dev, pos, &val); 738c2ecf20Sopenharmony_ci __put_user(cpu_to_le16(val), (__le16 __user *) buf); 748c2ecf20Sopenharmony_ci buf += 2; 758c2ecf20Sopenharmony_ci pos += 2; 768c2ecf20Sopenharmony_ci cnt -= 2; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci while (cnt >= 4) { 808c2ecf20Sopenharmony_ci unsigned int val; 818c2ecf20Sopenharmony_ci pci_user_read_config_dword(dev, pos, &val); 828c2ecf20Sopenharmony_ci __put_user(cpu_to_le32(val), (__le32 __user *) buf); 838c2ecf20Sopenharmony_ci buf += 4; 848c2ecf20Sopenharmony_ci pos += 4; 858c2ecf20Sopenharmony_ci cnt -= 4; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (cnt >= 2) { 898c2ecf20Sopenharmony_ci unsigned short val; 908c2ecf20Sopenharmony_ci pci_user_read_config_word(dev, pos, &val); 918c2ecf20Sopenharmony_ci __put_user(cpu_to_le16(val), (__le16 __user *) buf); 928c2ecf20Sopenharmony_ci buf += 2; 938c2ecf20Sopenharmony_ci pos += 2; 948c2ecf20Sopenharmony_ci cnt -= 2; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (cnt) { 988c2ecf20Sopenharmony_ci unsigned char val; 998c2ecf20Sopenharmony_ci pci_user_read_config_byte(dev, pos, &val); 1008c2ecf20Sopenharmony_ci __put_user(val, buf); 1018c2ecf20Sopenharmony_ci buf++; 1028c2ecf20Sopenharmony_ci pos++; 1038c2ecf20Sopenharmony_ci cnt--; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pci_config_pm_runtime_put(dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci *ppos = pos; 1098c2ecf20Sopenharmony_ci return nbytes; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, 1138c2ecf20Sopenharmony_ci size_t nbytes, loff_t *ppos) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct inode *ino = file_inode(file); 1168c2ecf20Sopenharmony_ci struct pci_dev *dev = PDE_DATA(ino); 1178c2ecf20Sopenharmony_ci int pos = *ppos; 1188c2ecf20Sopenharmony_ci int size = dev->cfg_size; 1198c2ecf20Sopenharmony_ci int cnt, ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 1228c2ecf20Sopenharmony_ci if (ret) 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (pos >= size) 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci if (nbytes >= size) 1288c2ecf20Sopenharmony_ci nbytes = size; 1298c2ecf20Sopenharmony_ci if (pos + nbytes > size) 1308c2ecf20Sopenharmony_ci nbytes = size - pos; 1318c2ecf20Sopenharmony_ci cnt = nbytes; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!access_ok(buf, cnt)) 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci pci_config_pm_runtime_get(dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if ((pos & 1) && cnt) { 1398c2ecf20Sopenharmony_ci unsigned char val; 1408c2ecf20Sopenharmony_ci __get_user(val, buf); 1418c2ecf20Sopenharmony_ci pci_user_write_config_byte(dev, pos, val); 1428c2ecf20Sopenharmony_ci buf++; 1438c2ecf20Sopenharmony_ci pos++; 1448c2ecf20Sopenharmony_ci cnt--; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if ((pos & 3) && cnt > 2) { 1488c2ecf20Sopenharmony_ci __le16 val; 1498c2ecf20Sopenharmony_ci __get_user(val, (__le16 __user *) buf); 1508c2ecf20Sopenharmony_ci pci_user_write_config_word(dev, pos, le16_to_cpu(val)); 1518c2ecf20Sopenharmony_ci buf += 2; 1528c2ecf20Sopenharmony_ci pos += 2; 1538c2ecf20Sopenharmony_ci cnt -= 2; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci while (cnt >= 4) { 1578c2ecf20Sopenharmony_ci __le32 val; 1588c2ecf20Sopenharmony_ci __get_user(val, (__le32 __user *) buf); 1598c2ecf20Sopenharmony_ci pci_user_write_config_dword(dev, pos, le32_to_cpu(val)); 1608c2ecf20Sopenharmony_ci buf += 4; 1618c2ecf20Sopenharmony_ci pos += 4; 1628c2ecf20Sopenharmony_ci cnt -= 4; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (cnt >= 2) { 1668c2ecf20Sopenharmony_ci __le16 val; 1678c2ecf20Sopenharmony_ci __get_user(val, (__le16 __user *) buf); 1688c2ecf20Sopenharmony_ci pci_user_write_config_word(dev, pos, le16_to_cpu(val)); 1698c2ecf20Sopenharmony_ci buf += 2; 1708c2ecf20Sopenharmony_ci pos += 2; 1718c2ecf20Sopenharmony_ci cnt -= 2; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (cnt) { 1758c2ecf20Sopenharmony_ci unsigned char val; 1768c2ecf20Sopenharmony_ci __get_user(val, buf); 1778c2ecf20Sopenharmony_ci pci_user_write_config_byte(dev, pos, val); 1788c2ecf20Sopenharmony_ci buf++; 1798c2ecf20Sopenharmony_ci pos++; 1808c2ecf20Sopenharmony_ci cnt--; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pci_config_pm_runtime_put(dev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci *ppos = pos; 1868c2ecf20Sopenharmony_ci i_size_write(ino, dev->cfg_size); 1878c2ecf20Sopenharmony_ci return nbytes; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistruct pci_filp_private { 1918c2ecf20Sopenharmony_ci enum pci_mmap_state mmap_state; 1928c2ecf20Sopenharmony_ci int write_combine; 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, 1968c2ecf20Sopenharmony_ci unsigned long arg) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct pci_dev *dev = PDE_DATA(file_inode(file)); 1998c2ecf20Sopenharmony_ci#ifdef HAVE_PCI_MMAP 2008c2ecf20Sopenharmony_ci struct pci_filp_private *fpriv = file->private_data; 2018c2ecf20Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 2028c2ecf20Sopenharmony_ci int ret = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 2058c2ecf20Sopenharmony_ci if (ret) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci switch (cmd) { 2098c2ecf20Sopenharmony_ci case PCIIOC_CONTROLLER: 2108c2ecf20Sopenharmony_ci ret = pci_domain_nr(dev->bus); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#ifdef HAVE_PCI_MMAP 2148c2ecf20Sopenharmony_ci case PCIIOC_MMAP_IS_IO: 2158c2ecf20Sopenharmony_ci if (!arch_can_pci_mmap_io()) 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci fpriv->mmap_state = pci_mmap_io; 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci case PCIIOC_MMAP_IS_MEM: 2218c2ecf20Sopenharmony_ci fpriv->mmap_state = pci_mmap_mem; 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci case PCIIOC_WRITE_COMBINE: 2258c2ecf20Sopenharmony_ci if (arch_can_pci_mmap_wc()) { 2268c2ecf20Sopenharmony_ci if (arg) 2278c2ecf20Sopenharmony_ci fpriv->write_combine = 1; 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci fpriv->write_combine = 0; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci /* If arch decided it can't, fall through... */ 2338c2ecf20Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 2348c2ecf20Sopenharmony_ci fallthrough; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci ret = -EINVAL; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#ifdef HAVE_PCI_MMAP 2448c2ecf20Sopenharmony_cistatic int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct pci_dev *dev = PDE_DATA(file_inode(file)); 2478c2ecf20Sopenharmony_ci struct pci_filp_private *fpriv = file->private_data; 2488c2ecf20Sopenharmony_ci int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO) || 2518c2ecf20Sopenharmony_ci security_locked_down(LOCKDOWN_PCI_ACCESS)) 2528c2ecf20Sopenharmony_ci return -EPERM; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (fpriv->mmap_state == pci_mmap_io) { 2558c2ecf20Sopenharmony_ci if (!arch_can_pci_mmap_io()) 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci res_bit = IORESOURCE_IO; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Make sure the caller is mapping a real resource for this device */ 2618c2ecf20Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 2628c2ecf20Sopenharmony_ci if (dev->resource[i].flags & res_bit && 2638c2ecf20Sopenharmony_ci pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (i >= PCI_STD_NUM_BARS) 2688c2ecf20Sopenharmony_ci return -ENODEV; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (fpriv->mmap_state == pci_mmap_mem && 2718c2ecf20Sopenharmony_ci fpriv->write_combine) { 2728c2ecf20Sopenharmony_ci if (dev->resource[i].flags & IORESOURCE_PREFETCH) 2738c2ecf20Sopenharmony_ci write_combine = 1; 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci ret = pci_mmap_page_range(dev, i, vma, 2788c2ecf20Sopenharmony_ci fpriv->mmap_state, write_combine); 2798c2ecf20Sopenharmony_ci if (ret < 0) 2808c2ecf20Sopenharmony_ci return ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int proc_bus_pci_open(struct inode *inode, struct file *file) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!fpriv) 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci fpriv->mmap_state = pci_mmap_io; 2938c2ecf20Sopenharmony_ci fpriv->write_combine = 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci file->private_data = fpriv; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int proc_bus_pci_release(struct inode *inode, struct file *file) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci kfree(file->private_data); 3038c2ecf20Sopenharmony_ci file->private_data = NULL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct proc_ops proc_bus_pci_ops = { 3108c2ecf20Sopenharmony_ci .proc_lseek = proc_bus_pci_lseek, 3118c2ecf20Sopenharmony_ci .proc_read = proc_bus_pci_read, 3128c2ecf20Sopenharmony_ci .proc_write = proc_bus_pci_write, 3138c2ecf20Sopenharmony_ci .proc_ioctl = proc_bus_pci_ioctl, 3148c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 3158c2ecf20Sopenharmony_ci .proc_compat_ioctl = proc_bus_pci_ioctl, 3168c2ecf20Sopenharmony_ci#endif 3178c2ecf20Sopenharmony_ci#ifdef HAVE_PCI_MMAP 3188c2ecf20Sopenharmony_ci .proc_open = proc_bus_pci_open, 3198c2ecf20Sopenharmony_ci .proc_release = proc_bus_pci_release, 3208c2ecf20Sopenharmony_ci .proc_mmap = proc_bus_pci_mmap, 3218c2ecf20Sopenharmony_ci#ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA 3228c2ecf20Sopenharmony_ci .proc_get_unmapped_area = get_pci_unmapped_area, 3238c2ecf20Sopenharmony_ci#endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */ 3248c2ecf20Sopenharmony_ci#endif /* HAVE_PCI_MMAP */ 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* iterator */ 3288c2ecf20Sopenharmony_cistatic void *pci_seq_start(struct seq_file *m, loff_t *pos) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct pci_dev *dev = NULL; 3318c2ecf20Sopenharmony_ci loff_t n = *pos; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for_each_pci_dev(dev) { 3348c2ecf20Sopenharmony_ci if (!n--) 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci return dev; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct pci_dev *dev = v; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci (*pos)++; 3458c2ecf20Sopenharmony_ci dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); 3468c2ecf20Sopenharmony_ci return dev; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void pci_seq_stop(struct seq_file *m, void *v) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci if (v) { 3528c2ecf20Sopenharmony_ci struct pci_dev *dev = v; 3538c2ecf20Sopenharmony_ci pci_dev_put(dev); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int show_device(struct seq_file *m, void *v) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci const struct pci_dev *dev = v; 3608c2ecf20Sopenharmony_ci const struct pci_driver *drv; 3618c2ecf20Sopenharmony_ci int i; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (dev == NULL) 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci drv = pci_dev_driver(dev); 3678c2ecf20Sopenharmony_ci seq_printf(m, "%02x%02x\t%04x%04x\t%x", 3688c2ecf20Sopenharmony_ci dev->bus->number, 3698c2ecf20Sopenharmony_ci dev->devfn, 3708c2ecf20Sopenharmony_ci dev->vendor, 3718c2ecf20Sopenharmony_ci dev->device, 3728c2ecf20Sopenharmony_ci dev->irq); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* only print standard and ROM resources to preserve compatibility */ 3758c2ecf20Sopenharmony_ci for (i = 0; i <= PCI_ROM_RESOURCE; i++) { 3768c2ecf20Sopenharmony_ci resource_size_t start, end; 3778c2ecf20Sopenharmony_ci pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 3788c2ecf20Sopenharmony_ci seq_printf(m, "\t%16llx", 3798c2ecf20Sopenharmony_ci (unsigned long long)(start | 3808c2ecf20Sopenharmony_ci (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci for (i = 0; i <= PCI_ROM_RESOURCE; i++) { 3838c2ecf20Sopenharmony_ci resource_size_t start, end; 3848c2ecf20Sopenharmony_ci pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 3858c2ecf20Sopenharmony_ci seq_printf(m, "\t%16llx", 3868c2ecf20Sopenharmony_ci dev->resource[i].start < dev->resource[i].end ? 3878c2ecf20Sopenharmony_ci (unsigned long long)(end - start) + 1 : 0); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci seq_putc(m, '\t'); 3908c2ecf20Sopenharmony_ci if (drv) 3918c2ecf20Sopenharmony_ci seq_puts(m, drv->name); 3928c2ecf20Sopenharmony_ci seq_putc(m, '\n'); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic const struct seq_operations proc_bus_pci_devices_op = { 3978c2ecf20Sopenharmony_ci .start = pci_seq_start, 3988c2ecf20Sopenharmony_ci .next = pci_seq_next, 3998c2ecf20Sopenharmony_ci .stop = pci_seq_stop, 4008c2ecf20Sopenharmony_ci .show = show_device 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_bus_pci_dir; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ciint pci_proc_attach_device(struct pci_dev *dev) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct pci_bus *bus = dev->bus; 4088c2ecf20Sopenharmony_ci struct proc_dir_entry *e; 4098c2ecf20Sopenharmony_ci char name[16]; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!proc_initialized) 4128c2ecf20Sopenharmony_ci return -EACCES; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (!bus->procdir) { 4158c2ecf20Sopenharmony_ci if (pci_proc_domain(bus)) { 4168c2ecf20Sopenharmony_ci sprintf(name, "%04x:%02x", pci_domain_nr(bus), 4178c2ecf20Sopenharmony_ci bus->number); 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci sprintf(name, "%02x", bus->number); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci bus->procdir = proc_mkdir(name, proc_bus_pci_dir); 4228c2ecf20Sopenharmony_ci if (!bus->procdir) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); 4278c2ecf20Sopenharmony_ci e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir, 4288c2ecf20Sopenharmony_ci &proc_bus_pci_ops, dev); 4298c2ecf20Sopenharmony_ci if (!e) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci proc_set_size(e, dev->cfg_size); 4328c2ecf20Sopenharmony_ci dev->procent = e; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint pci_proc_detach_device(struct pci_dev *dev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci proc_remove(dev->procent); 4408c2ecf20Sopenharmony_ci dev->procent = NULL; 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciint pci_proc_detach_bus(struct pci_bus *bus) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci proc_remove(bus->procdir); 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int __init pci_proc_init(void) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct pci_dev *dev = NULL; 4538c2ecf20Sopenharmony_ci proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); 4548c2ecf20Sopenharmony_ci proc_create_seq("devices", 0, proc_bus_pci_dir, 4558c2ecf20Sopenharmony_ci &proc_bus_pci_devices_op); 4568c2ecf20Sopenharmony_ci proc_initialized = 1; 4578c2ecf20Sopenharmony_ci for_each_pci_dev(dev) 4588c2ecf20Sopenharmony_ci pci_proc_attach_device(dev); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_cidevice_initcall(pci_proc_init); 463