aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPali Rohár <pali@kernel.org>2021-11-20 14:36:40 +0100
committerMartin Mares <mj@ucw.cz>2022-02-10 13:49:35 +0100
commit119c1376f9ca4d359a1816af9d31f4a2c2c83307 (patch)
tree331610e196b0138be1e0521d1c3008af8178c052
parent3b26eaa884987a828421415559aaf61772839fed (diff)
downloadpciutils-119c1376f9ca4d359a1816af9d31f4a2c2c83307.tar.gz
libpci: Add support for filling bridge resources
Extend libpci API and ABI to fill bridge resources from sysfs.
-rw-r--r--lib/access.c20
-rw-r--r--lib/caps.c2
-rw-r--r--lib/filter.c2
-rw-r--r--lib/internal.h6
-rw-r--r--lib/libpci.ver5
-rw-r--r--lib/pci.h4
-rw-r--r--lib/sysfs.c48
7 files changed, 73 insertions, 14 deletions
diff --git a/lib/access.c b/lib/access.c
index 9bd6989..b8fdbe2 100644
--- a/lib/access.c
+++ b/lib/access.c
@@ -189,7 +189,7 @@ pci_reset_properties(struct pci_dev *d)
}
int
-pci_fill_info_v35(struct pci_dev *d, int flags)
+pci_fill_info_v38(struct pci_dev *d, int flags)
{
unsigned int uflags = flags;
if (uflags & PCI_FILL_RESCAN)
@@ -203,19 +203,21 @@ pci_fill_info_v35(struct pci_dev *d, int flags)
}
/* In version 3.1, pci_fill_info got new flags => versioned alias */
-/* In versions 3.2, 3.3, 3.4 and 3.5, the same has happened */
-STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v35(d, flags));
-DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v35);
-DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v35);
+/* In versions 3.2, 3.3, 3.4, 3.5 and 3.8, the same has happened */
+STATIC_ALIAS(int pci_fill_info(struct pci_dev *d, int flags), pci_fill_info_v38(d, flags));
+DEFINE_ALIAS(int pci_fill_info_v30(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v31(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v32(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v33(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v34(struct pci_dev *d, int flags), pci_fill_info_v38);
+DEFINE_ALIAS(int pci_fill_info_v35(struct pci_dev *d, int flags), pci_fill_info_v38);
SYMBOL_VERSION(pci_fill_info_v30, pci_fill_info@LIBPCI_3.0);
SYMBOL_VERSION(pci_fill_info_v31, pci_fill_info@LIBPCI_3.1);
SYMBOL_VERSION(pci_fill_info_v32, pci_fill_info@LIBPCI_3.2);
SYMBOL_VERSION(pci_fill_info_v33, pci_fill_info@LIBPCI_3.3);
SYMBOL_VERSION(pci_fill_info_v34, pci_fill_info@LIBPCI_3.4);
-SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@@LIBPCI_3.5);
+SYMBOL_VERSION(pci_fill_info_v35, pci_fill_info@LIBPCI_3.5);
+SYMBOL_VERSION(pci_fill_info_v38, pci_fill_info@@LIBPCI_3.8);
void
pci_setup_cache(struct pci_dev *d, byte *cache, int len)
diff --git a/lib/caps.c b/lib/caps.c
index 3c025a9..039c86f 100644
--- a/lib/caps.c
+++ b/lib/caps.c
@@ -128,7 +128,7 @@ pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type,
unsigned int target = (cap_number ? *cap_number : 0);
unsigned int index = 0;
- pci_fill_info_v35(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
+ pci_fill_info_v38(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
for (c=d->first_cap; c; c=c->next)
{
diff --git a/lib/filter.c b/lib/filter.c
index 573fb28..195f813 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -129,7 +129,7 @@ pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d)
return 0;
if (f->device >= 0 || f->vendor >= 0)
{
- pci_fill_info_v35(d, PCI_FILL_IDENT);
+ pci_fill_info_v38(d, PCI_FILL_IDENT);
if ((f->device >= 0 && f->device != d->device_id) ||
(f->vendor >= 0 && f->vendor != d->vendor_id))
return 0;
diff --git a/lib/internal.h b/lib/internal.h
index 942e3cb..a1c8cc8 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -74,6 +74,7 @@ int pci_fill_info_v32(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v33(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v34(struct pci_dev *, int flags) VERSIONED_ABI;
int pci_fill_info_v35(struct pci_dev *, int flags) VERSIONED_ABI;
+int pci_fill_info_v38(struct pci_dev *, int flags) VERSIONED_ABI;
static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned int try_fields)
{
@@ -87,6 +88,11 @@ static inline int want_fill(struct pci_dev *d, unsigned want_fields, unsigned in
}
}
+static inline void clear_fill(struct pci_dev *d, unsigned clear_fields)
+{
+ d->known_fields &= ~clear_fields;
+}
+
struct pci_property {
struct pci_property *next;
u32 key;
diff --git a/lib/libpci.ver b/lib/libpci.ver
index e20c3f5..73f7fa7 100644
--- a/lib/libpci.ver
+++ b/lib/libpci.ver
@@ -82,3 +82,8 @@ LIBPCI_3.7 {
global:
pci_find_cap_nr;
};
+
+LIBPCI_3.8 {
+ global:
+ pci_fill_info;
+};
diff --git a/lib/pci.h b/lib/pci.h
index 328a468..b9fd9bf 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -142,6 +142,9 @@ struct pci_dev {
pciaddr_t flags[6]; /* PCI_IORESOURCE_* flags for regions */
pciaddr_t rom_flags; /* PCI_IORESOURCE_* flags for expansion ROM */
int domain; /* PCI domain (host bridge) */
+ pciaddr_t bridge_base_addr[4]; /* Bridge base addresses (without flags) */
+ pciaddr_t bridge_size[4]; /* Bridge sizes */
+ pciaddr_t bridge_flags[4]; /* PCI_IORESOURCE_* flags for bridge addresses */
/* Fields used internally */
struct pci_access *access;
@@ -205,6 +208,7 @@ char *pci_get_string_property(struct pci_dev *d, u32 prop) PCI_ABI;
#define PCI_FILL_IO_FLAGS 0x1000
#define PCI_FILL_DT_NODE 0x2000 /* Device tree node */
#define PCI_FILL_IOMMU_GROUP 0x4000
+#define PCI_FILL_BRIDGE_BASES 0x8000
#define PCI_FILL_RESCAN 0x00010000
void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI;
diff --git a/lib/sysfs.c b/lib/sysfs.c
index 4b8b2cd..fe3f0e5 100644
--- a/lib/sysfs.c
+++ b/lib/sysfs.c
@@ -153,14 +153,17 @@ sysfs_get_resources(struct pci_dev *d)
{
struct pci_access *a = d->access;
char namebuf[OBJNAMELEN], buf[256];
+ struct { pciaddr_t flags, base_addr, size; } lines[10];
+ int have_bar_bases, have_rom_base, have_bridge_bases;
FILE *file;
int i;
+ have_bar_bases = have_rom_base = have_bridge_bases = 0;
sysfs_obj_name(d, "resource", namebuf);
file = fopen(namebuf, "r");
if (!file)
a->error("Cannot open %s: %s", namebuf, strerror(errno));
- for (i = 0; i < 7; i++)
+ for (i = 0; i < 7+6+4+1; i++)
{
unsigned long long start, end, size, flags;
if (!fgets(buf, sizeof(buf), file))
@@ -177,16 +180,55 @@ sysfs_get_resources(struct pci_dev *d)
flags &= PCI_ADDR_FLAG_MASK;
d->base_addr[i] = start | flags;
d->size[i] = size;
+ have_bar_bases = 1;
}
- else
+ else if (i == 6)
{
d->rom_flags = flags;
flags &= PCI_ADDR_FLAG_MASK;
d->rom_base_addr = start | flags;
d->rom_size = size;
+ have_rom_base = 1;
}
+ else if (i < 7+6+4)
+ {
+ /*
+ * If kernel was compiled without CONFIG_PCI_IOV option then after
+ * the ROM line for configured bridge device (that which had set
+ * subordinary bus number to non-zero value) are four additional lines
+ * which describe resources behind bridge. For PCI-to-PCI bridges they
+ * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0,
+ * IO1, MEM0 and MEM1. For unconfigured bridges and other devices
+ * there is no additional line after the ROM line. If kernel was
+ * compiled with CONFIG_PCI_IOV option then after the ROM line and
+ * before the first bridge resource line are six additional lines
+ * which describe IOV resources. Read all remaining lines in resource
+ * file and based on the number of remaining lines (0, 4, 6, 10) parse
+ * resources behind bridge.
+ */
+ lines[i-7].flags = flags;
+ lines[i-7].base_addr = start;
+ lines[i-7].size = size;
+ }
+ }
+ if (i == 7+4 || i == 7+6+4)
+ {
+ int offset = (i == 7+6+4) ? 6 : 0;
+ for (i = 0; i < 4; i++)
+ {
+ d->bridge_flags[i] = lines[offset+i].flags;
+ d->bridge_base_addr[i] = lines[offset+i].base_addr;
+ d->bridge_size[i] = lines[offset+i].size;
+ }
+ have_bridge_bases = 1;
}
fclose(file);
+ if (!have_bar_bases)
+ clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS);
+ if (!have_rom_base)
+ clear_fill(d, PCI_FILL_ROM_BASE);
+ if (!have_bridge_bases)
+ clear_fill(d, PCI_FILL_BRIDGE_BASES);
}
static void sysfs_scan(struct pci_access *a)
@@ -307,7 +349,7 @@ sysfs_fill_info(struct pci_dev *d, unsigned int flags)
d->device_class = sysfs_get_value(d, "class", 1) >> 8;
if (want_fill(d, flags, PCI_FILL_IRQ))
d->irq = sysfs_get_value(d, "irq", 1);
- if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS))
+ if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
sysfs_get_resources(d);
}