diff options
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | biosdevname.1 | 31 | ||||
-rw-r--r-- | biosdevname.rules.in | 14 | ||||
-rw-r--r-- | biosdevname.spec.fedora.in | 26 | ||||
-rw-r--r-- | biosdevname.spec.suse.in | 26 | ||||
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/bios_dev_name.c | 58 | ||||
-rw-r--r-- | src/dmidecode/dmidecode.c | 17 | ||||
-rw-r--r-- | src/eths.c | 4 | ||||
-rw-r--r-- | src/naming_policy.c | 47 | ||||
-rw-r--r-- | src/pci.c | 326 | ||||
-rw-r--r-- | src/pci.h | 4 | ||||
-rw-r--r-- | src/pirq.c | 31 | ||||
-rw-r--r-- | src/pirq.h | 2 | ||||
-rw-r--r-- | src/sysfs.c | 3 |
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 @@ -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 @@ -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); @@ -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) { @@ -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; @@ -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]; @@ -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; } |