xref: /linux/drivers/pci/proc.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
3df62ab5eSBjorn Helgaas  * Procfs interface for the PCI bus
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <linux/init.h>
91da177e4SLinus Torvalds #include <linux/pci.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/proc_fs.h>
131da177e4SLinus Torvalds #include <linux/seq_file.h>
14aa0ac365SAlexey Dobriyan #include <linux/capability.h>
157c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
16eb627e17SMatthew Garrett #include <linux/security.h>
171da177e4SLinus Torvalds #include <asm/byteorder.h>
18bc56b9e0SGreg KH #include "pci.h"
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds static int proc_initialized;	/* = 0 */
211da177e4SLinus Torvalds 
proc_bus_pci_lseek(struct file * file,loff_t off,int whence)223c78bc61SRyan Desfosses static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
231da177e4SLinus Torvalds {
24359745d7SMuchun Song 	struct pci_dev *dev = pde_data(file_inode(file));
2554de90d6SAl Viro 	return fixed_size_llseek(file, off, whence, dev->cfg_size);
261da177e4SLinus Torvalds }
271da177e4SLinus Torvalds 
proc_bus_pci_read(struct file * file,char __user * buf,size_t nbytes,loff_t * ppos)283c78bc61SRyan Desfosses static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
293c78bc61SRyan Desfosses 				 size_t nbytes, loff_t *ppos)
301da177e4SLinus Torvalds {
31359745d7SMuchun Song 	struct pci_dev *dev = pde_data(file_inode(file));
321da177e4SLinus Torvalds 	unsigned int pos = *ppos;
331da177e4SLinus Torvalds 	unsigned int cnt, size;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	/*
361da177e4SLinus Torvalds 	 * Normal users can read only the standardized portion of the
371da177e4SLinus Torvalds 	 * configuration space as several chips lock up when trying to read
381da177e4SLinus Torvalds 	 * undefined locations (think of Intel PIIX4 as a typical example).
391da177e4SLinus Torvalds 	 */
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 	if (capable(CAP_SYS_ADMIN))
42d9dda78bSAl Viro 		size = dev->cfg_size;
431da177e4SLinus Torvalds 	else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
441da177e4SLinus Torvalds 		size = 128;
451da177e4SLinus Torvalds 	else
461da177e4SLinus Torvalds 		size = 64;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	if (pos >= size)
491da177e4SLinus Torvalds 		return 0;
501da177e4SLinus Torvalds 	if (nbytes >= size)
511da177e4SLinus Torvalds 		nbytes = size;
521da177e4SLinus Torvalds 	if (pos + nbytes > size)
531da177e4SLinus Torvalds 		nbytes = size - pos;
541da177e4SLinus Torvalds 	cnt = nbytes;
551da177e4SLinus Torvalds 
5696d4f267SLinus Torvalds 	if (!access_ok(buf, cnt))
571da177e4SLinus Torvalds 		return -EINVAL;
581da177e4SLinus Torvalds 
59b3c32c4fSHuang Ying 	pci_config_pm_runtime_get(dev);
60b3c32c4fSHuang Ying 
611da177e4SLinus Torvalds 	if ((pos & 1) && cnt) {
621da177e4SLinus Torvalds 		unsigned char val;
63e04b0ea2SBrian King 		pci_user_read_config_byte(dev, pos, &val);
641da177e4SLinus Torvalds 		__put_user(val, buf);
651da177e4SLinus Torvalds 		buf++;
661da177e4SLinus Torvalds 		pos++;
671da177e4SLinus Torvalds 		cnt--;
681da177e4SLinus Torvalds 	}
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	if ((pos & 3) && cnt > 2) {
711da177e4SLinus Torvalds 		unsigned short val;
72e04b0ea2SBrian King 		pci_user_read_config_word(dev, pos, &val);
73f17a077eSHarvey Harrison 		__put_user(cpu_to_le16(val), (__le16 __user *) buf);
741da177e4SLinus Torvalds 		buf += 2;
751da177e4SLinus Torvalds 		pos += 2;
761da177e4SLinus Torvalds 		cnt -= 2;
771da177e4SLinus Torvalds 	}
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 	while (cnt >= 4) {
801da177e4SLinus Torvalds 		unsigned int val;
81e04b0ea2SBrian King 		pci_user_read_config_dword(dev, pos, &val);
82f17a077eSHarvey Harrison 		__put_user(cpu_to_le32(val), (__le32 __user *) buf);
831da177e4SLinus Torvalds 		buf += 4;
841da177e4SLinus Torvalds 		pos += 4;
851da177e4SLinus Torvalds 		cnt -= 4;
86a153e5e1SKrzysztof Wilczyński 		cond_resched();
871da177e4SLinus Torvalds 	}
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	if (cnt >= 2) {
901da177e4SLinus Torvalds 		unsigned short val;
91e04b0ea2SBrian King 		pci_user_read_config_word(dev, pos, &val);
92f17a077eSHarvey Harrison 		__put_user(cpu_to_le16(val), (__le16 __user *) buf);
931da177e4SLinus Torvalds 		buf += 2;
941da177e4SLinus Torvalds 		pos += 2;
951da177e4SLinus Torvalds 		cnt -= 2;
961da177e4SLinus Torvalds 	}
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	if (cnt) {
991da177e4SLinus Torvalds 		unsigned char val;
100e04b0ea2SBrian King 		pci_user_read_config_byte(dev, pos, &val);
1011da177e4SLinus Torvalds 		__put_user(val, buf);
1021da177e4SLinus Torvalds 		pos++;
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds 
105b3c32c4fSHuang Ying 	pci_config_pm_runtime_put(dev);
106b3c32c4fSHuang Ying 
1071da177e4SLinus Torvalds 	*ppos = pos;
1081da177e4SLinus Torvalds 	return nbytes;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
proc_bus_pci_write(struct file * file,const char __user * buf,size_t nbytes,loff_t * ppos)1113c78bc61SRyan Desfosses static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
1123c78bc61SRyan Desfosses 				  size_t nbytes, loff_t *ppos)
1131da177e4SLinus Torvalds {
114496ad9aaSAl Viro 	struct inode *ino = file_inode(file);
115359745d7SMuchun Song 	struct pci_dev *dev = pde_data(ino);
1161da177e4SLinus Torvalds 	int pos = *ppos;
117d9dda78bSAl Viro 	int size = dev->cfg_size;
118eb627e17SMatthew Garrett 	int cnt, ret;
119eb627e17SMatthew Garrett 
120eb627e17SMatthew Garrett 	ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
121eb627e17SMatthew Garrett 	if (ret)
122eb627e17SMatthew Garrett 		return ret;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	if (pos >= size)
1251da177e4SLinus Torvalds 		return 0;
1261da177e4SLinus Torvalds 	if (nbytes >= size)
1271da177e4SLinus Torvalds 		nbytes = size;
1281da177e4SLinus Torvalds 	if (pos + nbytes > size)
1291da177e4SLinus Torvalds 		nbytes = size - pos;
1301da177e4SLinus Torvalds 	cnt = nbytes;
1311da177e4SLinus Torvalds 
13296d4f267SLinus Torvalds 	if (!access_ok(buf, cnt))
1331da177e4SLinus Torvalds 		return -EINVAL;
1341da177e4SLinus Torvalds 
135b3c32c4fSHuang Ying 	pci_config_pm_runtime_get(dev);
136b3c32c4fSHuang Ying 
1371da177e4SLinus Torvalds 	if ((pos & 1) && cnt) {
1381da177e4SLinus Torvalds 		unsigned char val;
1391da177e4SLinus Torvalds 		__get_user(val, buf);
140e04b0ea2SBrian King 		pci_user_write_config_byte(dev, pos, val);
1411da177e4SLinus Torvalds 		buf++;
1421da177e4SLinus Torvalds 		pos++;
1431da177e4SLinus Torvalds 		cnt--;
1441da177e4SLinus Torvalds 	}
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	if ((pos & 3) && cnt > 2) {
147f17a077eSHarvey Harrison 		__le16 val;
148f17a077eSHarvey Harrison 		__get_user(val, (__le16 __user *) buf);
149e04b0ea2SBrian King 		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
1501da177e4SLinus Torvalds 		buf += 2;
1511da177e4SLinus Torvalds 		pos += 2;
1521da177e4SLinus Torvalds 		cnt -= 2;
1531da177e4SLinus Torvalds 	}
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	while (cnt >= 4) {
156f17a077eSHarvey Harrison 		__le32 val;
157f17a077eSHarvey Harrison 		__get_user(val, (__le32 __user *) buf);
158e04b0ea2SBrian King 		pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
1591da177e4SLinus Torvalds 		buf += 4;
1601da177e4SLinus Torvalds 		pos += 4;
1611da177e4SLinus Torvalds 		cnt -= 4;
1621da177e4SLinus Torvalds 	}
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	if (cnt >= 2) {
165f17a077eSHarvey Harrison 		__le16 val;
166f17a077eSHarvey Harrison 		__get_user(val, (__le16 __user *) buf);
167e04b0ea2SBrian King 		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
1681da177e4SLinus Torvalds 		buf += 2;
1691da177e4SLinus Torvalds 		pos += 2;
1701da177e4SLinus Torvalds 		cnt -= 2;
1711da177e4SLinus Torvalds 	}
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	if (cnt) {
1741da177e4SLinus Torvalds 		unsigned char val;
1751da177e4SLinus Torvalds 		__get_user(val, buf);
176e04b0ea2SBrian King 		pci_user_write_config_byte(dev, pos, val);
1771da177e4SLinus Torvalds 		pos++;
1781da177e4SLinus Torvalds 	}
1791da177e4SLinus Torvalds 
180b3c32c4fSHuang Ying 	pci_config_pm_runtime_put(dev);
181b3c32c4fSHuang Ying 
1821da177e4SLinus Torvalds 	*ppos = pos;
183d9dda78bSAl Viro 	i_size_write(ino, dev->cfg_size);
1841da177e4SLinus Torvalds 	return nbytes;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
187cb2d0f84SKrzysztof Wilczyński #ifdef HAVE_PCI_MMAP
1881da177e4SLinus Torvalds struct pci_filp_private {
1891da177e4SLinus Torvalds 	enum pci_mmap_state mmap_state;
1901da177e4SLinus Torvalds 	int write_combine;
1911da177e4SLinus Torvalds };
192cb2d0f84SKrzysztof Wilczyński #endif /* HAVE_PCI_MMAP */
1931da177e4SLinus Torvalds 
proc_bus_pci_ioctl(struct file * file,unsigned int cmd,unsigned long arg)194add77184SMathieu Segaud static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
195add77184SMathieu Segaud 			       unsigned long arg)
1961da177e4SLinus Torvalds {
197359745d7SMuchun Song 	struct pci_dev *dev = pde_data(file_inode(file));
1981da177e4SLinus Torvalds #ifdef HAVE_PCI_MMAP
1991da177e4SLinus Torvalds 	struct pci_filp_private *fpriv = file->private_data;
2001da177e4SLinus Torvalds #endif /* HAVE_PCI_MMAP */
2011da177e4SLinus Torvalds 	int ret = 0;
2021da177e4SLinus Torvalds 
203eb627e17SMatthew Garrett 	ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
204eb627e17SMatthew Garrett 	if (ret)
205eb627e17SMatthew Garrett 		return ret;
206eb627e17SMatthew Garrett 
2071da177e4SLinus Torvalds 	switch (cmd) {
2081da177e4SLinus Torvalds 	case PCIIOC_CONTROLLER:
2091da177e4SLinus Torvalds 		ret = pci_domain_nr(dev->bus);
2101da177e4SLinus Torvalds 		break;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds #ifdef HAVE_PCI_MMAP
2131da177e4SLinus Torvalds 	case PCIIOC_MMAP_IS_IO:
214e854d8b2SDavid Woodhouse 		if (!arch_can_pci_mmap_io())
215e854d8b2SDavid Woodhouse 			return -EINVAL;
2161da177e4SLinus Torvalds 		fpriv->mmap_state = pci_mmap_io;
2171da177e4SLinus Torvalds 		break;
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	case PCIIOC_MMAP_IS_MEM:
2201da177e4SLinus Torvalds 		fpriv->mmap_state = pci_mmap_mem;
2211da177e4SLinus Torvalds 		break;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	case PCIIOC_WRITE_COMBINE:
224ae749c7aSDavid Woodhouse 		if (arch_can_pci_mmap_wc()) {
2251da177e4SLinus Torvalds 			if (arg)
2261da177e4SLinus Torvalds 				fpriv->write_combine = 1;
2271da177e4SLinus Torvalds 			else
2281da177e4SLinus Torvalds 				fpriv->write_combine = 0;
2291da177e4SLinus Torvalds 			break;
230ae749c7aSDavid Woodhouse 		}
231ae749c7aSDavid Woodhouse 		/* If arch decided it can't, fall through... */
232df561f66SGustavo A. R. Silva 		fallthrough;
23354325d08SGustavo A. R. Silva #endif /* HAVE_PCI_MMAP */
2341da177e4SLinus Torvalds 	default:
2351da177e4SLinus Torvalds 		ret = -EINVAL;
2361da177e4SLinus Torvalds 		break;
237f7625980SBjorn Helgaas 	}
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	return ret;
2401da177e4SLinus Torvalds }
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds #ifdef HAVE_PCI_MMAP
proc_bus_pci_mmap(struct file * file,struct vm_area_struct * vma)2431da177e4SLinus Torvalds static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
2441da177e4SLinus Torvalds {
245359745d7SMuchun Song 	struct pci_dev *dev = pde_data(file_inode(file));
2461da177e4SLinus Torvalds 	struct pci_filp_private *fpriv = file->private_data;
247*0ad722f1SArnd Bergmann 	resource_size_t start, end;
248e854d8b2SDavid Woodhouse 	int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM;
2491da177e4SLinus Torvalds 
250eb627e17SMatthew Garrett 	if (!capable(CAP_SYS_RAWIO) ||
251eb627e17SMatthew Garrett 	    security_locked_down(LOCKDOWN_PCI_ACCESS))
2521da177e4SLinus Torvalds 		return -EPERM;
2531da177e4SLinus Torvalds 
254e854d8b2SDavid Woodhouse 	if (fpriv->mmap_state == pci_mmap_io) {
255e854d8b2SDavid Woodhouse 		if (!arch_can_pci_mmap_io())
256e854d8b2SDavid Woodhouse 			return -EINVAL;
25717caf567SDavid Woodhouse 		res_bit = IORESOURCE_IO;
258e854d8b2SDavid Woodhouse 	}
25917caf567SDavid Woodhouse 
2609eff02e2SJesse Barnes 	/* Make sure the caller is mapping a real resource for this device */
261c9c13ba4SDenis Efremov 	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
26217caf567SDavid Woodhouse 		if (dev->resource[i].flags & res_bit &&
26317caf567SDavid Woodhouse 		    pci_mmap_fits(dev, i, vma,  PCI_MMAP_PROCFS))
2649eff02e2SJesse Barnes 			break;
2659eff02e2SJesse Barnes 	}
2669eff02e2SJesse Barnes 
267c9c13ba4SDenis Efremov 	if (i >= PCI_STD_NUM_BARS)
2689eff02e2SJesse Barnes 		return -ENODEV;
2699eff02e2SJesse Barnes 
270cef4d023SDavid Woodhouse 	if (fpriv->mmap_state == pci_mmap_mem &&
271cef4d023SDavid Woodhouse 	    fpriv->write_combine) {
272cef4d023SDavid Woodhouse 		if (dev->resource[i].flags & IORESOURCE_PREFETCH)
273cef4d023SDavid Woodhouse 			write_combine = 1;
2743a92c319SBjorn Helgaas 		else
275cef4d023SDavid Woodhouse 			return -EINVAL;
276cef4d023SDavid Woodhouse 	}
277dc217d2cSDaniel Vetter 
278dc217d2cSDaniel Vetter 	if (dev->resource[i].flags & IORESOURCE_MEM &&
279dc217d2cSDaniel Vetter 	    iomem_is_exclusive(dev->resource[i].start))
280dc217d2cSDaniel Vetter 		return -EINVAL;
281dc217d2cSDaniel Vetter 
282*0ad722f1SArnd Bergmann 	pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
283*0ad722f1SArnd Bergmann 
284*0ad722f1SArnd Bergmann 	/* Adjust vm_pgoff to be the offset within the resource */
285*0ad722f1SArnd Bergmann 	vma->vm_pgoff -= start >> PAGE_SHIFT;
286*0ad722f1SArnd Bergmann 	ret = pci_mmap_resource_range(dev, i, vma,
2873a92c319SBjorn Helgaas 				  fpriv->mmap_state, write_combine);
2881da177e4SLinus Torvalds 	if (ret < 0)
2891da177e4SLinus Torvalds 		return ret;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	return 0;
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds 
proc_bus_pci_open(struct inode * inode,struct file * file)2941da177e4SLinus Torvalds static int proc_bus_pci_open(struct inode *inode, struct file *file)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL);
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	if (!fpriv)
2991da177e4SLinus Torvalds 		return -ENOMEM;
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 	fpriv->mmap_state = pci_mmap_io;
3021da177e4SLinus Torvalds 	fpriv->write_combine = 0;
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	file->private_data = fpriv;
305636b21b5SDaniel Vetter 	file->f_mapping = iomem_get_mapping();
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	return 0;
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
proc_bus_pci_release(struct inode * inode,struct file * file)3101da177e4SLinus Torvalds static int proc_bus_pci_release(struct inode *inode, struct file *file)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	kfree(file->private_data);
3131da177e4SLinus Torvalds 	file->private_data = NULL;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	return 0;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds #endif /* HAVE_PCI_MMAP */
3181da177e4SLinus Torvalds 
31997a32539SAlexey Dobriyan static const struct proc_ops proc_bus_pci_ops = {
32097a32539SAlexey Dobriyan 	.proc_lseek	= proc_bus_pci_lseek,
32197a32539SAlexey Dobriyan 	.proc_read	= proc_bus_pci_read,
32297a32539SAlexey Dobriyan 	.proc_write	= proc_bus_pci_write,
32397a32539SAlexey Dobriyan 	.proc_ioctl	= proc_bus_pci_ioctl,
32497a32539SAlexey Dobriyan #ifdef CONFIG_COMPAT
32597a32539SAlexey Dobriyan 	.proc_compat_ioctl = proc_bus_pci_ioctl,
32697a32539SAlexey Dobriyan #endif
3271da177e4SLinus Torvalds #ifdef HAVE_PCI_MMAP
32897a32539SAlexey Dobriyan 	.proc_open	= proc_bus_pci_open,
32997a32539SAlexey Dobriyan 	.proc_release	= proc_bus_pci_release,
33097a32539SAlexey Dobriyan 	.proc_mmap	= proc_bus_pci_mmap,
3311da177e4SLinus Torvalds #ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA
33297a32539SAlexey Dobriyan 	.proc_get_unmapped_area = get_pci_unmapped_area,
3331da177e4SLinus Torvalds #endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */
3341da177e4SLinus Torvalds #endif /* HAVE_PCI_MMAP */
3351da177e4SLinus Torvalds };
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds /* iterator */
pci_seq_start(struct seq_file * m,loff_t * pos)3381da177e4SLinus Torvalds static void *pci_seq_start(struct seq_file *m, loff_t *pos)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	struct pci_dev *dev = NULL;
3411da177e4SLinus Torvalds 	loff_t n = *pos;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	for_each_pci_dev(dev) {
3441da177e4SLinus Torvalds 		if (!n--)
3451da177e4SLinus Torvalds 			break;
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 	return dev;
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds 
pci_seq_next(struct seq_file * m,void * v,loff_t * pos)3501da177e4SLinus Torvalds static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
3511da177e4SLinus Torvalds {
3521da177e4SLinus Torvalds 	struct pci_dev *dev = v;
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 	(*pos)++;
3551da177e4SLinus Torvalds 	dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
3561da177e4SLinus Torvalds 	return dev;
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds 
pci_seq_stop(struct seq_file * m,void * v)3591da177e4SLinus Torvalds static void pci_seq_stop(struct seq_file *m, void *v)
3601da177e4SLinus Torvalds {
3611da177e4SLinus Torvalds 	if (v) {
3621da177e4SLinus Torvalds 		struct pci_dev *dev = v;
3631da177e4SLinus Torvalds 		pci_dev_put(dev);
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
show_device(struct seq_file * m,void * v)3671da177e4SLinus Torvalds static int show_device(struct seq_file *m, void *v)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds 	const struct pci_dev *dev = v;
3701da177e4SLinus Torvalds 	const struct pci_driver *drv;
3711da177e4SLinus Torvalds 	int i;
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	if (dev == NULL)
3741da177e4SLinus Torvalds 		return 0;
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	drv = pci_dev_driver(dev);
3771da177e4SLinus Torvalds 	seq_printf(m, "%02x%02x\t%04x%04x\t%x",
3781da177e4SLinus Torvalds 			dev->bus->number,
3791da177e4SLinus Torvalds 			dev->devfn,
3801da177e4SLinus Torvalds 			dev->vendor,
3811da177e4SLinus Torvalds 			dev->device,
3821da177e4SLinus Torvalds 			dev->irq);
383fde09c6dSYu Zhao 
384fde09c6dSYu Zhao 	/* only print standard and ROM resources to preserve compatibility */
385fde09c6dSYu Zhao 	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
386e31dd6e4SGreg Kroah-Hartman 		resource_size_t start, end;
3872311b1f2SMichael Ellerman 		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
3881396a8c3SGreg Kroah-Hartman 		seq_printf(m, "\t%16llx",
3891396a8c3SGreg Kroah-Hartman 			(unsigned long long)(start |
3901396a8c3SGreg Kroah-Hartman 			(dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
3912311b1f2SMichael Ellerman 	}
392fde09c6dSYu Zhao 	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
393e31dd6e4SGreg Kroah-Hartman 		resource_size_t start, end;
3942311b1f2SMichael Ellerman 		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
3951396a8c3SGreg Kroah-Hartman 		seq_printf(m, "\t%16llx",
3961da177e4SLinus Torvalds 			dev->resource[i].start < dev->resource[i].end ?
3971396a8c3SGreg Kroah-Hartman 			(unsigned long long)(end - start) + 1 : 0);
3982311b1f2SMichael Ellerman 	}
3991da177e4SLinus Torvalds 	seq_putc(m, '\t');
4001da177e4SLinus Torvalds 	if (drv)
401590a18e1SMarkus Elfring 		seq_puts(m, drv->name);
4021da177e4SLinus Torvalds 	seq_putc(m, '\n');
4031da177e4SLinus Torvalds 	return 0;
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
40602d90fc3SJan Engelhardt static const struct seq_operations proc_bus_pci_devices_op = {
4071da177e4SLinus Torvalds 	.start	= pci_seq_start,
4081da177e4SLinus Torvalds 	.next	= pci_seq_next,
4091da177e4SLinus Torvalds 	.stop	= pci_seq_stop,
4101da177e4SLinus Torvalds 	.show	= show_device
4111da177e4SLinus Torvalds };
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds static struct proc_dir_entry *proc_bus_pci_dir;
4141da177e4SLinus Torvalds 
pci_proc_attach_device(struct pci_dev * dev)4151da177e4SLinus Torvalds int pci_proc_attach_device(struct pci_dev *dev)
4161da177e4SLinus Torvalds {
4171da177e4SLinus Torvalds 	struct pci_bus *bus = dev->bus;
4181da177e4SLinus Torvalds 	struct proc_dir_entry *e;
4191da177e4SLinus Torvalds 	char name[16];
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	if (!proc_initialized)
4221da177e4SLinus Torvalds 		return -EACCES;
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 	if (!bus->procdir) {
4251da177e4SLinus Torvalds 		if (pci_proc_domain(bus)) {
4261da177e4SLinus Torvalds 			sprintf(name, "%04x:%02x", pci_domain_nr(bus),
4271da177e4SLinus Torvalds 					bus->number);
4281da177e4SLinus Torvalds 		} else {
4291da177e4SLinus Torvalds 			sprintf(name, "%02x", bus->number);
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 		bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
4321da177e4SLinus Torvalds 		if (!bus->procdir)
4331da177e4SLinus Torvalds 			return -ENOMEM;
4341da177e4SLinus Torvalds 	}
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
437c7705f34SDenis V. Lunev 	e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir,
43897a32539SAlexey Dobriyan 			     &proc_bus_pci_ops, dev);
4391da177e4SLinus Torvalds 	if (!e)
4401da177e4SLinus Torvalds 		return -ENOMEM;
441271a15eaSDavid Howells 	proc_set_size(e, dev->cfg_size);
4421da177e4SLinus Torvalds 	dev->procent = e;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	return 0;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
pci_proc_detach_device(struct pci_dev * dev)4471da177e4SLinus Torvalds int pci_proc_detach_device(struct pci_dev *dev)
4481da177e4SLinus Torvalds {
449a8ca16eaSDavid Howells 	proc_remove(dev->procent);
4501da177e4SLinus Torvalds 	dev->procent = NULL;
4511da177e4SLinus Torvalds 	return 0;
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds 
pci_proc_detach_bus(struct pci_bus * bus)4541da177e4SLinus Torvalds int pci_proc_detach_bus(struct pci_bus *bus)
4551da177e4SLinus Torvalds {
456a8ca16eaSDavid Howells 	proc_remove(bus->procdir);
4571da177e4SLinus Torvalds 	return 0;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds 
pci_proc_init(void)4601da177e4SLinus Torvalds static int __init pci_proc_init(void)
4611da177e4SLinus Torvalds {
4621da177e4SLinus Torvalds 	struct pci_dev *dev = NULL;
4639c37066dSAlexey Dobriyan 	proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
464fddda2b7SChristoph Hellwig 	proc_create_seq("devices", 0, proc_bus_pci_dir,
465fddda2b7SChristoph Hellwig 		    &proc_bus_pci_devices_op);
4661da177e4SLinus Torvalds 	proc_initialized = 1;
4674e344b1cSKulikov Vasiliy 	for_each_pci_dev(dev)
4681da177e4SLinus Torvalds 		pci_proc_attach_device(dev);
4694e344b1cSKulikov Vasiliy 
4701da177e4SLinus Torvalds 	return 0;
4711da177e4SLinus Torvalds }
472eaf61142SRobert P. J. Day device_initcall(pci_proc_init);
473