summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@vyatta.com>2012-03-02 16:46:39 -0800
committerStephen Hemminger <shemminger@vyatta.com>2012-03-03 09:56:10 -0800
commit6f0d227c17e898f5c1a0e015bd3275f56ae23a19 (patch)
tree9f40b2f18710e1fee144cc9cff686e18fe6a47cb
parentd86431f9a431288b3b9d0bed26303a13cc1875b5 (diff)
downloadvyatta-biosdevname-6f0d227c17e898f5c1a0e015bd3275f56ae23a19.tar.gz
vyatta-biosdevname-6f0d227c17e898f5c1a0e015bd3275f56ae23a19.zip
Update to biosdevname 0.3.11
Manual merge from http://linux.dell.com/biosdevname/biosdevname-0.3.11/ (cherry picked from commit 79066f1bdcb77fa8cfca2d98464257e1e436b58d)
-rw-r--r--Makefile.am7
-rw-r--r--TODO2
-rw-r--r--biosdevname.131
-rw-r--r--biosdevname.rules.in14
-rw-r--r--biosdevname.spec.fedora.in26
-rw-r--r--biosdevname.spec.suse.in26
-rw-r--r--configure.ac21
-rw-r--r--src/Makefile.am6
-rw-r--r--src/bios_dev_name.c58
-rw-r--r--src/dmidecode/dmidecode.c17
-rw-r--r--src/eths.c4
-rw-r--r--src/naming_policy.c47
-rw-r--r--src/pci.c326
-rw-r--r--src/pci.h4
-rw-r--r--src/pirq.c31
-rw-r--r--src/pirq.h2
-rw-r--r--src/sysfs.c3
17 files changed, 540 insertions, 85 deletions
diff --git a/Makefile.am b/Makefile.am
index 6b65b5e..e90f675 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,12 +8,11 @@ dist_noinst_DATA = biosdevname.rules.in biosdevname.spec.fedora biosdevname.spec
CLEANFILES = version
install-data-local:
- mkdir -p $(DESTDIR)@sysconfdir@/udev/rules.d
- $(INSTALL_DATA) $(top_srcdir)/biosdevname.rules.in $(DESTDIR)@sysconfdir@/udev/rules.d/71-netdevice.rules
- mkdir -p $(DESTDIR)/lib/udev
+ mkdir -p $(DESTDIR)@RULEDIR@
+ $(INSTALL_DATA) $(top_srcdir)/biosdevname.rules.in $(DESTDIR)@RULEDEST@
uninstall-local:
- rm -f $(DESTDIR)@sysconfdir@/udev/rules.d/@RULEDEST@
+ rm -f $(DESTDIR)@RULEDEST@
include src/Makefile.am
diff --git a/TODO b/TODO
index eedc3e5..bdbbdef 100644
--- a/TODO
+++ b/TODO
@@ -1 +1 @@
-* Add USB device recognition
+* fix Ubuntu builds to not require -lz, when Fedora does.
diff --git a/biosdevname.1 b/biosdevname.1
index fd0f99b..8bfd8ca 100644
--- a/biosdevname.1
+++ b/biosdevname.1
@@ -35,6 +35,13 @@ Enable debugging
.TP
.B \-\-prefix \fI[string]
string use for embedded NICs in the physical policy (default=em)
+.TP
+.B \-\-nopirq
+Do not use $PIR table for mapping PCI device to slot. Some BIOS have
+incorrect values.
+.TP
+.B \-\-smbios \fI[x.y]
+Require minimum SMBIOS version x.y
.SH POLICIES
.br
The
@@ -50,7 +57,7 @@ policy uses the following scheme:
em<port>
for embedded NICs
.TP
-pci<slot>#<port>_<virtual instance>
+p<slot>#<port>_<virtual instance>
for cards in PCI slots
.br
.TP
@@ -69,7 +76,7 @@ different PCI tree, and thus cannot provide consistent enumeration.
Use of this policy should be limited to only scenarios where all PCI
devices are present at boot (cold-plug).
-.SH RETURN CODES
+.SH EXIT CODES
Returns 0 on success, with BIOS-suggested name printed to stdout.
.br
Returns 1 on provided device name lookup failure.
@@ -77,10 +84,30 @@ Returns 1 on provided device name lookup failure.
Returns 2 if system BIOS does not provide naming information.
biosdevname requires system BIOS to provide naming information, either
via SMBIOS or sysfs files.
+.br
+Returns 3 if not run as root but requires root privileges.
+.br
+Returns 4 if running in a virtual machine.
.SH SEE ALSO
.br
+http://linux.dell.com/wiki/index.php/Oss/libnetdevname
+.br
http://linux.dell.com/files/biosdevname/
+.br
+git://linux.dell.com/biosdevname.git
+
+.SH RELATED PROGRAMS
+.br
+The
+.B dmidecode
+package contains two tools useful for debugging BIOS features that
+biosdevname uses, specifically
+.B dmidecode
+to read the SMBIOS Type 9 and Type 41 tables, and
+.B biosdecode
+to read the PCI IRQ Routing Table. Please include the output of each
+of these programs in any bug reports.
.SH AUTHOR
biosdevname was written by Matt Domsch <Matt_Domsch@dell.com>
.PP
diff --git a/biosdevname.rules.in b/biosdevname.rules.in
index a3be69c..6379164 100644
--- a/biosdevname.rules.in
+++ b/biosdevname.rules.in
@@ -1,6 +1,20 @@
SUBSYSTEM!="net", GOTO="netdevicename_end"
KERNEL!="eth*", GOTO="netdevicename_end"
ACTION!="add", GOTO="netdevicename_end"
+NAME=="?*", GOTO="netdevicename_end"
+
+# kernel command line "biosdevname={0|1}" can turn off/on biosdevname
+IMPORT{cmdline}="biosdevname"
+ENV{biosdevname}=="?*", ENV{UDEV_BIOSDEVNAME}="$env{biosdevname}"
+# ENV{UDEV_BIOSDEVNAME} can be used for blacklist/whitelist
+# but will be overwritten by the kernel command line argument
+ENV{UDEV_BIOSDEVNAME}=="0", GOTO="netdevicename_end"
+ENV{UDEV_BIOSDEVNAME}=="1", GOTO="netdevicename_start"
+
+# uncomment the next line for biosdevname to be off by default
+# GOTO="netdevicename_end"
+
+LABEL="netdevicename_start"
# using NAME= instead of setting INTERFACE_NAME, so that persistent
# names aren't generated for these devices, they are "named" on each boot.
diff --git a/biosdevname.spec.fedora.in b/biosdevname.spec.fedora.in
index 7c77405..2b39634 100644
--- a/biosdevname.spec.fedora.in
+++ b/biosdevname.spec.fedora.in
@@ -9,7 +9,7 @@ URL: http://linux.dell.com/files/%{name}
# SMBIOS only exists on these arches. It's also likely that other
# arches don't expect the PCI bus to be sorted breadth-first, or of
# so, there haven't been any comments about that on LKML.
-ExclusiveArch: %{ix86} x86_64 ia64
+ExclusiveArch: %{ix86} x86_64
Source0: http://linux.dell.com/files/%{name}/permalink/%{name}-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: pciutils-devel, zlib-devel
@@ -37,7 +37,6 @@ make %{?_smp_mflags}
%install
rm -rf %{buildroot}
make install install-data DESTDIR=%{buildroot}
-rm %{buildroot}/sbin/%{name}S || :
%clean
rm -rf %{buildroot}
@@ -46,11 +45,32 @@ rm -rf %{buildroot}
%defattr(-,root,root,-)
%doc COPYING README
/sbin/%{name}
-%config(noreplace) %{_sysconfdir}/udev/rules.d/*.rules
+# hack for either /etc or /lib rules location
+/*/udev/rules.d/*.rules
%{_mandir}/man1/%{name}.1*
%changelog
+* Thu Feb 17 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.7-1
+- drop dump_pirq, suggest use biosdecode instead
+- don't use '#' in names, use 'p' instead, by popular demand
+- properly look for SMBIOS, then $PIR, then recurse
+- Add kernel command line parameter "biosdevname={0|1}" to turn off/on biosdevname
+- Fix segfault when BIOS advertises zero sized PIRQ Routing Table
+- Add 'bonding' and 'openvswitch' to the virtual devices list
+- fail PIRQ lookups if device domain is not 0
+- Don't suggest names if running in a virtual machine (Xen, KVM,
+ VMware tested, but should work on others)
+- Typo fixes
+
+* Tue Jan 25 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.6-1
+- drop biosdevnameS, it's unused and fails to build on F15
+
+* Tue Jan 25 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.5-1
+- install dump_pirq into /usr/sbin
+- fix udev rule, skip running if NAME is already set
+- move udev rule to /lib/udev/rules.d by default
+
* Thu Dec 16 2010 Matt Domsch <mdomsch@fedoraproject.org> - 0.3.4-1
- drop unnecessary explicit version requirement on udev
- bugfix: start indices at 1 not 0, to match Dell and HP server port designations
diff --git a/biosdevname.spec.suse.in b/biosdevname.spec.suse.in
index de00eaf..2dc3bdf 100644
--- a/biosdevname.spec.suse.in
+++ b/biosdevname.spec.suse.in
@@ -9,7 +9,7 @@ URL: http://linux.dell.com/files/%{name}
# SMBIOS only exists on these arches. It's also likely that other
# arches don't expect the PCI bus to be sorted breadth-first, or of
# so, there haven't been any comments about that on LKML.
-ExclusiveArch: %{ix86} x86_64 ia64
+ExclusiveArch: %{ix86} x86_64
Source0: http://linux.dell.com/files/%{name}/permalink/%{name}-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: pciutils-devel, zlib-devel
@@ -38,7 +38,6 @@ make %{?_smp_mflags}
%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
-rm %{buildroot}/sbin/%{name}S || :
%clean
rm -rf %{buildroot}
@@ -47,11 +46,32 @@ rm -rf %{buildroot}
%defattr(-,root,root,-)
%doc COPYING README
/sbin/%{name}
-%config(noreplace) %{_sysconfdir}/udev/rules.d/*%{name}.rules
+# hack for either /etc or /lib rules location
+/*/udev/rules.d/*.rules
%{_mandir}/man1/%{name}.1*
%changelog
+* Thu Feb 17 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.7-1
+- drop dump_pirq, suggest use biosdecode instead
+- don't use '#' in names, use 'p' instead, by popular demand
+- properly look for SMBIOS, then $PIR, then recurse
+- Add kernel command line parameter "biosdevname={0|1}" to turn off/on biosdevname
+- Fix segfault when BIOS advertises zero sized PIRQ Routing Table
+- Add 'bonding' and 'openvswitch' to the virtual devices list
+- fail PIRQ lookups if device domain is not 0
+- Don't suggest names if running in a virtual machine (Xen, KVM,
+ VMware tested, but should work on others)
+- Typo fixes
+
+* Tue Jan 25 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.6-1
+- drop biosdevnameS, it's unused and fails to build on F15
+
+* Tue Jan 25 2011 Matt Domsch <Matt_Domsch@dell.com> - 0.3.5-1
+- install dump_pirq into /usr/sbin
+- fix udev rule, skip running if NAME is already set
+- move udev rule to /lib/udev/rules.d by default
+
* Thu Dec 16 2010 Matt Domsch <mdomsch@fedoraproject.org> - 0.3.4-1
- drop unnecessary explicit version requirement on udev
- bugfix: start indices at 1 not 0, to match Dell and HP server port designations
diff --git a/configure.ac b/configure.ac
index 9c1b8d0..9519402 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
# vim:tw=0:ts=8:sw=8:et
AC_PREREQ(2.59)
-AC_INIT([biosdevname],[0.3.4],[Matt_Domsch@dell.com])
+AC_INIT([biosdevname],[0.3.11],[Jordan_Hargrave@dell.com])
AC_LANG([C])
AC_USE_SYSTEM_EXTENSIONS
AC_CONFIG_SRCDIR([src/read_proc.c])
@@ -24,9 +24,9 @@ AC_CHECK_FUNCS([realpath])
# Checks for libraries.
AC_CHECK_LIB([c], [snprintf])
# libz has to come before libpci because libpci needs it now.
-AC_CHECK_LIB([z], [gzopen], [],
+AC_CHECK_LIB([z], [gzopen], [true],
[AC_MSG_ERROR([You must install zlib-devel (Fedora/Red Hat/SuSE), libzip-dev (Debian/Ubuntu), or equivalent])])
-AC_CHECK_LIB([pci], [pci_alloc], [],
+AC_CHECK_LIB([pci], [pci_alloc], [true],
[AC_MSG_ERROR([You must install pciutils-devel (Fedora/Red Hat/SuSE), pciutils-dev (Debian/Ubuntu), or equivalent])],
-lz)
@@ -68,18 +68,21 @@ AC_CHECK_FUNCS([dup2 gettimeofday memset munmap select socket strcasecmp strchr
# this is ugly, but accounts for SLES 10, Red Hat/Fedora, and Ubuntu
# handles default udev rules as of udev 114 or thereabouts
-RULEDEST=71-biosdevname.rules
+RULEDEST=/lib/udev/rules.d/71-biosdevname.rules
+
if [[ -e /etc/udev/rules.d/60-net.rules ]]; then
- # RHEL / Fedora
- RULEDEST=60-biosdevname.rules
+ # RHEL 5 / Fedora
+ RULEDEST=/etc/udev/rules.d/60-biosdevname.rules
elif [[ -e /etc/udev/rules.d/31-network.rules ]]; then
# SLES 10
- RULEDEST=31-biosdevname.rules
+ RULEDEST=/etc/udev/rules.d/31-biosdevname.rules
elif [[ -e /etc/udev/rules.d/25-iftab.rules ]]; then
- # Ubuntu
- RULEDEST=25-biosdevname.rules
+ # older Ubuntu
+ RULEDEST=/etc/udev/rules.d/25-biosdevname.rules
fi
+RULEDIR=$(dirname $RULEDEST)
AC_SUBST(RULEDEST, $RULEDEST)
+AC_SUBST(RULEDIR, $RULEDIR)
echo "PACKAGE_NAME='$PACKAGE_NAME'" > version
echo "PACKAGE_VERSION='$PACKAGE_VERSION'" >> version
diff --git a/src/Makefile.am b/src/Makefile.am
index aedc2fd..4b59a71 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,7 +3,7 @@ AM_CFLAGS = -Wall -fno-strict-aliasing
man_MANS = biosdevname.1
-sbin_PROGRAMS = src/biosdevname src/biosdevnameS
+sbin_PROGRAMS = src/biosdevname
src_biosdevname_SOURCES = \
src/bios_dev_name.c \
src/bios_device.c \
@@ -16,9 +16,7 @@ src_biosdevname_SOURCES = \
src/dmidecode/dmidecode.c \
src/dmidecode/dmioem.c \
src/dmidecode/util.c
-
-src_biosdevnameS_SOURCES = $(src_biosdevname_SOURCES)
-src_biosdevnameS_LDFLAGS = -all-static
+src_biosdevname_LDADD = -lz -lpci
EXTRA_DIST += \
src/bios_dev_name.h \
diff --git a/src/bios_dev_name.c b/src/bios_dev_name.c
index 5757363..e2f8454 100644
--- a/src/bios_dev_name.c
+++ b/src/bios_dev_name.c
@@ -7,11 +7,16 @@
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
#include "libbiosdevname.h"
#include "bios_dev_name.h"
static struct bios_dev_name_opts opts;
+int nopirq;
+int smver_mjr;
+int smver_mnr;
static void usage(void)
{
@@ -21,6 +26,8 @@ static void usage(void)
fprintf(stderr, " -d or --debug enable debugging\n");
fprintf(stderr, " --policy [physical | all_ethN ]\n");
fprintf(stderr, " --prefix [string] string use for embedded NICs (default='em')\n");
+ fprintf(stderr, " --smbios [x.y] Require SMBIOS x.y or greater\n");
+ fprintf(stderr, " --nopirq Don't use $PIR table for slot numbers\n");
fprintf(stderr, " Example: biosdevname -i eth0\n");
fprintf(stderr, " returns: em1\n");
fprintf(stderr, " when eth0 is an embedded NIC with label '1' on the chassis.\n");
@@ -53,6 +60,8 @@ parse_opts(int argc, char **argv)
{"interface", no_argument, 0, 'i'},
{"policy", required_argument, 0, 'p'},
{"prefix", required_argument, 0, 'P'},
+ {"nopirq", no_argument, 0, 'x'},
+ {"smbios", required_argument, 0, 's'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv,
@@ -73,6 +82,12 @@ parse_opts(int argc, char **argv)
case 'P':
opts.prefix = optarg;
break;
+ case 's':
+ sscanf(optarg, "%u.%u", &smver_mjr, &smver_mnr);
+ break;
+ case 'x':
+ nopirq = 1;
+ break;
default:
usage();
exit(1);
@@ -89,6 +104,44 @@ parse_opts(int argc, char **argv)
opts.prefix = "em";
}
+static u_int32_t
+cpuid (u_int32_t eax, u_int32_t ecx)
+{
+ asm volatile (
+ "xor %%ebx, %%ebx; cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "a" (eax)
+ : "%ebx", "%edx");
+ return ecx;
+}
+
+/*
+ Algorithm suggested by:
+ http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458
+*/
+
+static int
+running_in_virtual_machine (void)
+{
+ u_int32_t eax=1U, ecx=0U;
+
+ ecx = cpuid (eax, ecx);
+ if (ecx & 0x80000000U)
+ return 1;
+ return 0;
+}
+
+static int
+running_as_root(void)
+{
+ uid_t uid = geteuid();
+ if (uid != 0) {
+ fprintf(stderr, "This program must be run as root.\n");
+ return 0;
+ }
+ return 1;
+}
+
int main(int argc, char *argv[])
{
int i, rc=0;
@@ -96,6 +149,11 @@ int main(int argc, char *argv[])
void *cookie = NULL;
parse_opts(argc, argv);
+
+ if (!running_as_root())
+ exit(3);
+ if (running_in_virtual_machine())
+ exit(4);
cookie = setup_bios_devices(opts.namingpolicy, opts.prefix);
if (!cookie) {
rc = 1;
diff --git a/src/dmidecode/dmidecode.c b/src/dmidecode/dmidecode.c
index ec91305..7bedc3a 100644
--- a/src/dmidecode/dmidecode.c
+++ b/src/dmidecode/dmidecode.c
@@ -40,6 +40,8 @@
#include "../pci.h"
#include "../naming_policy.h"
+extern int smver_mjr, smver_mnr;
+
static const char *bad_index = "<BAD INDEX>";
/*
@@ -176,12 +178,27 @@ static void to_dmi_header(struct dmi_header *h, u8 *data)
h->data=data;
}
+static int isvalidsmbios(int mjr, int mnr)
+{
+ if (!smver_mjr && !smver_mnr)
+ return 1;
+ if (mjr > smver_mjr)
+ return 1;
+ if ((mjr == smver_mjr) && (mnr >= smver_mnr))
+ return 1;
+ return 0;
+}
+
static void dmi_table(u32 base, u16 len, u16 num, u16 ver, const char *devmem, const struct libbiosdevname_state *state)
{
u8 *buf;
u8 *data;
int i=0;
+ /* Verify SMBIOS version */
+ if (!isvalidsmbios(ver >> 8, ver & 0xFF)) {
+ return;
+ }
if((buf=mem_chunk(base, len, devmem))==NULL)
{
#ifndef USE_MMAP
diff --git a/src/eths.c b/src/eths.c
index 6a6cd31..716f136 100644
--- a/src/eths.c
+++ b/src/eths.c
@@ -165,7 +165,7 @@ int unparse_network_device(char *buf, const int size, struct network_device *dev
char *s = buf;
s += snprintf(s, size-(s-buf), "Kernel name: %s\n", dev->kernel_name);
if (!zero_mac(dev->perm_addr))
- s += snprintf(s, size-(s-buf), "Permanant MAC: %s\n", pr_ether(buffer, sizeof(buffer), dev->perm_addr));
+ s += snprintf(s, size-(s-buf), "Permanent MAC: %s\n", pr_ether(buffer, sizeof(buffer), dev->perm_addr));
s += snprintf(s, size-(s-buf), "Assigned MAC : %s\n", pr_ether(buffer, sizeof(buffer), dev->dev_addr));
if (drvinfo_valid(dev)) {
s += snprintf(s, size-(s-buf), "Driver: %s\n", dev->drvinfo.driver);
@@ -201,7 +201,9 @@ int is_ethernet(struct network_device *dev)
goto out;
const char *nonethernet_drivers[] = {
+ "bonding",
"bridge",
+ "openvswitch",
"tun",
};
for (i=0; i<sizeof(nonethernet_drivers)/sizeof(nonethernet_drivers[0]); i++) {
diff --git a/src/naming_policy.c b/src/naming_policy.c
index 0bb9395..b651dc8 100644
--- a/src/naming_policy.c
+++ b/src/naming_policy.c
@@ -37,44 +37,49 @@ static void use_physical(const struct libbiosdevname_state *state, const char *p
char interface[IFNAMSIZ];
unsigned int portnum=0;
int known=0;
-
- memset(buffer, 0, sizeof(buffer));
- memset(location, 0, sizeof(location));
- memset(port, 0, sizeof(port));
- memset(interface, 0, sizeof(interface));
+ struct pci_device *vf;
list_for_each_entry(dev, &state->bios_devices, node) {
known = 0;
+ memset(buffer, 0, sizeof(buffer));
+ memset(location, 0, sizeof(location));
+ memset(port, 0, sizeof(port));
+ memset(interface, 0, sizeof(interface));
+
if (is_pci(dev)) {
+ vf = dev->pcidev;
if (dev->pcidev->physical_slot == 0) { /* embedded devices only */
- if (dev->pcidev->uses_sysfs & HAS_SYSFS_INDEX) {
- portnum = dev->pcidev->sysfs_index;
- snprintf(location, sizeof(location), "%s%u", prefix, portnum);
- known=1;
- }
- else if (dev->pcidev->uses_smbios & HAS_SMBIOS_INSTANCE && is_pci_smbios_type_ethernet(dev->pcidev)) {
- portnum = dev->pcidev->smbios_instance;
- snprintf(location, sizeof(location), "%s%u", prefix, portnum);
- known=1;
- }
- else if (dev->pcidev->embedded_index_valid) {
- portnum = dev->pcidev->embedded_index;
+ portnum = INT_MAX;
+ /* Use master VPD device if available */
+ if (vf->vpd_pf)
+ vf = vf->vpd_pf;
+ if (vf->uses_sysfs & HAS_SYSFS_INDEX)
+ portnum = vf->sysfs_index;
+ else if (vf->uses_smbios & HAS_SMBIOS_INSTANCE && is_pci_smbios_type_ethernet(vf))
+ portnum = vf->smbios_instance;
+ else if (vf->embedded_index_valid)
+ portnum = vf->embedded_index;
+ if (portnum != INT_MAX) {
snprintf(location, sizeof(location), "%s%u", prefix, portnum);
known=1;
}
}
else if (dev->pcidev->physical_slot < PHYSICAL_SLOT_UNKNOWN) {
- snprintf(location, sizeof(location), "pci%u", dev->pcidev->physical_slot);
- if (!dev->pcidev->is_sriov_virtual_function)
- portnum = dev->pcidev->index_in_slot;
+ snprintf(location, sizeof(location), "p%u", dev->pcidev->physical_slot);
+ if (dev->pcidev->vpd_port < INT_MAX)
+ portnum = dev->pcidev->vpd_port;
+ else if (!dev->pcidev->is_sriov_virtual_function)
+ portnum = dev->pcidev->index_in_slot;
else
portnum = dev->pcidev->pf->index_in_slot;
- snprintf(port, sizeof(port), "#%u", portnum);
+ snprintf(port, sizeof(port), "p%u", portnum);
known=1;
}
if (dev->pcidev->is_sriov_virtual_function)
snprintf(interface, sizeof(interface), "_%u", dev->pcidev->vf_index);
+ else if (dev->pcidev->vpd_pfi < INT_MAX)
+ snprintf(interface, sizeof(interface), "_%u", dev->pcidev->vpd_pfi);
if (known) {
snprintf(buffer, sizeof(buffer), "%s%s%s", location, port, interface);
diff --git a/src/pci.c b/src/pci.c
index a7ec6f9..9b9703a 100644
--- a/src/pci.c
+++ b/src/pci.c
@@ -15,11 +15,272 @@
#include <unistd.h>
#include <dirent.h>
#include <pci/pci.h>
+#include <fcntl.h>
#include "pci.h"
#include "sysfs.h"
#include "dmidecode/dmidecode.h"
#include "pirq.h"
+#ifndef PCI_CB_CAPABILITY_LIST
+#define PCI_CB_CAPABILITY_LIST 0x14
+#endif
+
+/* Borrowed from kernel vpd code */
+#define PCI_VPD_LRDT 0x80
+#define PCI_VPD_SRDT_END 0x78
+
+#define PCI_VPD_SRDT_LEN_MASK 0x7
+#define PCI_VPD_LRDT_TAG_SIZE 3
+#define PCI_VPD_SRDT_TAG_SIZE 1
+#define PCI_VPD_INFO_FLD_HDR_SIZE 3
+
+static inline u16 pci_vpd_lrdt_size(const u8 *lrdt)
+{
+ return (u16)lrdt[1] + ((u16)lrdt[2] << 8L);
+}
+
+static inline u8 pci_vpd_srdt_size(const u8* srdt)
+{
+ return (*srdt) & PCI_VPD_SRDT_LEN_MASK;
+}
+
+static inline u8 pci_vpd_info_field_size(const u8 *info_field)
+{
+ return info_field[2];
+}
+
+static int pci_vpd_size(struct pci_device *pdev, int fd)
+{
+ uint8_t buf[3], tag;
+ int off;
+
+ if (!is_pci_network(pdev))
+ return 0;
+ off = 0;
+ for(;;) {
+ if (pread(fd, buf, 1, off) != 1)
+ break;
+ if (buf[0] & PCI_VPD_LRDT) {
+ tag = buf[0];
+ if (pread(fd, buf, 3, off) != 3)
+ break;
+ off += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(buf);
+ } else {
+ tag = buf[0] & ~PCI_VPD_SRDT_LEN_MASK;
+ off += PCI_VPD_SRDT_TAG_SIZE + pci_vpd_srdt_size(buf);
+ }
+ if (tag == 0 || tag == 0xFF || tag == PCI_VPD_SRDT_END)
+ break;
+ }
+ return off;
+}
+
+static int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt)
+{
+ int i;
+
+ for (i = off; i < len;) {
+ u8 val = buf[i];
+
+ if (val & PCI_VPD_LRDT) {
+ if (i + PCI_VPD_LRDT_TAG_SIZE > len)
+ break;
+ if (val == rdt)
+ return i;
+ i += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(&buf[i]);
+ } else {
+ u8 tag = val & ~PCI_VPD_SRDT_LEN_MASK;
+
+ if (tag == rdt)
+ return i;
+ if (tag == PCI_VPD_SRDT_END)
+ break;
+ i += PCI_VPD_SRDT_TAG_SIZE + pci_vpd_srdt_size(&buf[i]);
+ }
+ }
+ return -1;
+}
+
+/* Search for matching key/subkey in the VPD data */
+static int pci_vpd_find_info_subkey(const u8 *buf, unsigned int off, unsigned int len,
+ const char *kw, const char *skw)
+{
+ int i;
+
+ for (i = off; i + PCI_VPD_INFO_FLD_HDR_SIZE <= off+len;) {
+ /* Match key and subkey names, can use * for regex */
+ if ((kw[0] == '*' || buf[i+0] == kw[0]) &&
+ (kw[1] == '*' || buf[i+1] == kw[1]) &&
+ (skw[0] == '*' || !memcmp(&buf[i+3], skw, 3)))
+ return i;
+ i += PCI_VPD_INFO_FLD_HDR_SIZE + pci_vpd_info_field_size(&buf[i]);
+ }
+ return -1;
+}
+
+static int parse_vpd(struct libbiosdevname_state *state, struct pci_device *pdev, int len, unsigned char *vpd)
+{
+ int i, j, k, isz, jsz, port, func, pfi;
+ struct pci_device *vf;
+
+ i = pci_vpd_find_tag(vpd, 0, len, 0x90);
+ if (i < 0)
+ return 1;
+ isz = pci_vpd_lrdt_size(&vpd[i]);
+ i += PCI_VPD_LRDT_TAG_SIZE;
+
+ /* Lookup Version */
+ j = pci_vpd_find_info_subkey(vpd, i, isz, "**", "DSV");
+ if (j < 0)
+ return 1;
+ jsz = pci_vpd_info_field_size(&vpd[j]);
+ j += PCI_VPD_INFO_FLD_HDR_SIZE;
+ if (memcmp(vpd+j+3, "1028VPDR.VER1.0", 15))
+ return 1;
+
+ /* Lookup Port Mappings */
+ j = pci_vpd_find_info_subkey(vpd, i, isz, "**", "DCM");
+ if (j < 0)
+ return 1;
+ jsz = pci_vpd_info_field_size(&vpd[j]);
+ j += PCI_VPD_INFO_FLD_HDR_SIZE;
+
+ for (k=3; k<jsz; k+=10) {
+ /* Parse Port Info */
+ sscanf((char *)vpd+j+k, "%1x%1x%2x", &port, &func, &pfi);
+ if ((vf = find_pci_dev_by_pci_addr(state, pdev->pci_dev->domain,
+ pdev->pci_dev->bus,
+ pdev->pci_dev->dev,
+ func)) != NULL) {
+ if (vf->vpd_port == INT_MAX) {
+ vf->vpd_port = port;
+ vf->vpd_pfi = pfi;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Read and parse PCI VPD section if it exists */
+static int read_pci_vpd(struct libbiosdevname_state *state, struct pci_device *pdev)
+{
+ char path[PATH_MAX];
+ char pci_name[16];
+ int fd, rc=1;
+ unsigned char *vpd;
+ off_t size;
+ ssize_t nrd;
+
+ unparse_pci_name(pci_name, sizeof(pci_name), pdev->pci_dev);
+ snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/vpd", pci_name);
+ if ((fd = open(path, O_RDONLY|O_SYNC)) >= 0) {
+ size = pci_vpd_size(pdev, fd);
+ if (size > 0) {
+ vpd = malloc(size);
+ if (vpd != NULL) {
+ if ((nrd = pread(fd, vpd, size, 0)) > 0)
+ rc = parse_vpd(state, pdev, nrd, vpd);
+ free(vpd);
+ }
+ }
+ close(fd);
+ }
+ return rc;
+}
+
+static void set_pci_vpd_instance(struct libbiosdevname_state *state)
+{
+ struct pci_device *dev, *dev2;
+
+ /* Read VPD information for each device */
+ list_for_each_entry(dev, &state->pci_devices, node) {
+ read_pci_vpd(state, dev);
+ }
+
+ /* Now match VPD master device */
+ list_for_each_entry(dev, &state->pci_devices, node) {
+ if (dev->vpd_port == INT_MAX)
+ continue;
+ list_for_each_entry(dev2, &state->pci_devices, node) {
+ if (dev2->pci_dev->domain == dev->pci_dev->domain &&
+ dev2->pci_dev->bus == dev->pci_dev->bus &&
+ dev2->pci_dev->dev == dev->pci_dev->dev &&
+ dev2->vpd_port == dev->vpd_port) {
+ dev2->vpd_count++;
+ dev->vpd_pf = dev2;
+ break;
+ }
+ }
+ }
+
+ /* Delete all VPD devices with single function */
+ list_for_each_entry(dev, &state->pci_devices, node) {
+ if (dev->vpd_count == 1) {
+ dev->vpd_port = INT_MAX;
+ dev->vpd_pfi = INT_MAX;
+ dev->vpd_pf = NULL;
+ }
+ }
+}
+
+static int pci_find_capability(struct pci_dev *p, int cap)
+{
+ u16 status;
+ u8 hdr, id;
+ int pos, ttl = 48;
+
+ status = pci_read_word(p, PCI_STATUS);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+ hdr = pci_read_byte(p, PCI_HEADER_TYPE);
+ switch(hdr & 0x7F) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ pos = PCI_CAPABILITY_LIST;
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ pos = PCI_CB_CAPABILITY_LIST;
+ break;
+ default:
+ return 0;
+ }
+
+ while (ttl--) {
+ pos = pci_read_byte(p, pos);
+ if (pos < 0x40)
+ break;
+ pos &= ~3;
+ id = pci_read_byte(p, pos+PCI_CAP_LIST_ID);
+ if (id == 0xFF)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+static struct pci_device *
+find_parent(struct libbiosdevname_state *state, struct pci_device *dev);
+
+static int pcie_get_slot(struct libbiosdevname_state *state, struct pci_device *p)
+{
+ int pos;
+ u32 slot, flag;
+
+ while (p) {
+ /* Return PCIE physical slot number */
+ if ((pos = pci_find_capability(p->pci_dev, PCI_CAP_ID_EXP)) != 0) {
+ flag = pci_read_word(p->pci_dev, pos + PCI_EXP_FLAGS);
+ slot = (pci_read_long(p->pci_dev, pos + PCI_EXP_SLTCAP) >> 19);
+ if ((flag & PCI_EXP_FLAGS_SLOT) && slot)
+ return slot;
+ }
+ p = find_parent(state, p);
+ }
+ return PHYSICAL_SLOT_UNKNOWN;
+}
+
static int read_pci_sysfs_path(char *buf, size_t bufsize, const struct pci_dev *pdev)
{
char path[PATH_MAX];
@@ -221,38 +482,30 @@ find_parent(struct libbiosdevname_state *state, struct pci_device *dev)
* our parent bridge on a card may not be included
* in the SMBIOS table. In that case, it falls back to "unknown".
*/
-static int pci_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev)
+static inline int pci_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev)
{
- struct pci_device *d = dev;
- int slot = d->physical_slot;
- while (d && slot == PHYSICAL_SLOT_UNKNOWN) {
- d = find_parent(state, d);
- if (d)
- slot = d->physical_slot;
- }
- return slot;
+ return dev->physical_slot;
}
-static int pirq_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev)
+static inline int pirq_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev)
{
- struct pci_device *d = dev;
- int slot;
- slot = pirq_pci_dev_to_slot(state->pirq_table, d->pci_dev->bus, d->pci_dev->dev);
- while (d && slot == PHYSICAL_SLOT_UNKNOWN) {
- d = find_parent(state, d);
- if (d)
- slot = pirq_pci_dev_to_slot(state->pirq_table, d->pci_dev->bus, d->pci_dev->dev);
- }
- return slot;
+ return pirq_pci_dev_to_slot(state->pirq_table, pci_domain_nr(dev->pci_dev), dev->pci_dev->bus, dev->pci_dev->dev);
}
static void dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev)
{
+ struct pci_device *d = dev;
int slot;
- slot = pci_dev_to_slot(state, dev);
- if (slot == PHYSICAL_SLOT_UNKNOWN) {
- slot = pirq_dev_to_slot(state, dev);
- }
+ do {
+ slot = pci_dev_to_slot(state, d);
+ if (slot == PHYSICAL_SLOT_UNKNOWN)
+ slot = pcie_get_slot(state, d);
+ if (slot == PHYSICAL_SLOT_UNKNOWN)
+ slot = pirq_dev_to_slot(state, d);
+ if (slot == PHYSICAL_SLOT_UNKNOWN)
+ d = find_parent(state, d);
+ } while (d && slot == PHYSICAL_SLOT_UNKNOWN);
+
dev->physical_slot = slot;
}
@@ -310,6 +563,7 @@ static void fill_pci_dev_sysfs(struct pci_device *dev, struct pci_dev *p)
}
}
+
static void add_pci_dev(struct libbiosdevname_state *state,
struct pci_dev *p)
{
@@ -327,6 +581,9 @@ static void add_pci_dev(struct libbiosdevname_state *state,
dev->physical_slot = PHYSICAL_SLOT_UNKNOWN;
dev->class = pci_read_word(p, PCI_CLASS_DEVICE);
dev->vf_index = INT_MAX;
+ dev->vpd_port = INT_MAX;
+ dev->vpd_pfi = INT_MAX;
+ dev->vpd_pf = NULL;
fill_pci_dev_sysfs(dev, p);
list_add(&dev->node, &state->pci_devices);
}
@@ -391,6 +648,8 @@ static int set_embedded_index(struct libbiosdevname_state *state)
continue;
if (pcidev->is_sriov_virtual_function) /* skip sriov VFs, they're handled later */
continue;
+ if (pcidev->vpd_port != INT_MAX)
+ continue;
pcidev->embedded_index = index;
pcidev->embedded_index_valid = 1;
index++;
@@ -414,8 +673,8 @@ static void set_sriov_pf_vf(struct libbiosdevname_state *state)
static int sort_pci(const struct pci_device *a, const struct pci_device *b)
{
- if (a->pci_dev->domain < b->pci_dev->domain) return -1;
- else if (a->pci_dev->domain > b->pci_dev->domain) return 1;
+ if (pci_domain_nr(a->pci_dev) < pci_domain_nr(b->pci_dev)) return -1;
+ else if (pci_domain_nr(a->pci_dev) > pci_domain_nr(b->pci_dev)) return 1;
if (a->pci_dev->bus < b->pci_dev->bus) return -1;
else if (a->pci_dev->bus > b->pci_dev->bus) return 1;
@@ -476,6 +735,7 @@ int get_pci_devices(struct libbiosdevname_state *state)
/* ordering here is important */
dmidecode_main(state); /* this will fail on Xen guests, that's OK */
sort_device_list(state);
+ set_pci_vpd_instance(state);
set_pci_slots(state);
set_embedded_index(state);
set_pci_slot_index(state);
@@ -542,8 +802,8 @@ int unparse_pci_device(char *buf, const int size, const struct pci_device *p)
if (p->smbios_type) {
s += snprintf(s, size-(s-buf), "SMBIOS Device Type: ");
s += unparse_smbios_type41_type(s, size-(s-buf), p->smbios_type);
- s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance);
- s += snprintf(s, size-(s-buf), "SMBIOS Enabled: %s\n", p->smbios_instance?"True":"False");
+ if (p->smbios_instance)
+ s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance);
}
if (p->uses_smbios & HAS_SMBIOS_LABEL && p->smbios_label)
s += snprintf(s, size-(s-buf), "SMBIOS Label: %s\n", p->smbios_label);
@@ -554,8 +814,16 @@ int unparse_pci_device(char *buf, const int size, const struct pci_device *p)
if (p->physical_slot > 0 && !p->is_sriov_virtual_function)
s += snprintf(s, size-(s-buf), "Index in slot: %u\n", p->index_in_slot);
if (p->embedded_index_valid)
- s += snprintf(s, size-(s-buf), "Embededed Index: %u\n", p->embedded_index);
-
+ s += snprintf(s, size-(s-buf), "Embedded Index: %u\n", p->embedded_index);
+ if (p->vpd_port < INT_MAX) {
+ s += snprintf(s, size-(s-buf), "VPD Port: %u\n", p->vpd_port);
+ s += snprintf(s, size-(s-buf), "VPD Index: %u\n", p->vpd_pfi);
+ if (p->vpd_pf) {
+ s += snprintf(s, size-(s-buf), "VPD PCI master: ");
+ s += unparse_pci_name(s, size-(s-buf), p->vpd_pf->pci_dev);
+ s += snprintf(s, size-(s-buf), " count %d\n", p->vpd_pf->vpd_count);
+ }
+ }
if (!list_empty(&p->vfs)) {
s += snprintf(s, size-(s-buf), "Virtual Functions:\n");
list_for_each_entry(dev, &p->vfs, vfnode) {
diff --git a/src/pci.h b/src/pci.h
index 78d6c9f..374f7b3 100644
--- a/src/pci.h
+++ b/src/pci.h
@@ -29,6 +29,10 @@ struct pci_device {
char * sysfs_label;
unsigned char uses_sysfs;
unsigned int vf_index;
+ unsigned int vpd_count;
+ unsigned int vpd_pfi;
+ unsigned int vpd_port;
+ struct pci_device *vpd_pf;
struct pci_device *pf;
struct list_head vfnode;
struct list_head vfs;
diff --git a/src/pirq.c b/src/pirq.c
index bf2ef2a..6568c24 100644
--- a/src/pirq.c
+++ b/src/pirq.c
@@ -18,21 +18,28 @@
#include <sys/mman.h>
#include "pirq.h"
+extern int nopirq;
+
/* If unknown, use INT_MAX so they get sorted last */
-int pirq_pci_dev_to_slot(struct routing_table *table, int bus, int dev)
+int pirq_pci_dev_to_slot(struct routing_table *table, int domain, int bus, int dev)
{
int i, num_slots;
struct slot_entry *slot;
if (!table)
return INT_MAX;
+ if (domain != 0) /* can't represent non-zero domains in PIRQ */
+ return INT_MAX;
num_slots = (table->size - 32) / sizeof(*slot);
for (i=0; i<num_slots; i++) {
slot = &table->slot[i];
if (slot->bus == bus &&
- PCI_DEVICE(slot->device) == dev)
+ PCI_DEVICE(slot->device) == dev) {
+ if (slot->slot >= '1' && slot->slot <= '9')
+ return slot->slot - '0';
return slot->slot;
+ }
}
return INT_MAX;
}
@@ -47,8 +54,13 @@ struct routing_table * pirq_alloc_read_table()
int i;
void *mem;
off_t offset=0L;
- int fd=open("/dev/mem", O_RDONLY);
+ int fd;
+ /* Skip PIRQ table parsing */
+ if (nopirq) {
+ return NULL;
+ }
+ fd = open("/dev/mem", O_RDONLY);
if(fd==-1)
return NULL;
@@ -63,9 +75,16 @@ struct routing_table * pirq_alloc_read_table()
table = (struct routing_table *)(mem+offset);
size = table->size;
/* quick sanity checks */
+ if (size == 0) {
+ table = NULL;
+ break;
+ }
/* Version must be 1.0 */
- if (! (table->version >> 8)==1 &&
- (table->version && 0xFF) == 0) break;
+ if (!((table->version >> 8) == 1 &&
+ (table->version & 0xFF) == 0)) {
+ table = NULL;
+ break;
+ }
table = malloc(size);
if (!table) break;
@@ -123,7 +142,7 @@ pirq_unparse_routing_table(struct routing_table *table)
printf("Bus : %x\n", table->router_bus);
printf("DevFn : %x\n", table->router_devfn);
printf("Exclusive IRQs : %x\n", table->exclusive_irqs);
- printf("Compatable Router: %x\n", table->compatable_router);
+ printf("Compatible Router: %x\n", table->compatable_router);
num_slots = (table->size - 32) / sizeof(*slot);
slot = &table->slot[0];
diff --git a/src/pirq.h b/src/pirq.h
index 293b81d..2fb0f8d 100644
--- a/src/pirq.h
+++ b/src/pirq.h
@@ -53,6 +53,6 @@ struct routing_table {
#define PCI_FUNC(devfn) ((devfn) & 0x07)
extern struct routing_table * pirq_alloc_read_table(void);
extern void pirq_free_table(struct routing_table *table);
-extern int pirq_pci_dev_to_slot(struct routing_table *table, int bus, int dev);
+extern int pirq_pci_dev_to_slot(struct routing_table *table, int domain, int bus, int dev);
#endif /* PIRQ_H_INCLUDED */
diff --git a/src/sysfs.c b/src/sysfs.c
index f2c616e..71e84e7 100644
--- a/src/sysfs.c
+++ b/src/sysfs.c
@@ -50,8 +50,9 @@ int sysfs_read_file(const char * path, char **output)
}
length = read(fd, result, resultsize-1);
+ close(fd);
+
if (length < 0) {
- close(fd);
ret = -1;
goto free_out;
}