mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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
This commit is contained in:
commit
8b3a73b4fe
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,34 +102,26 @@ 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;
|
||||
|
||||
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
|
||||
struct bin_attribute *res_attr;
|
||||
|
||||
res_attr = pdev->res_attr[i];
|
||||
if (res_attr) {
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
|
||||
kfree(res_attr);
|
||||
}
|
||||
|
||||
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_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), \
|
||||
}
|
||||
|
||||
#define pci_dev_resource_attr(_bar) \
|
||||
__pci_dev_resource_attr(_bar, resource##_bar,, \
|
||||
pci_mmap_resource_dense)
|
||||
|
||||
#define pci_dev_resource_sparse_attr(_bar) \
|
||||
__pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \
|
||||
pci_mmap_resource_sparse)
|
||||
|
||||
#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)
|
||||
{
|
||||
struct pci_bus_region bar;
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
return;
|
||||
|
||||
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");
|
||||
return a->attr.mode;
|
||||
}
|
||||
|
||||
void pci_remove_legacy_files(struct pci_bus *b)
|
||||
static umode_t pci_legacy_io_is_visible(struct kobject *kobj,
|
||||
const struct bin_attribute *a, int n)
|
||||
{
|
||||
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 __pci_legacy_is_visible(kobj, a, pci_mmap_io, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
res_attr = pdev->res_attr[i];
|
||||
if (res_attr) {
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
|
||||
kfree(res_attr);
|
||||
}
|
||||
|
||||
res_attr = pdev->res_attr_wc[i];
|
||||
if (res_attr) {
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
|
||||
kfree(res_attr);
|
||||
}
|
||||
}
|
||||
return fixed_size_llseek(filep, offset, whence,
|
||||
pci_resource_len(pdev, bar));
|
||||
}
|
||||
|
||||
static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
|
||||
{
|
||||
/* allocate attribute structure, piggyback attribute name */
|
||||
int name_len = write_combine ? 13 : 10;
|
||||
struct bin_attribute *res_attr;
|
||||
char *res_attr_name;
|
||||
int retval;
|
||||
/*
|
||||
* 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 = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
|
||||
if (!res_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
res_attr_name = (char *)(res_attr + 1);
|
||||
|
||||
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 (write_combine)
|
||||
pdev->res_attr_wc[num] = res_attr;
|
||||
else
|
||||
pdev->res_attr[num] = res_attr;
|
||||
|
||||
return 0;
|
||||
#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 \
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
int i;
|
||||
int retval;
|
||||
#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 inline umode_t
|
||||
__pci_resource_attr_is_visible(struct kobject *kobj,
|
||||
const struct bin_attribute *a,
|
||||
int bar, bool write_combine,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
||||
|
||||
/* Skip devices with non-mappable BARs */
|
||||
if (pdev->non_mappable_bars)
|
||||
return 0;
|
||||
|
||||
/* Expose the PCI resources from this device as files */
|
||||
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
|
||||
if (!pci_resource_len(pdev, bar))
|
||||
return 0;
|
||||
|
||||
/* skip empty resources */
|
||||
if (!pci_resource_len(pdev, i))
|
||||
continue;
|
||||
if ((pci_resource_flags(pdev, bar) & flags) != flags)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
if (write_combine && !arch_can_pci_mmap_wc())
|
||||
return 0;
|
||||
|
||||
return a->attr.mode;
|
||||
}
|
||||
#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; }
|
||||
|
||||
static umode_t pci_dev_resource_io_is_visible(struct kobject *kobj,
|
||||
const struct bin_attribute *a,
|
||||
int n)
|
||||
{
|
||||
return __pci_resource_attr_is_visible(kobj, a, n, false,
|
||||
IORESOURCE_IO);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
return pci_resource_len(pdev, n);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user