xref: /f-stack/dpdk/usertools/dpdk-pmdinfo.py (revision 2d9fd380)
1*2d9fd380Sjfb8856606#!/usr/bin/env python3
2d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause
3d30ea906Sjfb8856606# Copyright(c) 2016  Neil Horman <[email protected]>
42bfe3f2eSlogwang
52bfe3f2eSlogwang# -------------------------------------------------------------------------
62bfe3f2eSlogwang#
72bfe3f2eSlogwang# Utility to dump PMD_INFO_STRING support from an object file
82bfe3f2eSlogwang#
92bfe3f2eSlogwang# -------------------------------------------------------------------------
102bfe3f2eSlogwangimport json
112bfe3f2eSlogwangimport os
122bfe3f2eSlogwangimport platform
132bfe3f2eSlogwangimport sys
14*2d9fd380Sjfb8856606import argparse
152bfe3f2eSlogwangfrom elftools.common.exceptions import ELFError
164418919fSjohnjiangfrom elftools.common.py3compat import byte2int
172bfe3f2eSlogwangfrom elftools.elf.elffile import ELFFile
18*2d9fd380Sjfb8856606
192bfe3f2eSlogwang
202bfe3f2eSlogwang# For running from development directory. It should take precedence over the
212bfe3f2eSlogwang# installed pyelftools.
222bfe3f2eSlogwangsys.path.insert(0, '.')
232bfe3f2eSlogwang
242bfe3f2eSlogwangraw_output = False
252bfe3f2eSlogwangpcidb = None
262bfe3f2eSlogwang
272bfe3f2eSlogwang# ===========================================
282bfe3f2eSlogwang
292bfe3f2eSlogwangclass Vendor:
302bfe3f2eSlogwang    """
312bfe3f2eSlogwang    Class for vendors. This is the top level class
322bfe3f2eSlogwang    for the devices belong to a specific vendor.
332bfe3f2eSlogwang    self.devices is the device dictionary
342bfe3f2eSlogwang    subdevices are in each device.
352bfe3f2eSlogwang    """
362bfe3f2eSlogwang
372bfe3f2eSlogwang    def __init__(self, vendorStr):
382bfe3f2eSlogwang        """
392bfe3f2eSlogwang        Class initializes with the raw line from pci.ids
402bfe3f2eSlogwang        Parsing takes place inside __init__
412bfe3f2eSlogwang        """
422bfe3f2eSlogwang        self.ID = vendorStr.split()[0]
432bfe3f2eSlogwang        self.name = vendorStr.replace("%s " % self.ID, "").rstrip()
442bfe3f2eSlogwang        self.devices = {}
452bfe3f2eSlogwang
462bfe3f2eSlogwang    def addDevice(self, deviceStr):
472bfe3f2eSlogwang        """
482bfe3f2eSlogwang        Adds a device to self.devices
492bfe3f2eSlogwang        takes the raw line from pci.ids
502bfe3f2eSlogwang        """
512bfe3f2eSlogwang        s = deviceStr.strip()
522bfe3f2eSlogwang        devID = s.split()[0]
532bfe3f2eSlogwang        if devID in self.devices:
542bfe3f2eSlogwang            pass
552bfe3f2eSlogwang        else:
562bfe3f2eSlogwang            self.devices[devID] = Device(deviceStr)
572bfe3f2eSlogwang
582bfe3f2eSlogwang    def report(self):
592bfe3f2eSlogwang        print(self.ID, self.name)
602bfe3f2eSlogwang        for id, dev in self.devices.items():
612bfe3f2eSlogwang            dev.report()
622bfe3f2eSlogwang
632bfe3f2eSlogwang    def find_device(self, devid):
642bfe3f2eSlogwang        # convert to a hex string and remove 0x
652bfe3f2eSlogwang        devid = hex(devid)[2:]
662bfe3f2eSlogwang        try:
672bfe3f2eSlogwang            return self.devices[devid]
682bfe3f2eSlogwang        except:
692bfe3f2eSlogwang            return Device("%s  Unknown Device" % devid)
702bfe3f2eSlogwang
712bfe3f2eSlogwang
722bfe3f2eSlogwangclass Device:
732bfe3f2eSlogwang
742bfe3f2eSlogwang    def __init__(self, deviceStr):
752bfe3f2eSlogwang        """
762bfe3f2eSlogwang        Class for each device.
772bfe3f2eSlogwang        Each vendor has its own devices dictionary.
782bfe3f2eSlogwang        """
792bfe3f2eSlogwang        s = deviceStr.strip()
802bfe3f2eSlogwang        self.ID = s.split()[0]
812bfe3f2eSlogwang        self.name = s.replace("%s  " % self.ID, "")
822bfe3f2eSlogwang        self.subdevices = {}
832bfe3f2eSlogwang
842bfe3f2eSlogwang    def report(self):
852bfe3f2eSlogwang        print("\t%s\t%s" % (self.ID, self.name))
862bfe3f2eSlogwang        for subID, subdev in self.subdevices.items():
872bfe3f2eSlogwang            subdev.report()
882bfe3f2eSlogwang
892bfe3f2eSlogwang    def addSubDevice(self, subDeviceStr):
902bfe3f2eSlogwang        """
912bfe3f2eSlogwang        Adds a subvendor, subdevice to device.
922bfe3f2eSlogwang        Uses raw line from pci.ids
932bfe3f2eSlogwang        """
942bfe3f2eSlogwang        s = subDeviceStr.strip()
952bfe3f2eSlogwang        spl = s.split()
962bfe3f2eSlogwang        subVendorID = spl[0]
972bfe3f2eSlogwang        subDeviceID = spl[1]
982bfe3f2eSlogwang        subDeviceName = s.split("  ")[-1]
992bfe3f2eSlogwang        devID = "%s:%s" % (subVendorID, subDeviceID)
1002bfe3f2eSlogwang        self.subdevices[devID] = SubDevice(
1012bfe3f2eSlogwang            subVendorID, subDeviceID, subDeviceName)
1022bfe3f2eSlogwang
1032bfe3f2eSlogwang    def find_subid(self, subven, subdev):
1042bfe3f2eSlogwang        subven = hex(subven)[2:]
1052bfe3f2eSlogwang        subdev = hex(subdev)[2:]
1062bfe3f2eSlogwang        devid = "%s:%s" % (subven, subdev)
1072bfe3f2eSlogwang
1082bfe3f2eSlogwang        try:
1092bfe3f2eSlogwang            return self.subdevices[devid]
1102bfe3f2eSlogwang        except:
1112bfe3f2eSlogwang            if (subven == "ffff" and subdev == "ffff"):
1122bfe3f2eSlogwang                return SubDevice("ffff", "ffff", "(All Subdevices)")
1132bfe3f2eSlogwang            return SubDevice(subven, subdev, "(Unknown Subdevice)")
1142bfe3f2eSlogwang
1152bfe3f2eSlogwang
1162bfe3f2eSlogwangclass SubDevice:
1172bfe3f2eSlogwang    """
1182bfe3f2eSlogwang    Class for subdevices.
1192bfe3f2eSlogwang    """
1202bfe3f2eSlogwang
1212bfe3f2eSlogwang    def __init__(self, vendor, device, name):
1222bfe3f2eSlogwang        """
1232bfe3f2eSlogwang        Class initializes with vendorid, deviceid and name
1242bfe3f2eSlogwang        """
1252bfe3f2eSlogwang        self.vendorID = vendor
1262bfe3f2eSlogwang        self.deviceID = device
1272bfe3f2eSlogwang        self.name = name
1282bfe3f2eSlogwang
1292bfe3f2eSlogwang    def report(self):
1302bfe3f2eSlogwang        print("\t\t%s\t%s\t%s" % (self.vendorID, self.deviceID, self.name))
1312bfe3f2eSlogwang
1322bfe3f2eSlogwang
1332bfe3f2eSlogwangclass PCIIds:
1342bfe3f2eSlogwang    """
1352bfe3f2eSlogwang    Top class for all pci.ids entries.
1362bfe3f2eSlogwang    All queries will be asked to this class.
1372bfe3f2eSlogwang    PCIIds.vendors["0e11"].devices["0046"].\
1382bfe3f2eSlogwang    subdevices["0e11:4091"].name  =  "Smart Array 6i"
1392bfe3f2eSlogwang    """
1402bfe3f2eSlogwang
1412bfe3f2eSlogwang    def __init__(self, filename):
1422bfe3f2eSlogwang        """
1432bfe3f2eSlogwang        Prepares the directories.
1442bfe3f2eSlogwang        Checks local data file.
1452bfe3f2eSlogwang        Tries to load from local, if not found, downloads from web
1462bfe3f2eSlogwang        """
1472bfe3f2eSlogwang        self.version = ""
1482bfe3f2eSlogwang        self.date = ""
1492bfe3f2eSlogwang        self.vendors = {}
1502bfe3f2eSlogwang        self.contents = None
1512bfe3f2eSlogwang        self.readLocal(filename)
1522bfe3f2eSlogwang        self.parse()
1532bfe3f2eSlogwang
1542bfe3f2eSlogwang    def reportVendors(self):
1552bfe3f2eSlogwang        """Reports the vendors
1562bfe3f2eSlogwang        """
1572bfe3f2eSlogwang        for vid, v in self.vendors.items():
1582bfe3f2eSlogwang            print(v.ID, v.name)
1592bfe3f2eSlogwang
1602bfe3f2eSlogwang    def report(self, vendor=None):
1612bfe3f2eSlogwang        """
1622bfe3f2eSlogwang        Reports everything for all vendors or a specific vendor
1632bfe3f2eSlogwang        PCIIds.report()  reports everything
1642bfe3f2eSlogwang        PCIIDs.report("0e11") reports only "Compaq Computer Corporation"
1652bfe3f2eSlogwang        """
1662bfe3f2eSlogwang        if vendor is not None:
1672bfe3f2eSlogwang            self.vendors[vendor].report()
1682bfe3f2eSlogwang        else:
1692bfe3f2eSlogwang            for vID, v in self.vendors.items():
1702bfe3f2eSlogwang                v.report()
1712bfe3f2eSlogwang
1722bfe3f2eSlogwang    def find_vendor(self, vid):
1732bfe3f2eSlogwang        # convert vid to a hex string and remove the 0x
1742bfe3f2eSlogwang        vid = hex(vid)[2:]
1752bfe3f2eSlogwang
1762bfe3f2eSlogwang        try:
1772bfe3f2eSlogwang            return self.vendors[vid]
1782bfe3f2eSlogwang        except:
1792bfe3f2eSlogwang            return Vendor("%s Unknown Vendor" % (vid))
1802bfe3f2eSlogwang
1812bfe3f2eSlogwang    def findDate(self, content):
1822bfe3f2eSlogwang        for l in content:
1832bfe3f2eSlogwang            if l.find("Date:") > -1:
1842bfe3f2eSlogwang                return l.split()[-2].replace("-", "")
1852bfe3f2eSlogwang        return None
1862bfe3f2eSlogwang
1872bfe3f2eSlogwang    def parse(self):
188*2d9fd380Sjfb8856606        if not self.contents:
1892bfe3f2eSlogwang            print("data/%s-pci.ids not found" % self.date)
1902bfe3f2eSlogwang        else:
1912bfe3f2eSlogwang            vendorID = ""
1922bfe3f2eSlogwang            deviceID = ""
1932bfe3f2eSlogwang            for l in self.contents:
1942bfe3f2eSlogwang                if l[0] == "#":
1952bfe3f2eSlogwang                    continue
196*2d9fd380Sjfb8856606                elif not l.strip():
1972bfe3f2eSlogwang                    continue
1982bfe3f2eSlogwang                else:
1992bfe3f2eSlogwang                    if l.find("\t\t") == 0:
2002bfe3f2eSlogwang                        self.vendors[vendorID].devices[
2012bfe3f2eSlogwang                            deviceID].addSubDevice(l)
2022bfe3f2eSlogwang                    elif l.find("\t") == 0:
2032bfe3f2eSlogwang                        deviceID = l.strip().split()[0]
2042bfe3f2eSlogwang                        self.vendors[vendorID].addDevice(l)
2052bfe3f2eSlogwang                    else:
2062bfe3f2eSlogwang                        vendorID = l.split()[0]
2072bfe3f2eSlogwang                        self.vendors[vendorID] = Vendor(l)
2082bfe3f2eSlogwang
2092bfe3f2eSlogwang    def readLocal(self, filename):
2102bfe3f2eSlogwang        """
2112bfe3f2eSlogwang        Reads the local file
2122bfe3f2eSlogwang        """
213*2d9fd380Sjfb8856606        with open(filename, 'r', encoding='utf-8') as f:
2144418919fSjohnjiang            self.contents = f.readlines()
2152bfe3f2eSlogwang        self.date = self.findDate(self.contents)
2162bfe3f2eSlogwang
2172bfe3f2eSlogwang    def loadLocal(self):
2182bfe3f2eSlogwang        """
2192bfe3f2eSlogwang        Loads database from local. If there is no file,
2202bfe3f2eSlogwang        it creates a new one from web
2212bfe3f2eSlogwang        """
2222bfe3f2eSlogwang        self.date = idsfile[0].split("/")[1].split("-")[0]
2232bfe3f2eSlogwang        self.readLocal()
2242bfe3f2eSlogwang
2252bfe3f2eSlogwang
2262bfe3f2eSlogwang# =======================================
2272bfe3f2eSlogwang
2282bfe3f2eSlogwangdef search_file(filename, search_path):
2292bfe3f2eSlogwang    """ Given a search path, find file with requested name """
230*2d9fd380Sjfb8856606    for path in search_path.split(':'):
2312bfe3f2eSlogwang        candidate = os.path.join(path, filename)
2322bfe3f2eSlogwang        if os.path.exists(candidate):
2332bfe3f2eSlogwang            return os.path.abspath(candidate)
2342bfe3f2eSlogwang    return None
2352bfe3f2eSlogwang
2362bfe3f2eSlogwang
2372bfe3f2eSlogwangclass ReadElf(object):
2382bfe3f2eSlogwang    """ display_* methods are used to emit output into the output stream
2392bfe3f2eSlogwang    """
2402bfe3f2eSlogwang
2412bfe3f2eSlogwang    def __init__(self, file, output):
2422bfe3f2eSlogwang        """ file:
2432bfe3f2eSlogwang                stream object with the ELF file to read
2442bfe3f2eSlogwang
2452bfe3f2eSlogwang            output:
2462bfe3f2eSlogwang                output stream to write to
2472bfe3f2eSlogwang        """
2482bfe3f2eSlogwang        self.elffile = ELFFile(file)
2492bfe3f2eSlogwang        self.output = output
2502bfe3f2eSlogwang
2512bfe3f2eSlogwang        # Lazily initialized if a debug dump is requested
2522bfe3f2eSlogwang        self._dwarfinfo = None
2532bfe3f2eSlogwang
2542bfe3f2eSlogwang        self._versioninfo = None
2552bfe3f2eSlogwang
2562bfe3f2eSlogwang    def _section_from_spec(self, spec):
2572bfe3f2eSlogwang        """ Retrieve a section given a "spec" (either number or name).
2582bfe3f2eSlogwang            Return None if no such section exists in the file.
2592bfe3f2eSlogwang        """
2602bfe3f2eSlogwang        try:
2612bfe3f2eSlogwang            num = int(spec)
2622bfe3f2eSlogwang            if num < self.elffile.num_sections():
2632bfe3f2eSlogwang                return self.elffile.get_section(num)
2642bfe3f2eSlogwang            return None
2652bfe3f2eSlogwang        except ValueError:
2662bfe3f2eSlogwang            # Not a number. Must be a name then
2674418919fSjohnjiang            section = self.elffile.get_section_by_name(force_unicode(spec))
2684418919fSjohnjiang            if section is None:
2694418919fSjohnjiang                # No match with a unicode name.
2704418919fSjohnjiang                # Some versions of pyelftools (<= 0.23) store internal strings
2714418919fSjohnjiang                # as bytes. Try again with the name encoded as bytes.
2724418919fSjohnjiang                section = self.elffile.get_section_by_name(force_bytes(spec))
2734418919fSjohnjiang            return section
2742bfe3f2eSlogwang
2752bfe3f2eSlogwang    def pretty_print_pmdinfo(self, pmdinfo):
2762bfe3f2eSlogwang        global pcidb
2772bfe3f2eSlogwang
2782bfe3f2eSlogwang        for i in pmdinfo["pci_ids"]:
2792bfe3f2eSlogwang            vendor = pcidb.find_vendor(i[0])
2802bfe3f2eSlogwang            device = vendor.find_device(i[1])
2812bfe3f2eSlogwang            subdev = device.find_subid(i[2], i[3])
2822bfe3f2eSlogwang            print("%s (%s) : %s (%s) %s" %
2832bfe3f2eSlogwang                  (vendor.name, vendor.ID, device.name,
2842bfe3f2eSlogwang                   device.ID, subdev.name))
2852bfe3f2eSlogwang
2862bfe3f2eSlogwang    def parse_pmd_info_string(self, mystring):
2872bfe3f2eSlogwang        global raw_output
2882bfe3f2eSlogwang        global pcidb
2892bfe3f2eSlogwang
2902bfe3f2eSlogwang        optional_pmd_info = [
2912bfe3f2eSlogwang            {'id': 'params', 'tag': 'PMD PARAMETERS'},
2922bfe3f2eSlogwang            {'id': 'kmod', 'tag': 'PMD KMOD DEPENDENCIES'}
2932bfe3f2eSlogwang        ]
2942bfe3f2eSlogwang
2952bfe3f2eSlogwang        i = mystring.index("=")
2962bfe3f2eSlogwang        mystring = mystring[i + 2:]
2972bfe3f2eSlogwang        pmdinfo = json.loads(mystring)
2982bfe3f2eSlogwang
2992bfe3f2eSlogwang        if raw_output:
3002bfe3f2eSlogwang            print(json.dumps(pmdinfo))
3012bfe3f2eSlogwang            return
3022bfe3f2eSlogwang
3032bfe3f2eSlogwang        print("PMD NAME: " + pmdinfo["name"])
3042bfe3f2eSlogwang        for i in optional_pmd_info:
3052bfe3f2eSlogwang            try:
3062bfe3f2eSlogwang                print("%s: %s" % (i['tag'], pmdinfo[i['id']]))
3072bfe3f2eSlogwang            except KeyError:
3082bfe3f2eSlogwang                continue
3092bfe3f2eSlogwang
310*2d9fd380Sjfb8856606        if pmdinfo["pci_ids"]:
3112bfe3f2eSlogwang            print("PMD HW SUPPORT:")
3122bfe3f2eSlogwang            if pcidb is not None:
3132bfe3f2eSlogwang                self.pretty_print_pmdinfo(pmdinfo)
3142bfe3f2eSlogwang            else:
3152bfe3f2eSlogwang                print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
3162bfe3f2eSlogwang                for i in pmdinfo["pci_ids"]:
3172bfe3f2eSlogwang                    print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
3182bfe3f2eSlogwang                          (i[0], i[1], i[2], i[3]))
3192bfe3f2eSlogwang
3202bfe3f2eSlogwang        print("")
3212bfe3f2eSlogwang
3222bfe3f2eSlogwang    def display_pmd_info_strings(self, section_spec):
3232bfe3f2eSlogwang        """ Display a strings dump of a section. section_spec is either a
3242bfe3f2eSlogwang            section number or a name.
3252bfe3f2eSlogwang        """
3262bfe3f2eSlogwang        section = self._section_from_spec(section_spec)
3272bfe3f2eSlogwang        if section is None:
3282bfe3f2eSlogwang            return
3292bfe3f2eSlogwang
3302bfe3f2eSlogwang        data = section.data()
3312bfe3f2eSlogwang        dataptr = 0
3322bfe3f2eSlogwang
3332bfe3f2eSlogwang        while dataptr < len(data):
3342bfe3f2eSlogwang            while (dataptr < len(data) and
335*2d9fd380Sjfb8856606                   not 32 <= byte2int(data[dataptr]) <= 127):
3362bfe3f2eSlogwang                dataptr += 1
3372bfe3f2eSlogwang
3382bfe3f2eSlogwang            if dataptr >= len(data):
3392bfe3f2eSlogwang                break
3402bfe3f2eSlogwang
3412bfe3f2eSlogwang            endptr = dataptr
3422bfe3f2eSlogwang            while endptr < len(data) and byte2int(data[endptr]) != 0:
3432bfe3f2eSlogwang                endptr += 1
3442bfe3f2eSlogwang
3454418919fSjohnjiang            # pyelftools may return byte-strings, force decode them
3464418919fSjohnjiang            mystring = force_unicode(data[dataptr:endptr])
3472bfe3f2eSlogwang            rc = mystring.find("PMD_INFO_STRING")
348*2d9fd380Sjfb8856606            if rc != -1:
3490c6bd470Sfengbojiang                self.parse_pmd_info_string(mystring[rc:])
3502bfe3f2eSlogwang
3512bfe3f2eSlogwang            dataptr = endptr
3522bfe3f2eSlogwang
3532bfe3f2eSlogwang    def find_librte_eal(self, section):
3542bfe3f2eSlogwang        for tag in section.iter_tags():
3554418919fSjohnjiang            # pyelftools may return byte-strings, force decode them
3564418919fSjohnjiang            if force_unicode(tag.entry.d_tag) == 'DT_NEEDED':
3574418919fSjohnjiang                if "librte_eal" in force_unicode(tag.needed):
3584418919fSjohnjiang                    return force_unicode(tag.needed)
3592bfe3f2eSlogwang        return None
3602bfe3f2eSlogwang
3612bfe3f2eSlogwang    def search_for_autoload_path(self):
3622bfe3f2eSlogwang        scanelf = self
3632bfe3f2eSlogwang        scanfile = None
3642bfe3f2eSlogwang        library = None
3652bfe3f2eSlogwang
3662bfe3f2eSlogwang        section = self._section_from_spec(".dynamic")
3672bfe3f2eSlogwang        try:
3682bfe3f2eSlogwang            eallib = self.find_librte_eal(section)
3692bfe3f2eSlogwang            if eallib is not None:
3702bfe3f2eSlogwang                ldlibpath = os.environ.get('LD_LIBRARY_PATH')
3712bfe3f2eSlogwang                if ldlibpath is None:
3722bfe3f2eSlogwang                    ldlibpath = ""
3732bfe3f2eSlogwang                dtr = self.get_dt_runpath(section)
3742bfe3f2eSlogwang                library = search_file(eallib,
3752bfe3f2eSlogwang                                      dtr + ":" + ldlibpath +
3762bfe3f2eSlogwang                                      ":/usr/lib64:/lib64:/usr/lib:/lib")
3772bfe3f2eSlogwang                if library is None:
3782bfe3f2eSlogwang                    return (None, None)
379*2d9fd380Sjfb8856606                if not raw_output:
3802bfe3f2eSlogwang                    print("Scanning for autoload path in %s" % library)
381*2d9fd380Sjfb8856606                scanfile = open(library, 'rb')
3822bfe3f2eSlogwang                scanelf = ReadElf(scanfile, sys.stdout)
3832bfe3f2eSlogwang        except AttributeError:
3842bfe3f2eSlogwang            # Not a dynamic binary
3852bfe3f2eSlogwang            pass
3862bfe3f2eSlogwang        except ELFError:
3872bfe3f2eSlogwang            scanfile.close()
3882bfe3f2eSlogwang            return (None, None)
3892bfe3f2eSlogwang
3902bfe3f2eSlogwang        section = scanelf._section_from_spec(".rodata")
3912bfe3f2eSlogwang        if section is None:
3922bfe3f2eSlogwang            if scanfile is not None:
3932bfe3f2eSlogwang                scanfile.close()
3942bfe3f2eSlogwang            return (None, None)
3952bfe3f2eSlogwang
3962bfe3f2eSlogwang        data = section.data()
3972bfe3f2eSlogwang        dataptr = 0
3982bfe3f2eSlogwang
3992bfe3f2eSlogwang        while dataptr < len(data):
4002bfe3f2eSlogwang            while (dataptr < len(data) and
401*2d9fd380Sjfb8856606                   not 32 <= byte2int(data[dataptr]) <= 127):
4022bfe3f2eSlogwang                dataptr += 1
4032bfe3f2eSlogwang
4042bfe3f2eSlogwang            if dataptr >= len(data):
4052bfe3f2eSlogwang                break
4062bfe3f2eSlogwang
4072bfe3f2eSlogwang            endptr = dataptr
4082bfe3f2eSlogwang            while endptr < len(data) and byte2int(data[endptr]) != 0:
4092bfe3f2eSlogwang                endptr += 1
4102bfe3f2eSlogwang
4114418919fSjohnjiang            # pyelftools may return byte-strings, force decode them
4124418919fSjohnjiang            mystring = force_unicode(data[dataptr:endptr])
4132bfe3f2eSlogwang            rc = mystring.find("DPDK_PLUGIN_PATH")
414*2d9fd380Sjfb8856606            if rc != -1:
4152bfe3f2eSlogwang                rc = mystring.find("=")
4162bfe3f2eSlogwang                return (mystring[rc + 1:], library)
4172bfe3f2eSlogwang
4182bfe3f2eSlogwang            dataptr = endptr
4192bfe3f2eSlogwang        if scanfile is not None:
4202bfe3f2eSlogwang            scanfile.close()
4212bfe3f2eSlogwang        return (None, None)
4222bfe3f2eSlogwang
4232bfe3f2eSlogwang    def get_dt_runpath(self, dynsec):
4242bfe3f2eSlogwang        for tag in dynsec.iter_tags():
4254418919fSjohnjiang            # pyelftools may return byte-strings, force decode them
4264418919fSjohnjiang            if force_unicode(tag.entry.d_tag) == 'DT_RUNPATH':
4274418919fSjohnjiang                return force_unicode(tag.runpath)
4282bfe3f2eSlogwang        return ""
4292bfe3f2eSlogwang
4302bfe3f2eSlogwang    def process_dt_needed_entries(self):
4312bfe3f2eSlogwang        """ Look to see if there are any DT_NEEDED entries in the binary
4322bfe3f2eSlogwang            And process those if there are
4332bfe3f2eSlogwang        """
4342bfe3f2eSlogwang        runpath = ""
4352bfe3f2eSlogwang        ldlibpath = os.environ.get('LD_LIBRARY_PATH')
4362bfe3f2eSlogwang        if ldlibpath is None:
4372bfe3f2eSlogwang            ldlibpath = ""
4382bfe3f2eSlogwang
4392bfe3f2eSlogwang        dynsec = self._section_from_spec(".dynamic")
4402bfe3f2eSlogwang        try:
4412bfe3f2eSlogwang            runpath = self.get_dt_runpath(dynsec)
4422bfe3f2eSlogwang        except AttributeError:
4432bfe3f2eSlogwang            # dynsec is None, just return
4442bfe3f2eSlogwang            return
4452bfe3f2eSlogwang
4462bfe3f2eSlogwang        for tag in dynsec.iter_tags():
4474418919fSjohnjiang            # pyelftools may return byte-strings, force decode them
4484418919fSjohnjiang            if force_unicode(tag.entry.d_tag) == 'DT_NEEDED':
449*2d9fd380Sjfb8856606                if 'librte_' in force_unicode(tag.needed):
4504418919fSjohnjiang                    library = search_file(force_unicode(tag.needed),
4512bfe3f2eSlogwang                                          runpath + ":" + ldlibpath +
4522bfe3f2eSlogwang                                          ":/usr/lib64:/lib64:/usr/lib:/lib")
4532bfe3f2eSlogwang                    if library is not None:
454*2d9fd380Sjfb8856606                        with open(library, 'rb') as file:
4552bfe3f2eSlogwang                            try:
4562bfe3f2eSlogwang                                libelf = ReadElf(file, sys.stdout)
4572bfe3f2eSlogwang                            except ELFError:
4582bfe3f2eSlogwang                                print("%s is no an ELF file" % library)
4592bfe3f2eSlogwang                                continue
4602bfe3f2eSlogwang                            libelf.process_dt_needed_entries()
4612bfe3f2eSlogwang                            libelf.display_pmd_info_strings(".rodata")
4622bfe3f2eSlogwang                            file.close()
4632bfe3f2eSlogwang
4642bfe3f2eSlogwang
4654418919fSjohnjiang# compat: remove force_unicode & force_bytes when pyelftools<=0.23 support is
4664418919fSjohnjiang# dropped.
4674418919fSjohnjiangdef force_unicode(s):
4684418919fSjohnjiang    if hasattr(s, 'decode') and callable(s.decode):
4694418919fSjohnjiang        s = s.decode('latin-1')  # same encoding used in pyelftools py3compat
4704418919fSjohnjiang    return s
4714418919fSjohnjiang
4724418919fSjohnjiang
4734418919fSjohnjiangdef force_bytes(s):
4744418919fSjohnjiang    if hasattr(s, 'encode') and callable(s.encode):
4754418919fSjohnjiang        s = s.encode('latin-1')  # same encoding used in pyelftools py3compat
4764418919fSjohnjiang    return s
4774418919fSjohnjiang
4784418919fSjohnjiang
4792bfe3f2eSlogwangdef scan_autoload_path(autoload_path):
4802bfe3f2eSlogwang    global raw_output
4812bfe3f2eSlogwang
482*2d9fd380Sjfb8856606    if not os.path.exists(autoload_path):
4832bfe3f2eSlogwang        return
4842bfe3f2eSlogwang
4852bfe3f2eSlogwang    try:
4862bfe3f2eSlogwang        dirs = os.listdir(autoload_path)
4872bfe3f2eSlogwang    except OSError:
4882bfe3f2eSlogwang        # Couldn't read the directory, give up
4892bfe3f2eSlogwang        return
4902bfe3f2eSlogwang
4912bfe3f2eSlogwang    for d in dirs:
4922bfe3f2eSlogwang        dpath = os.path.join(autoload_path, d)
4932bfe3f2eSlogwang        if os.path.isdir(dpath):
4942bfe3f2eSlogwang            scan_autoload_path(dpath)
4952bfe3f2eSlogwang        if os.path.isfile(dpath):
4962bfe3f2eSlogwang            try:
497*2d9fd380Sjfb8856606                file = open(dpath, 'rb')
4982bfe3f2eSlogwang                readelf = ReadElf(file, sys.stdout)
4992bfe3f2eSlogwang            except ELFError:
5002bfe3f2eSlogwang                # this is likely not an elf file, skip it
5012bfe3f2eSlogwang                continue
5022bfe3f2eSlogwang            except IOError:
5032bfe3f2eSlogwang                # No permission to read the file, skip it
5042bfe3f2eSlogwang                continue
5052bfe3f2eSlogwang
506*2d9fd380Sjfb8856606            if not raw_output:
5072bfe3f2eSlogwang                print("Hw Support for library %s" % d)
5082bfe3f2eSlogwang            readelf.display_pmd_info_strings(".rodata")
5092bfe3f2eSlogwang            file.close()
5102bfe3f2eSlogwang
5112bfe3f2eSlogwang
5122bfe3f2eSlogwangdef scan_for_autoload_pmds(dpdk_path):
5132bfe3f2eSlogwang    """
5142bfe3f2eSlogwang    search the specified application or path for a pmd autoload path
5152bfe3f2eSlogwang    then scan said path for pmds and report hw support
5162bfe3f2eSlogwang    """
5172bfe3f2eSlogwang    global raw_output
5182bfe3f2eSlogwang
519*2d9fd380Sjfb8856606    if not os.path.isfile(dpdk_path):
520*2d9fd380Sjfb8856606        if not raw_output:
5212bfe3f2eSlogwang            print("Must specify a file name")
5222bfe3f2eSlogwang        return
5232bfe3f2eSlogwang
524*2d9fd380Sjfb8856606    file = open(dpdk_path, 'rb')
5252bfe3f2eSlogwang    try:
5262bfe3f2eSlogwang        readelf = ReadElf(file, sys.stdout)
5272bfe3f2eSlogwang    except ElfError:
528*2d9fd380Sjfb8856606        if not raw_output:
5292bfe3f2eSlogwang            print("Unable to parse %s" % file)
5302bfe3f2eSlogwang        return
5312bfe3f2eSlogwang
5322bfe3f2eSlogwang    (autoload_path, scannedfile) = readelf.search_for_autoload_path()
5334418919fSjohnjiang    if not autoload_path:
534*2d9fd380Sjfb8856606        if not raw_output:
5352bfe3f2eSlogwang            print("No autoload path configured in %s" % dpdk_path)
5362bfe3f2eSlogwang        return
537*2d9fd380Sjfb8856606    if not raw_output:
538*2d9fd380Sjfb8856606        if scannedfile is None:
5392bfe3f2eSlogwang            scannedfile = dpdk_path
5402bfe3f2eSlogwang        print("Found autoload path %s in %s" % (autoload_path, scannedfile))
5412bfe3f2eSlogwang
5422bfe3f2eSlogwang    file.close()
543*2d9fd380Sjfb8856606    if not raw_output:
5442bfe3f2eSlogwang        print("Discovered Autoload HW Support:")
5452bfe3f2eSlogwang    scan_autoload_path(autoload_path)
5462bfe3f2eSlogwang    return
5472bfe3f2eSlogwang
5482bfe3f2eSlogwang
5492bfe3f2eSlogwangdef main(stream=None):
5502bfe3f2eSlogwang    global raw_output
5512bfe3f2eSlogwang    global pcidb
5522bfe3f2eSlogwang
5532bfe3f2eSlogwang    pcifile_default = "./pci.ids"  # For unknown OS's assume local file
5542bfe3f2eSlogwang    if platform.system() == 'Linux':
5550c6bd470Sfengbojiang        # hwdata is the legacy location, misc is supported going forward
5560c6bd470Sfengbojiang        pcifile_default = "/usr/share/misc/pci.ids"
5570c6bd470Sfengbojiang        if not os.path.exists(pcifile_default):
5582bfe3f2eSlogwang            pcifile_default = "/usr/share/hwdata/pci.ids"
5592bfe3f2eSlogwang    elif platform.system() == 'FreeBSD':
5602bfe3f2eSlogwang        pcifile_default = "/usr/local/share/pciids/pci.ids"
5612bfe3f2eSlogwang        if not os.path.exists(pcifile_default):
5622bfe3f2eSlogwang            pcifile_default = "/usr/share/misc/pci_vendors"
5632bfe3f2eSlogwang
564*2d9fd380Sjfb8856606    parser = argparse.ArgumentParser(
565*2d9fd380Sjfb8856606        usage='usage: %(prog)s [-hrtp] [-d <pci id file>] elf_file',
566*2d9fd380Sjfb8856606        description="Dump pmd hardware support info")
567*2d9fd380Sjfb8856606    group = parser.add_mutually_exclusive_group()
568*2d9fd380Sjfb8856606    group.add_argument('-r', '--raw',
5692bfe3f2eSlogwang                       action='store_true', dest='raw_output',
570*2d9fd380Sjfb8856606                       help='dump raw json strings')
571*2d9fd380Sjfb8856606    group.add_argument("-t", "--table", dest="tblout",
572*2d9fd380Sjfb8856606                       help="output information on hw support as a hex table",
5732bfe3f2eSlogwang                       action='store_true')
574*2d9fd380Sjfb8856606    parser.add_argument("-d", "--pcidb", dest="pcifile",
575*2d9fd380Sjfb8856606                        help="specify a pci database to get vendor names from",
576*2d9fd380Sjfb8856606                        default=pcifile_default, metavar="FILE")
577*2d9fd380Sjfb8856606    parser.add_argument("-p", "--plugindir", dest="pdir",
5782bfe3f2eSlogwang                        help="scan dpdk for autoload plugins",
5792bfe3f2eSlogwang                        action='store_true')
580*2d9fd380Sjfb8856606    parser.add_argument("elf_file", help="driver shared object file")
581*2d9fd380Sjfb8856606    args = parser.parse_args()
5822bfe3f2eSlogwang
583*2d9fd380Sjfb8856606    if args.raw_output:
5842bfe3f2eSlogwang        raw_output = True
5852bfe3f2eSlogwang
586*2d9fd380Sjfb8856606    if args.tblout:
587*2d9fd380Sjfb8856606        args.pcifile = None
588*2d9fd380Sjfb8856606
589*2d9fd380Sjfb8856606    if args.pcifile:
590*2d9fd380Sjfb8856606        pcidb = PCIIds(args.pcifile)
5912bfe3f2eSlogwang        if pcidb is None:
5922bfe3f2eSlogwang            print("Pci DB file not found")
5932bfe3f2eSlogwang            exit(1)
5942bfe3f2eSlogwang
595*2d9fd380Sjfb8856606    if args.pdir:
5962bfe3f2eSlogwang        exit(scan_for_autoload_pmds(args[0]))
5972bfe3f2eSlogwang
5982bfe3f2eSlogwang    ldlibpath = os.environ.get('LD_LIBRARY_PATH')
599*2d9fd380Sjfb8856606    if ldlibpath is None:
6002bfe3f2eSlogwang        ldlibpath = ""
6012bfe3f2eSlogwang
602*2d9fd380Sjfb8856606    if os.path.exists(args.elf_file):
603*2d9fd380Sjfb8856606        myelffile = args.elf_file
6042bfe3f2eSlogwang    else:
605*2d9fd380Sjfb8856606        myelffile = search_file(args.elf_file,
606*2d9fd380Sjfb8856606                                ldlibpath + ":/usr/lib64:/lib64:/usr/lib:/lib")
6072bfe3f2eSlogwang
608*2d9fd380Sjfb8856606    if myelffile is None:
6092bfe3f2eSlogwang        print("File not found")
6102bfe3f2eSlogwang        sys.exit(1)
6112bfe3f2eSlogwang
612*2d9fd380Sjfb8856606    with open(myelffile, 'rb') as file:
6132bfe3f2eSlogwang        try:
6142bfe3f2eSlogwang            readelf = ReadElf(file, sys.stdout)
6152bfe3f2eSlogwang            readelf.process_dt_needed_entries()
6162bfe3f2eSlogwang            readelf.display_pmd_info_strings(".rodata")
6172bfe3f2eSlogwang            sys.exit(0)
6182bfe3f2eSlogwang
6192bfe3f2eSlogwang        except ELFError as ex:
6202bfe3f2eSlogwang            sys.stderr.write('ELF error: %s\n' % ex)
6212bfe3f2eSlogwang            sys.exit(1)
6222bfe3f2eSlogwang
6232bfe3f2eSlogwang
6242bfe3f2eSlogwang# -------------------------------------------------------------------------
6252bfe3f2eSlogwangif __name__ == '__main__':
6262bfe3f2eSlogwang    main()
627