summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:03 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-06-23 17:32:03 -0500
commit8b3a73b4fe107705f2a11f413a0516119632ea40 (patch)
tree6212258b9dd2a10188083552b3ee5aad2ddcc246
parent5523144a4b16a6a2967f129c6535c5dfbf050314 (diff)
parent92742802ecbf215a2b60dcfd326d2213595010f1 (diff)
Merge branch 'pci/sysfs'
- Require CAP_SYS_ADMIN to write to sysfs 'resourceN_resize' attributes (Krzysztof Wilczyński) - Convert PCI resource files to static attributes to avoid races that cause 'duplicate filename' warnings and boot panics (Krzysztof Wilczyński) - Remove pci_create_sysfs_dev_files() and pci_remove_sysfs_dev_files(), which are obsolete after converting to static attributes (Krzysztof Wilczyński) - Add security_locked_down(LOCKDOWN_PCI_ACCESS) to alpha PCI resource mmap path to match the generic path (Krzysztof Wilczyński) - Convert sysfs 'legacy_io' and 'legacy_mem' to static attributes (Krzysztof Wilczyński) - Remove pci_create_legacy_files() and pci_sysfs_init(), which are obsolete after converting to static attributes (Krzysztof Wilczyński) - Expose sysfs 'resourceN_resize' attributes only on platforms with PCI mmap (Krzysztof Wilczyński) - Use kstrtobool() to parse the 'rom' attribute input to avoid the unexpected behavior of enabling the ROM when writing '0' with no trailing newline (Krzysztof Wilczyński) * pci/sysfs: PCI/sysfs: Use kstrtobool() to parse the ROM attribute input PCI/sysfs: Limit BAR resize attribute scope to platforms with PCI mmap PCI/sysfs: Remove pci_create_legacy_files() and pci_sysfs_init() PCI/sysfs: Convert legacy I/O and memory attributes to static definitions PCI/sysfs: Add __weak pci_legacy_has_sparse() helper alpha/PCI: Compute legacy size in pci_mmap_legacy_page_range() PCI: Add macros for legacy I/O and memory address space sizes PCI/sysfs: Remove pci_{create,remove}_sysfs_dev_files() alpha/PCI: Convert resource files to static attributes alpha/PCI: Add static PCI resource attribute macros alpha/PCI: Remove WARN from __pci_mmap_fits() and __legacy_mmap_fits() alpha/PCI: Fix __pci_mmap_fits() overflow for zero-length BARs alpha/PCI: Use PCI resource accessor macros alpha/PCI: Use BAR index in sysfs attr->private instead of resource pointer alpha/PCI: Add security_locked_down() check to pci_mmap_resource() PCI/sysfs: Limit pci_sysfs_init() late_initcall compile scope PCI/sysfs: Add stubs for pci_{create,remove}_sysfs_dev_files() PCI/sysfs: Warn about BAR resize failure in __resource_resize_store() PCI/sysfs: Convert PCI resource files to static attributes PCI/sysfs: Add static PCI resource attribute macros PCI/sysfs: Add CAP_SYS_ADMIN check to __resource_resize_store() PCI/sysfs: Split pci_llseek_resource() for device and legacy attributes PCI/sysfs: Only allow supported resource types in I/O and MMIO helpers PCI: Add pci_resource_is_io() and pci_resource_is_mem() helpers PCI/sysfs: Use PCI resource accessor macros
-rw-r--r--arch/alpha/include/asm/pci.h13
-rw-r--r--arch/alpha/kernel/pci-sysfs.c384
-rw-r--r--arch/powerpc/include/asm/pci.h2
-rw-r--r--drivers/pci/bus.c1
-rw-r--r--drivers/pci/pci-sysfs.c585
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/probe.c6
-rw-r--r--drivers/pci/remove.c3
-rw-r--r--include/linux/pci.h39
9 files changed, 585 insertions, 464 deletions
diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h
index 6c04fcbdc8ed..ad5d1391e1fa 100644
--- a/arch/alpha/include/asm/pci.h
+++ b/arch/alpha/include/asm/pci.h
@@ -84,8 +84,17 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_state);
-extern void pci_adjust_legacy_attr(struct pci_bus *bus,
- enum pci_mmap_state mmap_type);
+extern bool pci_legacy_has_sparse(struct pci_bus *bus,
+ enum pci_mmap_state type);
#define HAVE_PCI_LEGACY 1
+extern const struct attribute_group pci_dev_resource_attr_group;
+extern const struct attribute_group pci_dev_resource_sparse_attr_group;
+extern const struct attribute_group pci_dev_resource_dense_attr_group;
+
+#define ARCH_PCI_DEV_GROUPS \
+ &pci_dev_resource_attr_group, \
+ &pci_dev_resource_sparse_attr_group, \
+ &pci_dev_resource_dense_attr_group,
+
#endif /* __ALPHA_PCI_H */
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 3048758304b5..94dbc470cd6c 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -11,8 +11,7 @@
*/
#include <linux/sched.h>
-#include <linux/stat.h>
-#include <linux/slab.h>
+#include <linux/security.h>
#include <linux/pci.h>
static int hose_mmap_page_range(struct pci_controller *hose,
@@ -36,20 +35,18 @@ static int hose_mmap_page_range(struct pci_controller *hose,
static int __pci_mmap_fits(struct pci_dev *pdev, int num,
struct vm_area_struct *vma, int sparse)
{
+ resource_size_t len = pci_resource_len(pdev, num);
unsigned long nr, start, size;
int shift = sparse ? 5 : 0;
+ if (!len)
+ return 0;
+
nr = vma_pages(vma);
start = vma->vm_pgoff;
- size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
+ size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1;
- if (start < size && size - start >= nr)
- return 1;
- WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d "
- "(size 0x%08lx)\n",
- current->comm, sparse ? " sparse" : "", start, start + nr,
- pci_name(pdev), num, size);
- return 0;
+ return start < size && size - start >= nr;
}
/**
@@ -68,26 +65,25 @@ static int pci_mmap_resource(struct kobject *kobj,
struct vm_area_struct *vma, int sparse)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
- struct resource *res = attr->private;
+ int barno = (unsigned long)attr->private;
enum pci_mmap_state mmap_type;
struct pci_bus_region bar;
- int i;
+ int ret;
- for (i = 0; i < PCI_STD_NUM_BARS; i++)
- if (res == &pdev->resource[i])
- break;
- if (i >= PCI_STD_NUM_BARS)
- return -ENODEV;
+ ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+ if (ret)
+ return ret;
- if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
+ if (pci_resource_is_mem(pdev, barno) &&
+ iomem_is_exclusive(pci_resource_start(pdev, barno)))
return -EINVAL;
- if (!__pci_mmap_fits(pdev, i, vma, sparse))
+ if (!__pci_mmap_fits(pdev, barno, vma, sparse))
return -EINVAL;
- pcibios_resource_to_bus(pdev->bus, &bar, res);
+ pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, barno));
vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
- mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
+ mmap_type = pci_resource_is_mem(pdev, barno) ? pci_mmap_mem : pci_mmap_io;
return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
}
@@ -106,33 +102,25 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
return pci_mmap_resource(kobj, attr, vma, 0);
}
-/**
- * pci_remove_resource_files - cleanup resource files
- * @pdev: pci_dev to cleanup
- *
- * If we created resource files for @dev, remove them from sysfs and
- * free their resources.
- */
-void pci_remove_resource_files(struct pci_dev *pdev)
-{
- int i;
+#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \
+static const struct bin_attribute \
+pci_dev_resource##_bar##_suffix##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .mmap = (_mmap), \
+}
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- struct bin_attribute *res_attr;
+#define pci_dev_resource_attr(_bar) \
+ __pci_dev_resource_attr(_bar, resource##_bar,, \
+ pci_mmap_resource_dense)
- res_attr = pdev->res_attr[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
- }
+#define pci_dev_resource_sparse_attr(_bar) \
+ __pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \
+ pci_mmap_resource_sparse)
- res_attr = pdev->res_attr_wc[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
- }
- }
-}
+#define pci_dev_resource_dense_attr(_bar) \
+ __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \
+ pci_mmap_resource_dense)
static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
{
@@ -141,7 +129,7 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
long dense_offset;
unsigned long sparse_size;
- pcibios_resource_to_bus(pdev->bus, &bar, &pdev->resource[num]);
+ pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, num));
/* All core logic chips have 4G sparse address space, except
CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
@@ -153,109 +141,10 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
return bar.end < sparse_size;
}
-static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
- char *suffix, struct bin_attribute *res_attr,
- unsigned long sparse)
-{
- size_t size = pci_resource_len(pdev, num);
-
- sprintf(name, "resource%d%s", num, suffix);
- res_attr->mmap = sparse ? pci_mmap_resource_sparse :
- pci_mmap_resource_dense;
- res_attr->attr.name = name;
- res_attr->attr.mode = S_IRUSR | S_IWUSR;
- res_attr->size = sparse ? size << 5 : size;
- res_attr->private = &pdev->resource[num];
- return sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
-}
-
-static int pci_create_attr(struct pci_dev *pdev, int num)
-{
- /* allocate attribute structure, piggyback attribute name */
- int retval, nlen1, nlen2 = 0, res_count = 1;
- unsigned long sparse_base, dense_base;
- struct bin_attribute *attr;
- struct pci_controller *hose = pdev->sysdata;
- char *suffix, *attr_name;
-
- suffix = ""; /* Assume bwx machine, normal resourceN files. */
- nlen1 = 10;
-
- if (pdev->resource[num].flags & IORESOURCE_MEM) {
- sparse_base = hose->sparse_mem_base;
- dense_base = hose->dense_mem_base;
- if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) {
- sparse_base = 0;
- suffix = "_dense";
- nlen1 = 16; /* resourceN_dense */
- }
- } else {
- sparse_base = hose->sparse_io_base;
- dense_base = hose->dense_io_base;
- }
-
- if (sparse_base) {
- suffix = "_sparse";
- nlen1 = 17;
- if (dense_base) {
- nlen2 = 16; /* resourceN_dense */
- res_count = 2;
- }
- }
-
- attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC);
- if (!attr)
- return -ENOMEM;
-
- /* Create bwx, sparse or single dense file */
- attr_name = (char *)(attr + res_count);
- pdev->res_attr[num] = attr;
- retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr,
- sparse_base);
- if (retval || res_count == 1)
- return retval;
-
- /* Create dense file */
- attr_name += nlen1;
- attr++;
- pdev->res_attr_wc[num] = attr;
- return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0);
-}
-
-/**
- * pci_create_resource_files - create resource files in sysfs for @pdev
- * @pdev: pci_dev in question
- *
- * Walk the resources in @dev creating files for each resource available.
- *
- * Return: %0 on success, or negative error code
- */
-int pci_create_resource_files(struct pci_dev *pdev)
-{
- int i;
- int retval;
-
- /* Expose the PCI resources from this device as files */
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
-
- /* skip empty resources */
- if (!pci_resource_len(pdev, i))
- continue;
-
- retval = pci_create_attr(pdev, i);
- if (retval) {
- pci_remove_resource_files(pdev);
- return retval;
- }
- }
- return 0;
-}
-
/* Legacy I/O bus mapping stuff. */
-static int __legacy_mmap_fits(struct pci_controller *hose,
- struct vm_area_struct *vma,
- unsigned long res_size, int sparse)
+static int __legacy_mmap_fits(struct vm_area_struct *vma,
+ unsigned long res_size)
{
unsigned long nr, start, size;
@@ -263,13 +152,7 @@ static int __legacy_mmap_fits(struct pci_controller *hose,
start = vma->vm_pgoff;
size = ((res_size - 1) >> PAGE_SHIFT) + 1;
- if (start < size && size - start >= nr)
- return 1;
- WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d "
- "(size 0x%08lx)\n",
- current->comm, sparse ? " sparse" : "", start, start + nr,
- hose->index, size);
- return 0;
+ return start < size && size - start >= nr;
}
static inline int has_sparse(struct pci_controller *hose,
@@ -290,36 +173,22 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
int sparse = has_sparse(hose, mmap_type);
unsigned long res_size;
- res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size :
- bus->legacy_io->size;
- if (!__legacy_mmap_fits(hose, vma, res_size, sparse))
+ res_size = (mmap_type == pci_mmap_mem) ? PCI_LEGACY_MEM_SIZE :
+ PCI_LEGACY_IO_SIZE;
+ if (sparse)
+ res_size <<= 5;
+
+ if (!__legacy_mmap_fits(vma, res_size))
return -EINVAL;
return hose_mmap_page_range(hose, vma, mmap_type, sparse);
}
-/**
- * pci_adjust_legacy_attr - adjustment of legacy file attributes
- * @bus: bus to create files under
- * @mmap_type: I/O port or memory
- *
- * Adjust file name and size for sparse mappings.
- */
-void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type)
+bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type)
{
struct pci_controller *hose = bus->sysdata;
- if (!has_sparse(hose, mmap_type))
- return;
-
- if (mmap_type == pci_mmap_mem) {
- bus->legacy_mem->attr.name = "legacy_mem_sparse";
- bus->legacy_mem->size <<= 5;
- } else {
- bus->legacy_io->attr.name = "legacy_io_sparse";
- bus->legacy_io->size <<= 5;
- }
- return;
+ return has_sparse(hose, type);
}
/* Legacy I/O bus read/write functions */
@@ -370,3 +239,166 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
}
return -EINVAL;
}
+
+pci_dev_resource_attr(0);
+pci_dev_resource_attr(1);
+pci_dev_resource_attr(2);
+pci_dev_resource_attr(3);
+pci_dev_resource_attr(4);
+pci_dev_resource_attr(5);
+
+pci_dev_resource_sparse_attr(0);
+pci_dev_resource_sparse_attr(1);
+pci_dev_resource_sparse_attr(2);
+pci_dev_resource_sparse_attr(3);
+pci_dev_resource_sparse_attr(4);
+pci_dev_resource_sparse_attr(5);
+
+pci_dev_resource_dense_attr(0);
+pci_dev_resource_dense_attr(1);
+pci_dev_resource_dense_attr(2);
+pci_dev_resource_dense_attr(3);
+pci_dev_resource_dense_attr(4);
+pci_dev_resource_dense_attr(5);
+
+static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev,
+ int bar)
+{
+ return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
+}
+
+static inline umode_t __pci_resource_attr_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+
+ if (!pci_resource_len(pdev, bar))
+ return 0;
+
+ return a->attr.mode;
+}
+
+static umode_t pci_dev_resource_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ struct pci_controller *hose = pdev->sysdata;
+
+ if (has_sparse(hose, pci_bar_mmap_type(pdev, bar)))
+ return 0;
+
+ return __pci_resource_attr_is_visible(kobj, a, bar);
+}
+
+static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ struct pci_controller *hose = pdev->sysdata;
+ enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
+
+ if (!has_sparse(hose, type))
+ return 0;
+
+ if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
+ return 0;
+
+ return __pci_resource_attr_is_visible(kobj, a, bar);
+}
+
+static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ struct pci_controller *hose = pdev->sysdata;
+ enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
+ unsigned long dense_base;
+
+ if (!has_sparse(hose, type))
+ return 0;
+
+ if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
+ return __pci_resource_attr_is_visible(kobj, a, bar);
+
+ dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base :
+ hose->dense_io_base;
+ if (!dense_base)
+ return 0;
+
+ return __pci_resource_attr_is_visible(kobj, a, bar);
+}
+
+static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj,
+ int bar, bool sparse)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ size_t size = pci_resource_len(pdev, bar);
+
+ return sparse ? size << 5 : size;
+}
+
+static size_t pci_dev_resource_bin_size(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ return __pci_dev_resource_bin_size(kobj, bar, false);
+}
+
+static size_t pci_dev_resource_sparse_bin_size(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar)
+{
+ return __pci_dev_resource_bin_size(kobj, bar, true);
+}
+
+static const struct bin_attribute *const pci_dev_resource_attrs[] = {
+ &pci_dev_resource0_attr,
+ &pci_dev_resource1_attr,
+ &pci_dev_resource2_attr,
+ &pci_dev_resource3_attr,
+ &pci_dev_resource4_attr,
+ &pci_dev_resource5_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_dev_resource_sparse_attrs[] = {
+ &pci_dev_resource0_sparse_attr,
+ &pci_dev_resource1_sparse_attr,
+ &pci_dev_resource2_sparse_attr,
+ &pci_dev_resource3_sparse_attr,
+ &pci_dev_resource4_sparse_attr,
+ &pci_dev_resource5_sparse_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_dev_resource_dense_attrs[] = {
+ &pci_dev_resource0_dense_attr,
+ &pci_dev_resource1_dense_attr,
+ &pci_dev_resource2_dense_attr,
+ &pci_dev_resource3_dense_attr,
+ &pci_dev_resource4_dense_attr,
+ &pci_dev_resource5_dense_attr,
+ NULL,
+};
+
+const struct attribute_group pci_dev_resource_attr_group = {
+ .bin_attrs = pci_dev_resource_attrs,
+ .is_bin_visible = pci_dev_resource_is_visible,
+ .bin_size = pci_dev_resource_bin_size,
+};
+
+const struct attribute_group pci_dev_resource_sparse_attr_group = {
+ .bin_attrs = pci_dev_resource_sparse_attrs,
+ .is_bin_visible = pci_dev_resource_sparse_is_visible,
+ .bin_size = pci_dev_resource_sparse_bin_size,
+};
+
+const struct attribute_group pci_dev_resource_dense_attr_group = {
+ .bin_attrs = pci_dev_resource_dense_attrs,
+ .is_bin_visible = pci_dev_resource_dense_is_visible,
+ .bin_size = pci_dev_resource_bin_size,
+};
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h
index 46a9c4491ed0..72f286e74786 100644
--- a/arch/powerpc/include/asm/pci.h
+++ b/arch/powerpc/include/asm/pci.h
@@ -82,8 +82,6 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_state);
-extern void pci_adjust_legacy_attr(struct pci_bus *bus,
- enum pci_mmap_state mmap_type);
#define HAVE_PCI_LEGACY 1
extern void pcibios_claim_one_bus(struct pci_bus *b);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 6c1ad1f542d9..655ed53436d3 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -354,7 +354,6 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_fixup_device(pci_fixup_final, dev);
if (pci_is_bridge(dev))
of_pci_make_dev_node(dev);
- pci_create_sysfs_dev_files(dev);
pci_proc_attach_device(dev);
pci_bridge_d3_update(dev);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index d37860841260..5ec0b245a69b 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -37,8 +37,6 @@
#define ARCH_PCI_DEV_GROUPS
#endif
-static int sysfs_initialized; /* = 0 */
-
/* show configuration fields */
#define pci_config_attr(field, format_string) \
static ssize_t \
@@ -177,7 +175,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
max = PCI_BRIDGE_RESOURCES;
for (i = 0; i < max; i++) {
- struct resource *res = &pci_dev->resource[i];
+ struct resource *res = pci_resource_n(pci_dev, i);
struct resource zerores = {};
/* For backwards compatibility */
@@ -674,11 +672,6 @@ static const struct attribute_group pcibus_group = {
.attrs = pcibus_attrs,
};
-const struct attribute_group *pcibus_groups[] = {
- &pcibus_group,
- NULL,
-};
-
static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -689,7 +682,7 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "%u\n", (pdev == vga_dev));
return sysfs_emit(buf, "%u\n",
- !!(pdev->resource[PCI_ROM_RESOURCE].flags &
+ !!(pci_resource_flags(pdev, PCI_ROM_RESOURCE) &
IORESOURCE_ROM_SHADOW));
}
static DEVICE_ATTR_RO(boot_vga);
@@ -877,19 +870,6 @@ static const struct attribute_group pci_dev_config_attr_group = {
.bin_size = pci_dev_config_attr_bin_size,
};
-/*
- * llseek operation for mmappable PCI resources.
- * May be left unused if the arch doesn't provide them.
- */
-static __maybe_unused loff_t
-pci_llseek_resource(struct file *filep,
- struct kobject *kobj __always_unused,
- const struct bin_attribute *attr,
- loff_t offset, int whence)
-{
- return fixed_size_llseek(filep, offset, whence, attr->size);
-}
-
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
@@ -981,91 +961,148 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
}
-/**
- * pci_adjust_legacy_attr - adjustment of legacy file attributes
- * @b: bus to create files under
- * @mmap_type: I/O port or memory
- *
- * Stub implementation. Can be overridden by arch if necessary.
- */
-void __weak pci_adjust_legacy_attr(struct pci_bus *b,
- enum pci_mmap_state mmap_type)
+bool __weak pci_legacy_has_sparse(struct pci_bus *bus,
+ enum pci_mmap_state type)
{
+ return false;
}
-/**
- * pci_create_legacy_files - create legacy I/O port and memory files
- * @b: bus to create files under
- *
- * Some platforms allow access to legacy I/O port and ISA memory space on
- * a per-bus basis. This routine creates the files and ties them into
- * their associated read, write and mmap files from pci-sysfs.c
- *
- * On error unwind, but don't propagate the error to the caller
- * as it is ok to set up the PCI bus without these files.
- */
-void pci_create_legacy_files(struct pci_bus *b)
+static inline umode_t __pci_legacy_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ enum pci_mmap_state type,
+ bool sparse)
{
- int error;
+ struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
- if (!sysfs_initialized)
- return;
+ if (pci_legacy_has_sparse(bus, type) != sparse)
+ return 0;
- b->legacy_io = kzalloc_objs(struct bin_attribute, 2, GFP_ATOMIC);
- if (!b->legacy_io)
- goto kzalloc_err;
+ return a->attr.mode;
+}
- sysfs_bin_attr_init(b->legacy_io);
- b->legacy_io->attr.name = "legacy_io";
- b->legacy_io->size = 0xffff;
- b->legacy_io->attr.mode = 0600;
- b->legacy_io->read = pci_read_legacy_io;
- b->legacy_io->write = pci_write_legacy_io;
- /* See pci_create_attr() for motivation */
- b->legacy_io->llseek = pci_llseek_resource;
- b->legacy_io->mmap = pci_mmap_legacy_io;
- b->legacy_io->f_mapping = iomem_get_mapping;
- pci_adjust_legacy_attr(b, pci_mmap_io);
- error = device_create_bin_file(&b->dev, b->legacy_io);
- if (error)
- goto legacy_io_err;
+static umode_t pci_legacy_io_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a, int n)
+{
+ return __pci_legacy_is_visible(kobj, a, pci_mmap_io, false);
+}
- /* Allocated above after the legacy_io struct */
- b->legacy_mem = b->legacy_io + 1;
- sysfs_bin_attr_init(b->legacy_mem);
- b->legacy_mem->attr.name = "legacy_mem";
- b->legacy_mem->size = 1024*1024;
- b->legacy_mem->attr.mode = 0600;
- b->legacy_mem->mmap = pci_mmap_legacy_mem;
- /* See pci_create_attr() for motivation */
- b->legacy_mem->llseek = pci_llseek_resource;
- b->legacy_mem->f_mapping = iomem_get_mapping;
- pci_adjust_legacy_attr(b, pci_mmap_mem);
- error = device_create_bin_file(&b->dev, b->legacy_mem);
- if (error)
- goto legacy_mem_err;
+static umode_t pci_legacy_io_sparse_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
+{
+ return __pci_legacy_is_visible(kobj, a, pci_mmap_io, true);
+}
- return;
+static umode_t pci_legacy_mem_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a, int n)
+{
+ return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, false);
+}
-legacy_mem_err:
- device_remove_bin_file(&b->dev, b->legacy_io);
-legacy_io_err:
- kfree(b->legacy_io);
- b->legacy_io = NULL;
-kzalloc_err:
- dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n");
+static umode_t pci_legacy_mem_sparse_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
+{
+ return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, true);
}
-void pci_remove_legacy_files(struct pci_bus *b)
+static loff_t pci_llseek_resource_legacy(struct file *filep,
+ struct kobject *kobj __always_unused,
+ const struct bin_attribute *attr,
+ loff_t offset, int whence)
{
- if (b->legacy_io) {
- device_remove_bin_file(&b->dev, b->legacy_io);
- device_remove_bin_file(&b->dev, b->legacy_mem);
- kfree(b->legacy_io); /* both are allocated here */
- }
+ return fixed_size_llseek(filep, offset, whence, attr->size);
}
+
+static const struct bin_attribute pci_legacy_io_attr = {
+ .attr = { .name = "legacy_io", .mode = 0600 },
+ .size = PCI_LEGACY_IO_SIZE,
+ .read = pci_read_legacy_io,
+ .write = pci_write_legacy_io,
+ .mmap = pci_mmap_legacy_io,
+ .llseek = pci_llseek_resource_legacy,
+ .f_mapping = iomem_get_mapping,
+};
+
+static const struct bin_attribute pci_legacy_io_sparse_attr = {
+ .attr = { .name = "legacy_io_sparse", .mode = 0600 },
+ .size = PCI_LEGACY_IO_SIZE << 5,
+ .read = pci_read_legacy_io,
+ .write = pci_write_legacy_io,
+ .mmap = pci_mmap_legacy_io,
+ .llseek = pci_llseek_resource_legacy,
+ .f_mapping = iomem_get_mapping,
+};
+
+static const struct bin_attribute pci_legacy_mem_attr = {
+ .attr = { .name = "legacy_mem", .mode = 0600 },
+ .size = PCI_LEGACY_MEM_SIZE,
+ .mmap = pci_mmap_legacy_mem,
+ .llseek = pci_llseek_resource_legacy,
+ .f_mapping = iomem_get_mapping,
+};
+
+static const struct bin_attribute pci_legacy_mem_sparse_attr = {
+ .attr = { .name = "legacy_mem_sparse", .mode = 0600 },
+ .size = PCI_LEGACY_MEM_SIZE << 5,
+ .mmap = pci_mmap_legacy_mem,
+ .llseek = pci_llseek_resource_legacy,
+ .f_mapping = iomem_get_mapping,
+};
+
+static const struct bin_attribute *const pci_legacy_io_attrs[] = {
+ &pci_legacy_io_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_legacy_io_sparse_attrs[] = {
+ &pci_legacy_io_sparse_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_legacy_mem_attrs[] = {
+ &pci_legacy_mem_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_legacy_mem_sparse_attrs[] = {
+ &pci_legacy_mem_sparse_attr,
+ NULL,
+};
+
+static const struct attribute_group pci_legacy_io_group = {
+ .bin_attrs = pci_legacy_io_attrs,
+ .is_bin_visible = pci_legacy_io_is_visible,
+};
+
+static const struct attribute_group pci_legacy_io_sparse_group = {
+ .bin_attrs = pci_legacy_io_sparse_attrs,
+ .is_bin_visible = pci_legacy_io_sparse_is_visible,
+};
+
+static const struct attribute_group pci_legacy_mem_group = {
+ .bin_attrs = pci_legacy_mem_attrs,
+ .is_bin_visible = pci_legacy_mem_is_visible,
+};
+
+static const struct attribute_group pci_legacy_mem_sparse_group = {
+ .bin_attrs = pci_legacy_mem_sparse_attrs,
+ .is_bin_visible = pci_legacy_mem_sparse_is_visible,
+};
+
#endif /* HAVE_PCI_LEGACY */
+const struct attribute_group *pcibus_groups[] = {
+ &pcibus_group,
+#ifdef HAVE_PCI_LEGACY
+ &pci_legacy_io_group,
+ &pci_legacy_io_sparse_group,
+ &pci_legacy_mem_group,
+ &pci_legacy_mem_sparse_group,
+#endif
+ NULL,
+};
+
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
/**
* pci_mmap_resource - map a PCI resource into user memory space
@@ -1082,20 +1119,24 @@ static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *a
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
int bar = (unsigned long)attr->private;
enum pci_mmap_state mmap_type;
- struct resource *res = &pdev->resource[bar];
int ret;
ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
if (ret)
return ret;
- if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
+ if (!pci_resource_is_mem(pdev, bar) &&
+ !(pci_resource_is_io(pdev, bar) && arch_can_pci_mmap_io()))
+ return -EIO;
+
+ if (pci_resource_is_mem(pdev, bar) &&
+ iomem_is_exclusive(pci_resource_start(pdev, bar)))
return -EINVAL;
if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS))
return -EINVAL;
- mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
+ mmap_type = pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
}
@@ -1123,6 +1164,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
int bar = (unsigned long)attr->private;
unsigned long port = off;
+ if (!pci_resource_is_io(pdev, bar))
+ return -EIO;
+
port += pci_resource_start(pdev, bar);
if (port > pci_resource_end(pdev, bar))
@@ -1157,14 +1201,14 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
#endif
}
-static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj,
+static ssize_t pci_read_resource(struct file *filp, struct kobject *kobj,
const 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,
+static ssize_t pci_write_resource(struct file *filp, struct kobject *kobj,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
@@ -1177,127 +1221,192 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
return pci_resource_io(filp, kobj, attr, buf, off, count, true);
}
-/**
- * pci_remove_resource_files - cleanup resource files
- * @pdev: dev to cleanup
- *
- * If we created resource files for @pdev, remove them from sysfs and
- * free their resources.
- */
-static void pci_remove_resource_files(struct pci_dev *pdev)
+static loff_t pci_llseek_resource(struct file *filep,
+ struct kobject *kobj,
+ const struct bin_attribute *attr,
+ loff_t offset, int whence)
{
- int i;
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ int bar = (unsigned long)attr->private;
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
- struct bin_attribute *res_attr;
+ return fixed_size_llseek(filep, offset, whence,
+ pci_resource_len(pdev, bar));
+}
- res_attr = pdev->res_attr[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
- }
+/*
+ * generic_file_llseek() consults f_mapping->host to determine
+ * the file size. As iomem_inode knows nothing about the
+ * attribute, it's not going to work, so override it as well.
+ */
+#if arch_can_pci_mmap_io()
+# define __PCI_RESOURCE_IO_MMAP_ATTRS \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource, \
+ .mmap = pci_mmap_resource_uc,
+#else
+# define __PCI_RESOURCE_IO_MMAP_ATTRS
+#endif
- res_attr = pdev->res_attr_wc[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
- }
- }
+#define pci_dev_resource_io_attr(_bar) \
+static const struct bin_attribute dev_resource##_bar##_io_attr = { \
+ .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .read = pci_read_resource, \
+ .write = pci_write_resource, \
+ __PCI_RESOURCE_IO_MMAP_ATTRS \
+}
+
+#define pci_dev_resource_uc_attr(_bar) \
+static const struct bin_attribute dev_resource##_bar##_uc_attr = { \
+ .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource, \
+ .mmap = pci_mmap_resource_uc, \
+}
+
+#define pci_dev_resource_wc_attr(_bar) \
+static const struct bin_attribute dev_resource##_bar##_wc_attr = { \
+ .attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource, \
+ .mmap = pci_mmap_resource_wc, \
}
-static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
+static inline umode_t
+__pci_resource_attr_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int bar, bool write_combine,
+ unsigned long flags)
{
- /* allocate attribute structure, piggyback attribute name */
- int name_len = write_combine ? 13 : 10;
- struct bin_attribute *res_attr;
- char *res_attr_name;
- int retval;
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
- res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
- if (!res_attr)
- return -ENOMEM;
+ if (pdev->non_mappable_bars)
+ return 0;
- res_attr_name = (char *)(res_attr + 1);
+ if (!pci_resource_len(pdev, bar))
+ return 0;
- sysfs_bin_attr_init(res_attr);
- if (write_combine) {
- sprintf(res_attr_name, "resource%d_wc", num);
- res_attr->mmap = pci_mmap_resource_wc;
- } else {
- sprintf(res_attr_name, "resource%d", num);
- if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
- res_attr->read = pci_read_resource_io;
- res_attr->write = pci_write_resource_io;
- if (arch_can_pci_mmap_io())
- res_attr->mmap = pci_mmap_resource_uc;
- } else {
- res_attr->mmap = pci_mmap_resource_uc;
- }
- }
- if (res_attr->mmap) {
- res_attr->f_mapping = iomem_get_mapping;
- /*
- * generic_file_llseek() consults f_mapping->host to determine
- * the file size. As iomem_inode knows nothing about the
- * attribute, it's not going to work, so override it as well.
- */
- res_attr->llseek = pci_llseek_resource;
- }
- res_attr->attr.name = res_attr_name;
- res_attr->attr.mode = 0600;
- res_attr->size = pci_resource_len(pdev, num);
- res_attr->private = (void *)(unsigned long)num;
- retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- if (retval) {
- kfree(res_attr);
- return retval;
- }
+ if ((pci_resource_flags(pdev, bar) & flags) != flags)
+ return 0;
- if (write_combine)
- pdev->res_attr_wc[num] = res_attr;
- else
- pdev->res_attr[num] = res_attr;
+ if (write_combine && !arch_can_pci_mmap_wc())
+ return 0;
- return 0;
+ return a->attr.mode;
}
-/**
- * pci_create_resource_files - create resource files in sysfs for @dev
- * @pdev: dev in question
- *
- * Walk the resources in @pdev creating files for each resource available.
- */
-static int pci_create_resource_files(struct pci_dev *pdev)
+static umode_t pci_dev_resource_io_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
{
- int i;
- int retval;
+ return __pci_resource_attr_is_visible(kobj, a, n, false,
+ IORESOURCE_IO);
+}
- /* Skip devices with non-mappable BARs */
- if (pdev->non_mappable_bars)
- return 0;
+static umode_t pci_dev_resource_uc_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
+{
+ return __pci_resource_attr_is_visible(kobj, a, n, false,
+ IORESOURCE_MEM);
+}
- /* Expose the PCI resources from this device as files */
- for (i = 0; i < PCI_STD_NUM_BARS; i++) {
+static umode_t pci_dev_resource_wc_is_visible(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
+{
+ return __pci_resource_attr_is_visible(kobj, a, n, true,
+ IORESOURCE_MEM | IORESOURCE_PREFETCH);
+}
- /* skip empty resources */
- if (!pci_resource_len(pdev, i))
- continue;
+static size_t pci_dev_resource_bin_size(struct kobject *kobj,
+ const struct bin_attribute *a,
+ int n)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
- retval = pci_create_attr(pdev, i, 0);
- /* for prefetchable resources, create a WC mappable file */
- if (!retval && arch_can_pci_mmap_wc() &&
- pdev->resource[i].flags & IORESOURCE_PREFETCH)
- retval = pci_create_attr(pdev, i, 1);
- if (retval) {
- pci_remove_resource_files(pdev);
- return retval;
- }
- }
- return 0;
+ return pci_resource_len(pdev, n);
}
-#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */
-int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
-void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
+
+pci_dev_resource_io_attr(0);
+pci_dev_resource_io_attr(1);
+pci_dev_resource_io_attr(2);
+pci_dev_resource_io_attr(3);
+pci_dev_resource_io_attr(4);
+pci_dev_resource_io_attr(5);
+
+pci_dev_resource_uc_attr(0);
+pci_dev_resource_uc_attr(1);
+pci_dev_resource_uc_attr(2);
+pci_dev_resource_uc_attr(3);
+pci_dev_resource_uc_attr(4);
+pci_dev_resource_uc_attr(5);
+
+pci_dev_resource_wc_attr(0);
+pci_dev_resource_wc_attr(1);
+pci_dev_resource_wc_attr(2);
+pci_dev_resource_wc_attr(3);
+pci_dev_resource_wc_attr(4);
+pci_dev_resource_wc_attr(5);
+
+static const struct bin_attribute *const pci_dev_resource_io_attrs[] = {
+ &dev_resource0_io_attr,
+ &dev_resource1_io_attr,
+ &dev_resource2_io_attr,
+ &dev_resource3_io_attr,
+ &dev_resource4_io_attr,
+ &dev_resource5_io_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = {
+ &dev_resource0_uc_attr,
+ &dev_resource1_uc_attr,
+ &dev_resource2_uc_attr,
+ &dev_resource3_uc_attr,
+ &dev_resource4_uc_attr,
+ &dev_resource5_uc_attr,
+ NULL,
+};
+
+static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = {
+ &dev_resource0_wc_attr,
+ &dev_resource1_wc_attr,
+ &dev_resource2_wc_attr,
+ &dev_resource3_wc_attr,
+ &dev_resource4_wc_attr,
+ &dev_resource5_wc_attr,
+ NULL,
+};
+
+static const struct attribute_group pci_dev_resource_io_attr_group = {
+ .bin_attrs = pci_dev_resource_io_attrs,
+ .is_bin_visible = pci_dev_resource_io_is_visible,
+ .bin_size = pci_dev_resource_bin_size,
+};
+
+static const struct attribute_group pci_dev_resource_uc_attr_group = {
+ .bin_attrs = pci_dev_resource_uc_attrs,
+ .is_bin_visible = pci_dev_resource_uc_is_visible,
+ .bin_size = pci_dev_resource_bin_size,
+};
+
+static const struct attribute_group pci_dev_resource_wc_attr_group = {
+ .bin_attrs = pci_dev_resource_wc_attrs,
+ .is_bin_visible = pci_dev_resource_wc_is_visible,
+ .bin_size = pci_dev_resource_bin_size,
+};
+
+static const struct attribute_group *pci_dev_resource_attr_groups[] = {
+ &pci_dev_resource_io_attr_group,
+ &pci_dev_resource_uc_attr_group,
+ &pci_dev_resource_wc_attr_group,
+ NULL,
+};
+#else
+#define pci_dev_resource_attr_groups NULL
#endif
/**
@@ -1309,18 +1418,19 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
* @off: file offset
* @count: number of byte in input
*
- * writing anything except 0 enables it
+ * Writing a boolean value enables or disables the ROM display.
*/
static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+ bool enable;
- if ((off == 0) && (*buf == '0') && (count == 2))
- pdev->rom_attr_enabled = 0;
- else
- pdev->rom_attr_enabled = 1;
+ if (kstrtobool(buf, &enable))
+ return -EINVAL;
+
+ pdev->rom_attr_enabled = enable;
return count;
}
@@ -1552,6 +1662,7 @@ static const struct attribute_group pci_dev_reset_method_attr_group = {
.is_visible = pci_dev_reset_attr_is_visible,
};
+#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
static ssize_t __resource_resize_show(struct device *dev, int n, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -1576,6 +1687,9 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
int ret;
u16 cmd;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
if (kstrtoul(buf, 0, &size) < 0)
return -EINVAL;
@@ -1598,14 +1712,17 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
pci_write_config_word(pdev, PCI_COMMAND,
cmd & ~PCI_COMMAND_MEMORY);
- pci_remove_resource_files(pdev);
+ sysfs_remove_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups);
ret = pci_resize_resource(pdev, n, size, 0);
+ if (ret)
+ pci_warn(pdev, "Failed to resize BAR %d: %pe\n",
+ n, ERR_PTR(ret));
pci_assign_unassigned_bus_resources(bus);
- if (pci_create_resource_files(pdev))
- pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n");
+ if (sysfs_create_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups))
+ pci_warn(pdev, "Failed to recreate resource groups after BAR resizing\n");
pci_write_config_word(pdev, PCI_COMMAND, cmd);
pm_put:
@@ -1648,7 +1765,7 @@ static struct attribute *resource_resize_attrs[] = {
NULL,
};
-static umode_t resource_resize_is_visible(struct kobject *kobj,
+static umode_t resource_resize_attr_is_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
@@ -1656,54 +1773,11 @@ static umode_t resource_resize_is_visible(struct kobject *kobj,
return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode;
}
-static const struct attribute_group pci_dev_resource_resize_group = {
+static const struct attribute_group pci_dev_resource_resize_attr_group = {
.attrs = resource_resize_attrs,
- .is_visible = resource_resize_is_visible,
+ .is_visible = resource_resize_attr_is_visible,
};
-
-int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
-{
- if (!sysfs_initialized)
- return -EACCES;
-
- return pci_create_resource_files(pdev);
-}
-
-/**
- * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
- * @pdev: device whose entries we should free
- *
- * Cleanup when @pdev is removed from sysfs.
- */
-void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
-{
- if (!sysfs_initialized)
- return;
-
- pci_remove_resource_files(pdev);
-}
-
-static int __init pci_sysfs_init(void)
-{
- struct pci_dev *pdev = NULL;
- struct pci_bus *pbus = NULL;
- int retval;
-
- sysfs_initialized = 1;
- for_each_pci_dev(pdev) {
- retval = pci_create_sysfs_dev_files(pdev);
- if (retval) {
- pci_dev_put(pdev);
- return retval;
- }
- }
-
- while ((pbus = pci_find_next_bus(pbus)))
- pci_create_legacy_files(pbus);
-
- return 0;
-}
-late_initcall(pci_sysfs_init);
+#endif
static struct attribute *pci_dev_dev_attrs[] = {
&dev_attr_boot_vga.attr,
@@ -1774,6 +1848,12 @@ static const struct attribute_group pci_dev_group = {
const struct attribute_group *pci_dev_groups[] = {
&pci_dev_group,
+#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
+ &pci_dev_resource_io_attr_group,
+ &pci_dev_resource_uc_attr_group,
+ &pci_dev_resource_wc_attr_group,
+ &pci_dev_resource_resize_attr_group,
+#endif
&pci_dev_config_attr_group,
&pci_dev_rom_attr_group,
&pci_dev_reset_attr_group,
@@ -1785,7 +1865,6 @@ const struct attribute_group *pci_dev_groups[] = {
#ifdef CONFIG_ACPI
&pci_dev_acpi_attr_group,
#endif
- &pci_dev_resource_resize_group,
ARCH_PCI_DEV_GROUPS
NULL,
};
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 472b6c2f7c4d..19660d068fb7 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -358,14 +358,6 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; }
int pci_hp_add_bridge(struct pci_dev *dev);
bool pci_hp_spurious_link_change(struct pci_dev *pdev);
-#if defined(CONFIG_SYSFS) && defined(HAVE_PCI_LEGACY)
-void pci_create_legacy_files(struct pci_bus *bus);
-void pci_remove_legacy_files(struct pci_bus *bus);
-#else
-static inline void pci_create_legacy_files(struct pci_bus *bus) { }
-static inline void pci_remove_legacy_files(struct pci_bus *bus) { }
-#endif
-
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
extern struct mutex pci_slot_mutex;
@@ -392,17 +384,17 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
}
+#ifdef HAVE_PCI_LEGACY
+bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type);
+#endif
+
#ifdef CONFIG_SYSFS
-int pci_create_sysfs_dev_files(struct pci_dev *pdev);
-void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern const struct attribute_group *pci_dev_groups[];
extern const struct attribute_group *pci_dev_attr_groups[];
extern const struct attribute_group *pcibus_groups[];
extern const struct attribute_group *pci_bus_groups[];
extern const struct attribute_group pci_doe_sysfs_group;
#else
-static inline int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; }
-static inline void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { }
#define pci_dev_groups NULL
#define pci_dev_attr_groups NULL
#define pcibus_groups NULL
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index b63cd0c310bc..748c7a198262 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1073,9 +1073,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
dev_err(&bus->dev, "failed to add bus: %d\n", err);
}
- /* Create legacy_io and legacy_mem files for this bus */
- pci_create_legacy_files(bus);
-
if (parent)
dev_info(parent, "PCI host bridge to bus %s\n", name);
else
@@ -1281,9 +1278,6 @@ add_dev:
dev_err(&child->dev, "failed to add bus: %d\n", ret);
}
- /* Create legacy_io and legacy_mem files for this bus */
- pci_create_legacy_files(child);
-
return child;
}
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index e9d519993853..d8bffa21498a 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -26,7 +26,6 @@ static void pci_stop_dev(struct pci_dev *dev)
device_release_driver(&dev->dev);
pci_proc_detach_device(dev);
- pci_remove_sysfs_dev_files(dev);
of_pci_remove_node(dev);
}
@@ -66,8 +65,6 @@ void pci_remove_bus(struct pci_bus *bus)
list_del(&bus->node);
pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
- pci_remove_legacy_files(bus);
-
if (bus->ops->remove_bus)
bus->ops->remove_bus(bus);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 7a4c9bf7137c..f8a049921fed 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -27,6 +27,7 @@
#include <linux/mod_devicetable.h>
#include <linux/types.h>
+#include <linux/sizes.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/list.h>
@@ -515,8 +516,6 @@ struct pci_dev {
spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */
u32 saved_config_space[16]; /* Config space saved at suspend time */
struct hlist_head saved_cap_space;
- struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
- struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_HOTPLUG_PCI_PCIE
unsigned int broken_cmd_compl:1; /* No compl for some cmds */
@@ -729,8 +728,6 @@ struct pci_bus {
pci_bus_flags_t bus_flags; /* Inherited by child buses */
struct device *bridge;
struct device dev;
- struct bin_attribute *legacy_io; /* Legacy I/O for this bus */
- struct bin_attribute *legacy_mem; /* Legacy mem */
unsigned int is_added:1;
unsigned int unsafe_warn:1; /* warned about RW1C config write */
unsigned int flit_mode:1; /* Link in Flit mode */
@@ -1173,6 +1170,10 @@ enum {
/* These external functions are only available when PCI support is enabled */
#ifdef CONFIG_PCI
+/* PCI legacy I/O port and memory address space sizes. */
+#define PCI_LEGACY_IO_SIZE (SZ_64K - 1)
+#define PCI_LEGACY_MEM_SIZE SZ_1M
+
extern unsigned int pci_flags;
static inline void pci_set_flags(int flags) { pci_flags = flags; }
@@ -2309,6 +2310,31 @@ int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma);
CONCATENATE(__pci_dev_for_each_res, COUNT_ARGS(__VA_ARGS__)) \
(dev, res, __VA_ARGS__)
+/**
+ * pci_resource_is_io - check if a PCI resource is of I/O port type.
+ * @dev: PCI device to check.
+ * @resno: The resource number (BAR index) to check.
+ *
+ * Returns true if the resource type is I/O port.
+ */
+static inline bool pci_resource_is_io(const struct pci_dev *dev, int resno)
+{
+ return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_IO;
+}
+
+/**
+ * pci_resource_is_mem - check if a PCI resource is of memory type.
+ * @dev: PCI device to check.
+ * @resno: The resource number (BAR index) to check.
+ *
+ * Returns true if the resource type is memory, including
+ * prefetchable memory.
+ */
+static inline bool pci_resource_is_mem(const struct pci_dev *dev, int resno)
+{
+ return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_MEM;
+}
+
/*
* Similar to the helpers above, these manipulate per-pci_dev
* driver-specific data. They are really just a wrapper around
@@ -2515,11 +2541,6 @@ int pcibios_alloc_irq(struct pci_dev *dev);
void pcibios_free_irq(struct pci_dev *dev);
resource_size_t pcibios_default_alignment(void);
-#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
-extern int pci_create_resource_files(struct pci_dev *dev);
-extern void pci_remove_resource_files(struct pci_dev *dev);
-#endif
-
#if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG)
void __init pci_mmcfg_early_init(void);
void __init pci_mmcfg_late_init(void);