xref: /dpdk/buildtools/pmdinfogen.py (revision e1d8a879abac499a66801bb8238d59ba52f4fc6b)
16c4bf8f4SDmitry Kozlyuk#!/usr/bin/env python3
26c4bf8f4SDmitry Kozlyuk# SPDX-License-Identifier: BSD-3-Clause
36c4bf8f4SDmitry Kozlyuk# Copyright (c) 2016 Neil Horman <nhorman@tuxdriver.com>
46c4bf8f4SDmitry Kozlyuk# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
56c4bf8f4SDmitry Kozlyuk
66c4bf8f4SDmitry Kozlyukimport argparse
76c4bf8f4SDmitry Kozlyukimport ctypes
86c4bf8f4SDmitry Kozlyukimport json
9*e1d8a879SDavid Marchandimport re
106c4bf8f4SDmitry Kozlyukimport sys
116c4bf8f4SDmitry Kozlyukimport tempfile
126c4bf8f4SDmitry Kozlyuk
135031436fSDmitry Kozlyuktry:
1465ef14c5SDmitry Kozlyuk    import elftools
156c4bf8f4SDmitry Kozlyuk    from elftools.elf.elffile import ELFFile
166c4bf8f4SDmitry Kozlyuk    from elftools.elf.sections import SymbolTableSection
175031436fSDmitry Kozlyukexcept ImportError:
185031436fSDmitry Kozlyuk    pass
195031436fSDmitry Kozlyuk
205031436fSDmitry Kozlyukimport coff
216c4bf8f4SDmitry Kozlyuk
226c4bf8f4SDmitry Kozlyuk
236c4bf8f4SDmitry Kozlyukclass ELFSymbol:
246c4bf8f4SDmitry Kozlyuk    def __init__(self, image, symbol):
256c4bf8f4SDmitry Kozlyuk        self._image = image
266c4bf8f4SDmitry Kozlyuk        self._symbol = symbol
276c4bf8f4SDmitry Kozlyuk
286c4bf8f4SDmitry Kozlyuk    @property
296c4bf8f4SDmitry Kozlyuk    def string_value(self):
305031436fSDmitry Kozlyuk        size = self._symbol["st_size"]
315031436fSDmitry Kozlyuk        value = self.get_value(0, size)
328050b615SDmitry Kozlyuk        return coff.decode_asciiz(value)  # not COFF-specific
336c4bf8f4SDmitry Kozlyuk
345031436fSDmitry Kozlyuk    def get_value(self, offset, size):
355031436fSDmitry Kozlyuk        section = self._symbol["st_shndx"]
365031436fSDmitry Kozlyuk        data = self._image.get_section(section).data()
375031436fSDmitry Kozlyuk        base = self._symbol["st_value"] + offset
385031436fSDmitry Kozlyuk        return data[base : base + size]
395031436fSDmitry Kozlyuk
406c4bf8f4SDmitry Kozlyuk
416c4bf8f4SDmitry Kozlyukclass ELFImage:
426c4bf8f4SDmitry Kozlyuk    def __init__(self, data):
4365ef14c5SDmitry Kozlyuk        version = tuple(int(c) for c in elftools.__version__.split("."))
4465ef14c5SDmitry Kozlyuk        self._legacy_elftools = version < (0, 24)
4565ef14c5SDmitry Kozlyuk
466c4bf8f4SDmitry Kozlyuk        self._image = ELFFile(data)
4765ef14c5SDmitry Kozlyuk
4865ef14c5SDmitry Kozlyuk        section = b".symtab" if self._legacy_elftools else ".symtab"
4965ef14c5SDmitry Kozlyuk        self._symtab = self._image.get_section_by_name(section)
506c4bf8f4SDmitry Kozlyuk        if not isinstance(self._symtab, SymbolTableSection):
516c4bf8f4SDmitry Kozlyuk            raise Exception(".symtab section is not a symbol table")
526c4bf8f4SDmitry Kozlyuk
536c4bf8f4SDmitry Kozlyuk    @property
546c4bf8f4SDmitry Kozlyuk    def is_big_endian(self):
556c4bf8f4SDmitry Kozlyuk        return not self._image.little_endian
566c4bf8f4SDmitry Kozlyuk
576c4bf8f4SDmitry Kozlyuk    def find_by_name(self, name):
5865ef14c5SDmitry Kozlyuk        symbol = self._get_symbol_by_name(name)
595031436fSDmitry Kozlyuk        return ELFSymbol(self._image, symbol[0]) if symbol else None
606c4bf8f4SDmitry Kozlyuk
6165ef14c5SDmitry Kozlyuk    def _get_symbol_by_name(self, name):
6265ef14c5SDmitry Kozlyuk        if not self._legacy_elftools:
6365ef14c5SDmitry Kozlyuk            return self._symtab.get_symbol_by_name(name)
6465ef14c5SDmitry Kozlyuk        name = name.encode("utf-8")
6565ef14c5SDmitry Kozlyuk        for symbol in self._symtab.iter_symbols():
6665ef14c5SDmitry Kozlyuk            if symbol.name == name:
6765ef14c5SDmitry Kozlyuk                return [symbol]
6865ef14c5SDmitry Kozlyuk        return None
6965ef14c5SDmitry Kozlyuk
70*e1d8a879SDavid Marchand    def find_by_pattern(self, pattern):
71*e1d8a879SDavid Marchand        pattern = pattern.encode("utf-8") if self._legacy_elftools else pattern
726c4bf8f4SDmitry Kozlyuk        for i in range(self._symtab.num_symbols()):
736c4bf8f4SDmitry Kozlyuk            symbol = self._symtab.get_symbol(i)
74*e1d8a879SDavid Marchand            if re.match(pattern, symbol.name):
755031436fSDmitry Kozlyuk                yield ELFSymbol(self._image, symbol)
765031436fSDmitry Kozlyuk
775031436fSDmitry Kozlyuk
785031436fSDmitry Kozlyukclass COFFSymbol:
795031436fSDmitry Kozlyuk    def __init__(self, image, symbol):
805031436fSDmitry Kozlyuk        self._image = image
815031436fSDmitry Kozlyuk        self._symbol = symbol
825031436fSDmitry Kozlyuk
835031436fSDmitry Kozlyuk    def get_value(self, offset, size):
845031436fSDmitry Kozlyuk        value = self._symbol.get_value(offset)
855031436fSDmitry Kozlyuk        return value[:size] if value else value
865031436fSDmitry Kozlyuk
875031436fSDmitry Kozlyuk    @property
885031436fSDmitry Kozlyuk    def string_value(self):
895031436fSDmitry Kozlyuk        value = self._symbol.get_value(0)
905031436fSDmitry Kozlyuk        return coff.decode_asciiz(value) if value else ''
915031436fSDmitry Kozlyuk
925031436fSDmitry Kozlyuk
935031436fSDmitry Kozlyukclass COFFImage:
945031436fSDmitry Kozlyuk    def __init__(self, data):
955031436fSDmitry Kozlyuk        self._image = coff.Image(data)
965031436fSDmitry Kozlyuk
975031436fSDmitry Kozlyuk    @property
985031436fSDmitry Kozlyuk    def is_big_endian(self):
995031436fSDmitry Kozlyuk        return False
1005031436fSDmitry Kozlyuk
101*e1d8a879SDavid Marchand    def find_by_pattern(self, pattern):
1025031436fSDmitry Kozlyuk        for symbol in self._image.symbols:
103*e1d8a879SDavid Marchand            if re.match(pattern, symbol.name):
1045031436fSDmitry Kozlyuk                yield COFFSymbol(self._image, symbol)
1055031436fSDmitry Kozlyuk
1065031436fSDmitry Kozlyuk    def find_by_name(self, name):
1075031436fSDmitry Kozlyuk        for symbol in self._image.symbols:
1085031436fSDmitry Kozlyuk            if symbol.name == name:
1095031436fSDmitry Kozlyuk                return COFFSymbol(self._image, symbol)
1105031436fSDmitry Kozlyuk        return None
1116c4bf8f4SDmitry Kozlyuk
1126c4bf8f4SDmitry Kozlyuk
1136c4bf8f4SDmitry Kozlyukdef define_rte_pci_id(is_big_endian):
1146c4bf8f4SDmitry Kozlyuk    base_type = ctypes.LittleEndianStructure
1156c4bf8f4SDmitry Kozlyuk    if is_big_endian:
1166c4bf8f4SDmitry Kozlyuk        base_type = ctypes.BigEndianStructure
1176c4bf8f4SDmitry Kozlyuk
1186c4bf8f4SDmitry Kozlyuk    class rte_pci_id(base_type):
1196c4bf8f4SDmitry Kozlyuk        _pack_ = True
1206c4bf8f4SDmitry Kozlyuk        _fields_ = [
1216c4bf8f4SDmitry Kozlyuk            ("class_id", ctypes.c_uint32),
1226c4bf8f4SDmitry Kozlyuk            ("vendor_id", ctypes.c_uint16),
1236c4bf8f4SDmitry Kozlyuk            ("device_id", ctypes.c_uint16),
1246c4bf8f4SDmitry Kozlyuk            ("subsystem_vendor_id", ctypes.c_uint16),
1256c4bf8f4SDmitry Kozlyuk            ("subsystem_device_id", ctypes.c_uint16),
1266c4bf8f4SDmitry Kozlyuk        ]
1276c4bf8f4SDmitry Kozlyuk
1286c4bf8f4SDmitry Kozlyuk    return rte_pci_id
1296c4bf8f4SDmitry Kozlyuk
1306c4bf8f4SDmitry Kozlyuk
1316c4bf8f4SDmitry Kozlyukclass Driver:
1326c4bf8f4SDmitry Kozlyuk    OPTIONS = [
1336c4bf8f4SDmitry Kozlyuk        ("params", "_param_string_export"),
1346c4bf8f4SDmitry Kozlyuk        ("kmod", "_kmod_dep_export"),
1356c4bf8f4SDmitry Kozlyuk    ]
1366c4bf8f4SDmitry Kozlyuk
1376c4bf8f4SDmitry Kozlyuk    def __init__(self, name, options):
1386c4bf8f4SDmitry Kozlyuk        self.name = name
1396c4bf8f4SDmitry Kozlyuk        for key, value in options.items():
1406c4bf8f4SDmitry Kozlyuk            setattr(self, key, value)
1416c4bf8f4SDmitry Kozlyuk        self.pci_ids = []
1426c4bf8f4SDmitry Kozlyuk
1436c4bf8f4SDmitry Kozlyuk    @classmethod
1446c4bf8f4SDmitry Kozlyuk    def load(cls, image, symbol):
1456c4bf8f4SDmitry Kozlyuk        name = symbol.string_value
1466c4bf8f4SDmitry Kozlyuk
1476c4bf8f4SDmitry Kozlyuk        options = {}
1486c4bf8f4SDmitry Kozlyuk        for key, suffix in cls.OPTIONS:
1496c4bf8f4SDmitry Kozlyuk            option_symbol = image.find_by_name("__%s%s" % (name, suffix))
1506c4bf8f4SDmitry Kozlyuk            if option_symbol:
1516c4bf8f4SDmitry Kozlyuk                value = option_symbol.string_value
1526c4bf8f4SDmitry Kozlyuk                options[key] = value
1536c4bf8f4SDmitry Kozlyuk
1546c4bf8f4SDmitry Kozlyuk        driver = cls(name, options)
1556c4bf8f4SDmitry Kozlyuk
1566c4bf8f4SDmitry Kozlyuk        pci_table_name_symbol = image.find_by_name("__%s_pci_tbl_export" % name)
1576c4bf8f4SDmitry Kozlyuk        if pci_table_name_symbol:
1586c4bf8f4SDmitry Kozlyuk            driver.pci_ids = cls._load_pci_ids(image, pci_table_name_symbol)
1596c4bf8f4SDmitry Kozlyuk
1606c4bf8f4SDmitry Kozlyuk        return driver
1616c4bf8f4SDmitry Kozlyuk
1626c4bf8f4SDmitry Kozlyuk    @staticmethod
1636c4bf8f4SDmitry Kozlyuk    def _load_pci_ids(image, table_name_symbol):
1646c4bf8f4SDmitry Kozlyuk        table_name = table_name_symbol.string_value
1656c4bf8f4SDmitry Kozlyuk        table_symbol = image.find_by_name(table_name)
1666c4bf8f4SDmitry Kozlyuk        if not table_symbol:
1676c4bf8f4SDmitry Kozlyuk            raise Exception("PCI table declared but not defined: %d" % table_name)
1686c4bf8f4SDmitry Kozlyuk
1696c4bf8f4SDmitry Kozlyuk        rte_pci_id = define_rte_pci_id(image.is_big_endian)
1706c4bf8f4SDmitry Kozlyuk
1716c4bf8f4SDmitry Kozlyuk        result = []
1725031436fSDmitry Kozlyuk        while True:
1735031436fSDmitry Kozlyuk            size = ctypes.sizeof(rte_pci_id)
1745031436fSDmitry Kozlyuk            offset = size * len(result)
1755031436fSDmitry Kozlyuk            data = table_symbol.get_value(offset, size)
1765031436fSDmitry Kozlyuk            if not data:
1775031436fSDmitry Kozlyuk                break
1785031436fSDmitry Kozlyuk            pci_id = rte_pci_id.from_buffer_copy(data)
1796c4bf8f4SDmitry Kozlyuk            if not pci_id.device_id:
1806c4bf8f4SDmitry Kozlyuk                break
1815031436fSDmitry Kozlyuk            result.append(
1825031436fSDmitry Kozlyuk                [
1836c4bf8f4SDmitry Kozlyuk                    pci_id.vendor_id,
1846c4bf8f4SDmitry Kozlyuk                    pci_id.device_id,
1856c4bf8f4SDmitry Kozlyuk                    pci_id.subsystem_vendor_id,
1866c4bf8f4SDmitry Kozlyuk                    pci_id.subsystem_device_id,
1875031436fSDmitry Kozlyuk                ]
1885031436fSDmitry Kozlyuk            )
1896c4bf8f4SDmitry Kozlyuk        return result
1906c4bf8f4SDmitry Kozlyuk
1916c4bf8f4SDmitry Kozlyuk    def dump(self, file):
1926c4bf8f4SDmitry Kozlyuk        dumped = json.dumps(self.__dict__)
1936c4bf8f4SDmitry Kozlyuk        escaped = dumped.replace('"', '\\"')
1946c4bf8f4SDmitry Kozlyuk        print(
1956c4bf8f4SDmitry Kozlyuk            'const char %s_pmd_info[] __attribute__((used)) = "PMD_INFO_STRING= %s";'
1966c4bf8f4SDmitry Kozlyuk            % (self.name, escaped),
1976c4bf8f4SDmitry Kozlyuk            file=file,
1986c4bf8f4SDmitry Kozlyuk        )
1996c4bf8f4SDmitry Kozlyuk
2006c4bf8f4SDmitry Kozlyuk
2016c4bf8f4SDmitry Kozlyukdef load_drivers(image):
2026c4bf8f4SDmitry Kozlyuk    drivers = []
203*e1d8a879SDavid Marchand    for symbol in image.find_by_pattern("^this_pmd_name[0-9]+$"):
2046c4bf8f4SDmitry Kozlyuk        drivers.append(Driver.load(image, symbol))
2056c4bf8f4SDmitry Kozlyuk    return drivers
2066c4bf8f4SDmitry Kozlyuk
2076c4bf8f4SDmitry Kozlyuk
2086c4bf8f4SDmitry Kozlyukdef dump_drivers(drivers, file):
2096c4bf8f4SDmitry Kozlyuk    # Keep legacy order of definitions.
2106c4bf8f4SDmitry Kozlyuk    for driver in reversed(drivers):
2116c4bf8f4SDmitry Kozlyuk        driver.dump(file)
2126c4bf8f4SDmitry Kozlyuk
2136c4bf8f4SDmitry Kozlyuk
2146c4bf8f4SDmitry Kozlyukdef parse_args():
2156c4bf8f4SDmitry Kozlyuk    parser = argparse.ArgumentParser()
2165031436fSDmitry Kozlyuk    parser.add_argument("format", help="object file format, 'elf' or 'coff'")
2170fe5c4e5SDmitry Kozlyuk    parser.add_argument(
2180fe5c4e5SDmitry Kozlyuk        "input", nargs='+', help="input object file path or '-' for stdin"
2190fe5c4e5SDmitry Kozlyuk    )
2206c4bf8f4SDmitry Kozlyuk    parser.add_argument("output", help="output C file path or '-' for stdout")
2216c4bf8f4SDmitry Kozlyuk    return parser.parse_args()
2226c4bf8f4SDmitry Kozlyuk
2236c4bf8f4SDmitry Kozlyuk
2246c4bf8f4SDmitry Kozlyukdef open_input(path):
2256c4bf8f4SDmitry Kozlyuk    if path == "-":
2266c4bf8f4SDmitry Kozlyuk        temp = tempfile.TemporaryFile()
2276c4bf8f4SDmitry Kozlyuk        temp.write(sys.stdin.buffer.read())
2286c4bf8f4SDmitry Kozlyuk        return temp
2296c4bf8f4SDmitry Kozlyuk    return open(path, "rb")
2306c4bf8f4SDmitry Kozlyuk
2316c4bf8f4SDmitry Kozlyuk
2325031436fSDmitry Kozlyukdef read_input(path):
2335031436fSDmitry Kozlyuk    if path == "-":
2345031436fSDmitry Kozlyuk        return sys.stdin.buffer.read()
2355031436fSDmitry Kozlyuk    with open(path, "rb") as file:
2365031436fSDmitry Kozlyuk        return file.read()
2375031436fSDmitry Kozlyuk
2385031436fSDmitry Kozlyuk
2395031436fSDmitry Kozlyukdef load_image(fmt, path):
2405031436fSDmitry Kozlyuk    if fmt == "elf":
2415031436fSDmitry Kozlyuk        return ELFImage(open_input(path))
2425031436fSDmitry Kozlyuk    if fmt == "coff":
2435031436fSDmitry Kozlyuk        return COFFImage(read_input(path))
2445031436fSDmitry Kozlyuk    raise Exception("unsupported object file format")
2455031436fSDmitry Kozlyuk
2465031436fSDmitry Kozlyuk
2476c4bf8f4SDmitry Kozlyukdef open_output(path):
2486c4bf8f4SDmitry Kozlyuk    if path == "-":
2496c4bf8f4SDmitry Kozlyuk        return sys.stdout
2506c4bf8f4SDmitry Kozlyuk    return open(path, "w")
2516c4bf8f4SDmitry Kozlyuk
2526c4bf8f4SDmitry Kozlyuk
253e6e9730cSDmitry Kozlyukdef write_header(output):
254e6e9730cSDmitry Kozlyuk    output.write(
255e6e9730cSDmitry Kozlyuk        "static __attribute__((unused)) const char *generator = \"%s\";\n" % sys.argv[0]
256e6e9730cSDmitry Kozlyuk    )
257e6e9730cSDmitry Kozlyuk
258e6e9730cSDmitry Kozlyuk
2596c4bf8f4SDmitry Kozlyukdef main():
2606c4bf8f4SDmitry Kozlyuk    args = parse_args()
2610fe5c4e5SDmitry Kozlyuk    if args.input.count('-') > 1:
2620fe5c4e5SDmitry Kozlyuk        raise Exception("'-' input cannot be used multiple times")
2635031436fSDmitry Kozlyuk    if args.format == "elf" and "ELFFile" not in globals():
2645031436fSDmitry Kozlyuk        raise Exception("elftools module not found")
2655031436fSDmitry Kozlyuk
2666c4bf8f4SDmitry Kozlyuk    output = open_output(args.output)
267e6e9730cSDmitry Kozlyuk    write_header(output)
2680fe5c4e5SDmitry Kozlyuk    for path in args.input:
2690fe5c4e5SDmitry Kozlyuk        image = load_image(args.format, path)
2700fe5c4e5SDmitry Kozlyuk        drivers = load_drivers(image)
2716c4bf8f4SDmitry Kozlyuk        dump_drivers(drivers, output)
2726c4bf8f4SDmitry Kozlyuk
2736c4bf8f4SDmitry Kozlyuk
2746c4bf8f4SDmitry Kozlyukif __name__ == "__main__":
2756c4bf8f4SDmitry Kozlyuk    main()
276