2 # -------------------------------------------------------------------------
4 # Utility to dump PMD_INFO_STRING support from an object file
6 # -------------------------------------------------------------------------
12 from elftools.common.exceptions import ELFError
13 from elftools.common.py3compat import (byte2int, bytes2str, str2bytes)
14 from elftools.elf.elffile import ELFFile
15 from optparse import OptionParser
17 # For running from development directory. It should take precedence over the
18 # installed pyelftools.
19 sys.path.insert(0, '.')
24 # ===========================================
29 Class for vendors. This is the top level class
30 for the devices belong to a specific vendor.
31 self.devices is the device dictionary
32 subdevices are in each device.
35 def __init__(self, vendorStr):
37 Class initializes with the raw line from pci.ids
38 Parsing takes place inside __init__
40 self.ID = vendorStr.split()[0]
41 self.name = vendorStr.replace("%s " % self.ID, "").rstrip()
44 def addDevice(self, deviceStr):
46 Adds a device to self.devices
47 takes the raw line from pci.ids
51 if devID in self.devices:
54 self.devices[devID] = Device(deviceStr)
57 print self.ID, self.name
58 for id, dev in self.devices.items():
61 def find_device(self, devid):
62 # convert to a hex string and remove 0x
63 devid = hex(devid)[2:]
65 return self.devices[devid]
67 return Device("%s Unknown Device" % devid)
72 def __init__(self, deviceStr):
74 Class for each device.
75 Each vendor has its own devices dictionary.
78 self.ID = s.split()[0]
79 self.name = s.replace("%s " % self.ID, "")
83 print "\t%s\t%s" % (self.ID, self.name)
84 for subID, subdev in self.subdevices.items():
87 def addSubDevice(self, subDeviceStr):
89 Adds a subvendor, subdevice to device.
90 Uses raw line from pci.ids
92 s = subDeviceStr.strip()
96 subDeviceName = s.split(" ")[-1]
97 devID = "%s:%s" % (subVendorID, subDeviceID)
98 self.subdevices[devID] = SubDevice(
99 subVendorID, subDeviceID, subDeviceName)
101 def find_subid(self, subven, subdev):
102 subven = hex(subven)[2:]
103 subdev = hex(subdev)[2:]
104 devid = "%s:%s" % (subven, subdev)
107 return self.subdevices[devid]
109 if (subven == "ffff" and subdev == "ffff"):
110 return SubDevice("ffff", "ffff", "(All Subdevices)")
112 return SubDevice(subven, subdev, "(Unknown Subdevice)")
117 Class for subdevices.
120 def __init__(self, vendor, device, name):
122 Class initializes with vendorid, deviceid and name
124 self.vendorID = vendor
125 self.deviceID = device
129 print "\t\t%s\t%s\t%s" % (self.vendorID, self.deviceID, self.name)
134 Top class for all pci.ids entries.
135 All queries will be asked to this class.
136 PCIIds.vendors["0e11"].devices["0046"].\
137 subdevices["0e11:4091"].name = "Smart Array 6i"
140 def __init__(self, filename):
142 Prepares the directories.
143 Checks local data file.
144 Tries to load from local, if not found, downloads from web
150 self.readLocal(filename)
153 def reportVendors(self):
154 """Reports the vendors
156 for vid, v in self.vendors.items():
159 def report(self, vendor=None):
161 Reports everything for all vendors or a specific vendor
162 PCIIds.report() reports everything
163 PCIIDs.report("0e11") reports only "Compaq Computer Corporation"
165 if vendor is not None:
166 self.vendors[vendor].report()
168 for vID, v in self.vendors.items():
171 def find_vendor(self, vid):
172 # convert vid to a hex string and remove the 0x
176 return self.vendors[vid]
178 return Vendor("%s Unknown Vendor" % (vid))
180 def findDate(self, content):
182 if l.find("Date:") > -1:
183 return l.split()[-2].replace("-", "")
187 if len(self.contents) < 1:
188 print "data/%s-pci.ids not found" % self.date
192 for l in self.contents:
195 elif len(l.strip()) == 0:
198 if l.find("\t\t") == 0:
199 self.vendors[vendorID].devices[
200 deviceID].addSubDevice(l)
201 elif l.find("\t") == 0:
202 deviceID = l.strip().split()[0]
203 self.vendors[vendorID].addDevice(l)
205 vendorID = l.split()[0]
206 self.vendors[vendorID] = Vendor(l)
208 def readLocal(self, filename):
212 self.contents = open(filename).readlines()
213 self.date = self.findDate(self.contents)
217 Loads database from local. If there is no file,
218 it creates a new one from web
220 self.date = idsfile[0].split("/")[1].split("-")[0]
224 # =======================================
226 def search_file(filename, search_path):
227 """ Given a search path, find file with requested name """
228 for path in string.split(search_path, ":"):
229 candidate = os.path.join(path, filename)
230 if os.path.exists(candidate):
231 return os.path.abspath(candidate)
235 class ReadElf(object):
236 """ display_* methods are used to emit output into the output stream
239 def __init__(self, file, output):
241 stream object with the ELF file to read
244 output stream to write to
246 self.elffile = ELFFile(file)
249 # Lazily initialized if a debug dump is requested
250 self._dwarfinfo = None
252 self._versioninfo = None
254 def _section_from_spec(self, spec):
255 """ Retrieve a section given a "spec" (either number or name).
256 Return None if no such section exists in the file.
260 if num < self.elffile.num_sections():
261 return self.elffile.get_section(num)
265 # Not a number. Must be a name then
266 return self.elffile.get_section_by_name(str2bytes(spec))
268 def pretty_print_pmdinfo(self, pmdinfo):
271 for i in pmdinfo["pci_ids"]:
272 vendor = pcidb.find_vendor(i[0])
273 device = vendor.find_device(i[1])
274 subdev = device.find_subid(i[2], i[3])
275 print("%s (%s) : %s (%s) %s" %
276 (vendor.name, vendor.ID, device.name,
277 device.ID, subdev.name))
279 def parse_pmd_info_string(self, mystring):
283 optional_pmd_info = [
284 {'id': 'params', 'tag': 'PMD PARAMETERS'},
285 {'id': 'kmod', 'tag': 'PMD KMOD DEPENDENCIES'}
288 i = mystring.index("=")
289 mystring = mystring[i + 2:]
290 pmdinfo = json.loads(mystring)
293 print(json.dumps(pmdinfo))
296 print("PMD NAME: " + pmdinfo["name"])
297 for i in optional_pmd_info:
299 print("%s: %s" % (i['tag'], pmdinfo[i['id']]))
303 if (len(pmdinfo["pci_ids"]) != 0):
304 print("PMD HW SUPPORT:")
305 if pcidb is not None:
306 self.pretty_print_pmdinfo(pmdinfo)
308 print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
309 for i in pmdinfo["pci_ids"]:
310 print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
311 (i[0], i[1], i[2], i[3]))
315 def display_pmd_info_strings(self, section_spec):
316 """ Display a strings dump of a section. section_spec is either a
317 section number or a name.
319 section = self._section_from_spec(section_spec)
323 data = section.data()
326 while dataptr < len(data):
327 while (dataptr < len(data) and
328 not (32 <= byte2int(data[dataptr]) <= 127)):
331 if dataptr >= len(data):
335 while endptr < len(data) and byte2int(data[endptr]) != 0:
338 mystring = bytes2str(data[dataptr:endptr])
339 rc = mystring.find("PMD_INFO_STRING")
341 self.parse_pmd_info_string(mystring)
345 def find_librte_eal(self, section):
346 for tag in section.iter_tags():
347 if tag.entry.d_tag == 'DT_NEEDED':
348 if "librte_eal" in tag.needed:
352 def search_for_autoload_path(self):
357 section = self._section_from_spec(".dynamic")
359 eallib = self.find_librte_eal(section)
360 if eallib is not None:
361 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
362 if ldlibpath is None:
364 dtr = self.get_dt_runpath(section)
365 library = search_file(eallib,
366 dtr + ":" + ldlibpath +
367 ":/usr/lib64:/lib64:/usr/lib:/lib")
370 if raw_output is False:
371 print("Scanning for autoload path in %s" % library)
372 scanfile = open(library, 'rb')
373 scanelf = ReadElf(scanfile, sys.stdout)
374 except AttributeError:
375 # Not a dynamic binary
381 section = scanelf._section_from_spec(".rodata")
383 if scanfile is not None:
387 data = section.data()
390 while dataptr < len(data):
391 while (dataptr < len(data) and
392 not (32 <= byte2int(data[dataptr]) <= 127)):
395 if dataptr >= len(data):
399 while endptr < len(data) and byte2int(data[endptr]) != 0:
402 mystring = bytes2str(data[dataptr:endptr])
403 rc = mystring.find("DPDK_PLUGIN_PATH")
405 rc = mystring.find("=")
406 return (mystring[rc + 1:], library)
409 if scanfile is not None:
413 def get_dt_runpath(self, dynsec):
414 for tag in dynsec.iter_tags():
415 if tag.entry.d_tag == 'DT_RUNPATH':
419 def process_dt_needed_entries(self):
420 """ Look to see if there are any DT_NEEDED entries in the binary
421 And process those if there are
425 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
426 if ldlibpath is None:
429 dynsec = self._section_from_spec(".dynamic")
431 runpath = self.get_dt_runpath(dynsec)
432 except AttributeError:
433 # dynsec is None, just return
436 for tag in dynsec.iter_tags():
437 if tag.entry.d_tag == 'DT_NEEDED':
438 rc = tag.needed.find("librte_pmd")
440 library = search_file(tag.needed,
441 runpath + ":" + ldlibpath +
442 ":/usr/lib64:/lib64:/usr/lib:/lib")
443 if library is not None:
444 if raw_output is False:
445 print("Scanning %s for pmd information" % library)
446 with open(library, 'rb') as file:
448 libelf = ReadElf(file, sys.stdout)
450 print("%s is no an ELF file" % library)
452 libelf.process_dt_needed_entries()
453 libelf.display_pmd_info_strings(".rodata")
457 def scan_autoload_path(autoload_path):
460 if os.path.exists(autoload_path) is False:
464 dirs = os.listdir(autoload_path)
466 # Couldn't read the directory, give up
470 dpath = os.path.join(autoload_path, d)
471 if os.path.isdir(dpath):
472 scan_autoload_path(dpath)
473 if os.path.isfile(dpath):
475 file = open(dpath, 'rb')
476 readelf = ReadElf(file, sys.stdout)
478 # this is likely not an elf file, skip it
481 # No permission to read the file, skip it
484 if raw_output is False:
485 print("Hw Support for library %s" % d)
486 readelf.display_pmd_info_strings(".rodata")
490 def scan_for_autoload_pmds(dpdk_path):
492 search the specified application or path for a pmd autoload path
493 then scan said path for pmds and report hw support
497 if (os.path.isfile(dpdk_path) is False):
498 if raw_output is False:
499 print("Must specify a file name")
502 file = open(dpdk_path, 'rb')
504 readelf = ReadElf(file, sys.stdout)
506 if raw_output is False:
507 print("Unable to parse %s" % file)
510 (autoload_path, scannedfile) = readelf.search_for_autoload_path()
511 if (autoload_path is None or autoload_path is ""):
512 if (raw_output is False):
513 print("No autoload path configured in %s" % dpdk_path)
515 if (raw_output is False):
516 if (scannedfile is None):
517 scannedfile = dpdk_path
518 print("Found autoload path %s in %s" % (autoload_path, scannedfile))
521 if (raw_output is False):
522 print("Discovered Autoload HW Support:")
523 scan_autoload_path(autoload_path)
527 def main(stream=None):
531 pcifile_default = "./pci.ids" # For unknown OS's assume local file
532 if platform.system() == 'Linux':
533 pcifile_default = "/usr/share/hwdata/pci.ids"
534 elif platform.system() == 'FreeBSD':
535 pcifile_default = "/usr/local/share/pciids/pci.ids"
536 if not os.path.exists(pcifile_default):
537 pcifile_default = "/usr/share/misc/pci_vendors"
539 optparser = OptionParser(
540 usage='usage: %prog [-hrtp] [-d <pci id file] <elf-file>',
541 description="Dump pmd hardware support info",
542 add_help_option=True)
543 optparser.add_option('-r', '--raw',
544 action='store_true', dest='raw_output',
545 help='Dump raw json strings')
546 optparser.add_option("-d", "--pcidb", dest="pcifile",
547 help="specify a pci database "
548 "to get vendor names from",
549 default=pcifile_default, metavar="FILE")
550 optparser.add_option("-t", "--table", dest="tblout",
551 help="output information on hw support as a "
554 optparser.add_option("-p", "--plugindir", dest="pdir",
555 help="scan dpdk for autoload plugins",
558 options, args = optparser.parse_args()
560 if options.raw_output:
564 pcidb = PCIIds(options.pcifile)
566 print("Pci DB file not found")
570 options.pcifile = None
574 optparser.print_usage()
577 if options.pdir is True:
578 exit(scan_for_autoload_pmds(args[0]))
580 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
581 if (ldlibpath is None):
584 if (os.path.exists(args[0]) is True):
587 myelffile = search_file(
588 args[0], ldlibpath + ":/usr/lib64:/lib64:/usr/lib:/lib")
590 if (myelffile is None):
591 print("File not found")
594 with open(myelffile, 'rb') as file:
596 readelf = ReadElf(file, sys.stdout)
597 readelf.process_dt_needed_entries()
598 readelf.display_pmd_info_strings(".rodata")
601 except ELFError as ex:
602 sys.stderr.write('ELF error: %s\n' % ex)
606 # -------------------------------------------------------------------------
607 if __name__ == '__main__':