xref: /dpdk/buildtools/pmdinfogen.py (revision 65ef14c576bccf74acb9ee7da11e8ea98de8fabc)
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
96c4bf8f4SDmitry Kozlyukimport sys
106c4bf8f4SDmitry Kozlyukimport tempfile
116c4bf8f4SDmitry Kozlyuk
125031436fSDmitry Kozlyuktry:
13*65ef14c5SDmitry Kozlyuk    import elftools
146c4bf8f4SDmitry Kozlyuk    from elftools.elf.elffile import ELFFile
156c4bf8f4SDmitry Kozlyuk    from elftools.elf.sections import SymbolTableSection
165031436fSDmitry Kozlyukexcept ImportError:
175031436fSDmitry Kozlyuk    pass
185031436fSDmitry Kozlyuk
195031436fSDmitry Kozlyukimport coff
206c4bf8f4SDmitry Kozlyuk
216c4bf8f4SDmitry Kozlyuk
226c4bf8f4SDmitry Kozlyukclass ELFSymbol:
236c4bf8f4SDmitry Kozlyuk    def __init__(self, image, symbol):
246c4bf8f4SDmitry Kozlyuk        self._image = image
256c4bf8f4SDmitry Kozlyuk        self._symbol = symbol
266c4bf8f4SDmitry Kozlyuk
276c4bf8f4SDmitry Kozlyuk    @property
286c4bf8f4SDmitry Kozlyuk    def string_value(self):
295031436fSDmitry Kozlyuk        size = self._symbol["st_size"]
305031436fSDmitry Kozlyuk        value = self.get_value(0, size)
316c4bf8f4SDmitry Kozlyuk        return value[:-1].decode() if value else ""
326c4bf8f4SDmitry Kozlyuk
335031436fSDmitry Kozlyuk    def get_value(self, offset, size):
345031436fSDmitry Kozlyuk        section = self._symbol["st_shndx"]
355031436fSDmitry Kozlyuk        data = self._image.get_section(section).data()
365031436fSDmitry Kozlyuk        base = self._symbol["st_value"] + offset
375031436fSDmitry Kozlyuk        return data[base : base + size]
385031436fSDmitry Kozlyuk
396c4bf8f4SDmitry Kozlyuk
406c4bf8f4SDmitry Kozlyukclass ELFImage:
416c4bf8f4SDmitry Kozlyuk    def __init__(self, data):
42*65ef14c5SDmitry Kozlyuk        version = tuple(int(c) for c in elftools.__version__.split("."))
43*65ef14c5SDmitry Kozlyuk        self._legacy_elftools = version < (0, 24)
44*65ef14c5SDmitry Kozlyuk
456c4bf8f4SDmitry Kozlyuk        self._image = ELFFile(data)
46*65ef14c5SDmitry Kozlyuk
47*65ef14c5SDmitry Kozlyuk        section = b".symtab" if self._legacy_elftools else ".symtab"
48*65ef14c5SDmitry Kozlyuk        self._symtab = self._image.get_section_by_name(section)
496c4bf8f4SDmitry Kozlyuk        if not isinstance(self._symtab, SymbolTableSection):
506c4bf8f4SDmitry Kozlyuk            raise Exception(".symtab section is not a symbol table")
516c4bf8f4SDmitry Kozlyuk
526c4bf8f4SDmitry Kozlyuk    @property
536c4bf8f4SDmitry Kozlyuk    def is_big_endian(self):
546c4bf8f4SDmitry Kozlyuk        return not self._image.little_endian
556c4bf8f4SDmitry Kozlyuk
566c4bf8f4SDmitry Kozlyuk    def find_by_name(self, name):
57*65ef14c5SDmitry Kozlyuk        symbol = self._get_symbol_by_name(name)
585031436fSDmitry Kozlyuk        return ELFSymbol(self._image, symbol[0]) if symbol else None
596c4bf8f4SDmitry Kozlyuk
60*65ef14c5SDmitry Kozlyuk    def _get_symbol_by_name(self, name):
61*65ef14c5SDmitry Kozlyuk        if not self._legacy_elftools:
62*65ef14c5SDmitry Kozlyuk            return self._symtab.get_symbol_by_name(name)
63*65ef14c5SDmitry Kozlyuk        name = name.encode("utf-8")
64*65ef14c5SDmitry Kozlyuk        for symbol in self._symtab.iter_symbols():
65*65ef14c5SDmitry Kozlyuk            if symbol.name == name:
66*65ef14c5SDmitry Kozlyuk                return [symbol]
67*65ef14c5SDmitry Kozlyuk        return None
68*65ef14c5SDmitry Kozlyuk
696c4bf8f4SDmitry Kozlyuk    def find_by_prefix(self, prefix):
70*65ef14c5SDmitry Kozlyuk        prefix = prefix.encode("utf-8") if self._legacy_elftools else prefix
716c4bf8f4SDmitry Kozlyuk        for i in range(self._symtab.num_symbols()):
726c4bf8f4SDmitry Kozlyuk            symbol = self._symtab.get_symbol(i)
736c4bf8f4SDmitry Kozlyuk            if symbol.name.startswith(prefix):
745031436fSDmitry Kozlyuk                yield ELFSymbol(self._image, symbol)
755031436fSDmitry Kozlyuk
765031436fSDmitry Kozlyuk
775031436fSDmitry Kozlyukclass COFFSymbol:
785031436fSDmitry Kozlyuk    def __init__(self, image, symbol):
795031436fSDmitry Kozlyuk        self._image = image
805031436fSDmitry Kozlyuk        self._symbol = symbol
815031436fSDmitry Kozlyuk
825031436fSDmitry Kozlyuk    def get_value(self, offset, size):
835031436fSDmitry Kozlyuk        value = self._symbol.get_value(offset)
845031436fSDmitry Kozlyuk        return value[:size] if value else value
855031436fSDmitry Kozlyuk
865031436fSDmitry Kozlyuk    @property
875031436fSDmitry Kozlyuk    def string_value(self):
885031436fSDmitry Kozlyuk        value = self._symbol.get_value(0)
895031436fSDmitry Kozlyuk        return coff.decode_asciiz(value) if value else ''
905031436fSDmitry Kozlyuk
915031436fSDmitry Kozlyuk
925031436fSDmitry Kozlyukclass COFFImage:
935031436fSDmitry Kozlyuk    def __init__(self, data):
945031436fSDmitry Kozlyuk        self._image = coff.Image(data)
955031436fSDmitry Kozlyuk
965031436fSDmitry Kozlyuk    @property
975031436fSDmitry Kozlyuk    def is_big_endian(self):
985031436fSDmitry Kozlyuk        return False
995031436fSDmitry Kozlyuk
1005031436fSDmitry Kozlyuk    def find_by_prefix(self, prefix):
1015031436fSDmitry Kozlyuk        for symbol in self._image.symbols:
1025031436fSDmitry Kozlyuk            if symbol.name.startswith(prefix):
1035031436fSDmitry Kozlyuk                yield COFFSymbol(self._image, symbol)
1045031436fSDmitry Kozlyuk
1055031436fSDmitry Kozlyuk    def find_by_name(self, name):
1065031436fSDmitry Kozlyuk        for symbol in self._image.symbols:
1075031436fSDmitry Kozlyuk            if symbol.name == name:
1085031436fSDmitry Kozlyuk                return COFFSymbol(self._image, symbol)
1095031436fSDmitry Kozlyuk        return None
1106c4bf8f4SDmitry Kozlyuk
1116c4bf8f4SDmitry Kozlyuk
1126c4bf8f4SDmitry Kozlyukdef define_rte_pci_id(is_big_endian):
1136c4bf8f4SDmitry Kozlyuk    base_type = ctypes.LittleEndianStructure
1146c4bf8f4SDmitry Kozlyuk    if is_big_endian:
1156c4bf8f4SDmitry Kozlyuk        base_type = ctypes.BigEndianStructure
1166c4bf8f4SDmitry Kozlyuk
1176c4bf8f4SDmitry Kozlyuk    class rte_pci_id(base_type):
1186c4bf8f4SDmitry Kozlyuk        _pack_ = True
1196c4bf8f4SDmitry Kozlyuk        _fields_ = [
1206c4bf8f4SDmitry Kozlyuk            ("class_id", ctypes.c_uint32),
1216c4bf8f4SDmitry Kozlyuk            ("vendor_id", ctypes.c_uint16),
1226c4bf8f4SDmitry Kozlyuk            ("device_id", ctypes.c_uint16),
1236c4bf8f4SDmitry Kozlyuk            ("subsystem_vendor_id", ctypes.c_uint16),
1246c4bf8f4SDmitry Kozlyuk            ("subsystem_device_id", ctypes.c_uint16),
1256c4bf8f4SDmitry Kozlyuk        ]
1266c4bf8f4SDmitry Kozlyuk
1276c4bf8f4SDmitry Kozlyuk    return rte_pci_id
1286c4bf8f4SDmitry Kozlyuk
1296c4bf8f4SDmitry Kozlyuk
1306c4bf8f4SDmitry Kozlyukclass Driver:
1316c4bf8f4SDmitry Kozlyuk    OPTIONS = [
1326c4bf8f4SDmitry Kozlyuk        ("params", "_param_string_export"),
1336c4bf8f4SDmitry Kozlyuk        ("kmod", "_kmod_dep_export"),
1346c4bf8f4SDmitry Kozlyuk    ]
1356c4bf8f4SDmitry Kozlyuk
1366c4bf8f4SDmitry Kozlyuk    def __init__(self, name, options):
1376c4bf8f4SDmitry Kozlyuk        self.name = name
1386c4bf8f4SDmitry Kozlyuk        for key, value in options.items():
1396c4bf8f4SDmitry Kozlyuk            setattr(self, key, value)
1406c4bf8f4SDmitry Kozlyuk        self.pci_ids = []
1416c4bf8f4SDmitry Kozlyuk
1426c4bf8f4SDmitry Kozlyuk    @classmethod
1436c4bf8f4SDmitry Kozlyuk    def load(cls, image, symbol):
1446c4bf8f4SDmitry Kozlyuk        name = symbol.string_value
1456c4bf8f4SDmitry Kozlyuk
1466c4bf8f4SDmitry Kozlyuk        options = {}
1476c4bf8f4SDmitry Kozlyuk        for key, suffix in cls.OPTIONS:
1486c4bf8f4SDmitry Kozlyuk            option_symbol = image.find_by_name("__%s%s" % (name, suffix))
1496c4bf8f4SDmitry Kozlyuk            if option_symbol:
1506c4bf8f4SDmitry Kozlyuk                value = option_symbol.string_value
1516c4bf8f4SDmitry Kozlyuk                options[key] = value
1526c4bf8f4SDmitry Kozlyuk
1536c4bf8f4SDmitry Kozlyuk        driver = cls(name, options)
1546c4bf8f4SDmitry Kozlyuk
1556c4bf8f4SDmitry Kozlyuk        pci_table_name_symbol = image.find_by_name("__%s_pci_tbl_export" % name)
1566c4bf8f4SDmitry Kozlyuk        if pci_table_name_symbol:
1576c4bf8f4SDmitry Kozlyuk            driver.pci_ids = cls._load_pci_ids(image, pci_table_name_symbol)
1586c4bf8f4SDmitry Kozlyuk
1596c4bf8f4SDmitry Kozlyuk        return driver
1606c4bf8f4SDmitry Kozlyuk
1616c4bf8f4SDmitry Kozlyuk    @staticmethod
1626c4bf8f4SDmitry Kozlyuk    def _load_pci_ids(image, table_name_symbol):
1636c4bf8f4SDmitry Kozlyuk        table_name = table_name_symbol.string_value
1646c4bf8f4SDmitry Kozlyuk        table_symbol = image.find_by_name(table_name)
1656c4bf8f4SDmitry Kozlyuk        if not table_symbol:
1666c4bf8f4SDmitry Kozlyuk            raise Exception("PCI table declared but not defined: %d" % table_name)
1676c4bf8f4SDmitry Kozlyuk
1686c4bf8f4SDmitry Kozlyuk        rte_pci_id = define_rte_pci_id(image.is_big_endian)
1696c4bf8f4SDmitry Kozlyuk
1706c4bf8f4SDmitry Kozlyuk        result = []
1715031436fSDmitry Kozlyuk        while True:
1725031436fSDmitry Kozlyuk            size = ctypes.sizeof(rte_pci_id)
1735031436fSDmitry Kozlyuk            offset = size * len(result)
1745031436fSDmitry Kozlyuk            data = table_symbol.get_value(offset, size)
1755031436fSDmitry Kozlyuk            if not data:
1765031436fSDmitry Kozlyuk                break
1775031436fSDmitry Kozlyuk            pci_id = rte_pci_id.from_buffer_copy(data)
1786c4bf8f4SDmitry Kozlyuk            if not pci_id.device_id:
1796c4bf8f4SDmitry Kozlyuk                break
1805031436fSDmitry Kozlyuk            result.append(
1815031436fSDmitry Kozlyuk                [
1826c4bf8f4SDmitry Kozlyuk                    pci_id.vendor_id,
1836c4bf8f4SDmitry Kozlyuk                    pci_id.device_id,
1846c4bf8f4SDmitry Kozlyuk                    pci_id.subsystem_vendor_id,
1856c4bf8f4SDmitry Kozlyuk                    pci_id.subsystem_device_id,
1865031436fSDmitry Kozlyuk                ]
1875031436fSDmitry Kozlyuk            )
1886c4bf8f4SDmitry Kozlyuk        return result
1896c4bf8f4SDmitry Kozlyuk
1906c4bf8f4SDmitry Kozlyuk    def dump(self, file):
1916c4bf8f4SDmitry Kozlyuk        dumped = json.dumps(self.__dict__)
1926c4bf8f4SDmitry Kozlyuk        escaped = dumped.replace('"', '\\"')
1936c4bf8f4SDmitry Kozlyuk        print(
1946c4bf8f4SDmitry Kozlyuk            'const char %s_pmd_info[] __attribute__((used)) = "PMD_INFO_STRING= %s";'
1956c4bf8f4SDmitry Kozlyuk            % (self.name, escaped),
1966c4bf8f4SDmitry Kozlyuk            file=file,
1976c4bf8f4SDmitry Kozlyuk        )
1986c4bf8f4SDmitry Kozlyuk
1996c4bf8f4SDmitry Kozlyuk
2006c4bf8f4SDmitry Kozlyukdef load_drivers(image):
2016c4bf8f4SDmitry Kozlyuk    drivers = []
2026c4bf8f4SDmitry Kozlyuk    for symbol in image.find_by_prefix("this_pmd_name"):
2036c4bf8f4SDmitry Kozlyuk        drivers.append(Driver.load(image, symbol))
2046c4bf8f4SDmitry Kozlyuk    return drivers
2056c4bf8f4SDmitry Kozlyuk
2066c4bf8f4SDmitry Kozlyuk
2076c4bf8f4SDmitry Kozlyukdef dump_drivers(drivers, file):
2086c4bf8f4SDmitry Kozlyuk    # Keep legacy order of definitions.
2096c4bf8f4SDmitry Kozlyuk    for driver in reversed(drivers):
2106c4bf8f4SDmitry Kozlyuk        driver.dump(file)
2116c4bf8f4SDmitry Kozlyuk
2126c4bf8f4SDmitry Kozlyuk
2136c4bf8f4SDmitry Kozlyukdef parse_args():
2146c4bf8f4SDmitry Kozlyuk    parser = argparse.ArgumentParser()
2155031436fSDmitry Kozlyuk    parser.add_argument("format", help="object file format, 'elf' or 'coff'")
2160fe5c4e5SDmitry Kozlyuk    parser.add_argument(
2170fe5c4e5SDmitry Kozlyuk        "input", nargs='+', help="input object file path or '-' for stdin"
2180fe5c4e5SDmitry Kozlyuk    )
2196c4bf8f4SDmitry Kozlyuk    parser.add_argument("output", help="output C file path or '-' for stdout")
2206c4bf8f4SDmitry Kozlyuk    return parser.parse_args()
2216c4bf8f4SDmitry Kozlyuk
2226c4bf8f4SDmitry Kozlyuk
2236c4bf8f4SDmitry Kozlyukdef open_input(path):
2246c4bf8f4SDmitry Kozlyuk    if path == "-":
2256c4bf8f4SDmitry Kozlyuk        temp = tempfile.TemporaryFile()
2266c4bf8f4SDmitry Kozlyuk        temp.write(sys.stdin.buffer.read())
2276c4bf8f4SDmitry Kozlyuk        return temp
2286c4bf8f4SDmitry Kozlyuk    return open(path, "rb")
2296c4bf8f4SDmitry Kozlyuk
2306c4bf8f4SDmitry Kozlyuk
2315031436fSDmitry Kozlyukdef read_input(path):
2325031436fSDmitry Kozlyuk    if path == "-":
2335031436fSDmitry Kozlyuk        return sys.stdin.buffer.read()
2345031436fSDmitry Kozlyuk    with open(path, "rb") as file:
2355031436fSDmitry Kozlyuk        return file.read()
2365031436fSDmitry Kozlyuk
2375031436fSDmitry Kozlyuk
2385031436fSDmitry Kozlyukdef load_image(fmt, path):
2395031436fSDmitry Kozlyuk    if fmt == "elf":
2405031436fSDmitry Kozlyuk        return ELFImage(open_input(path))
2415031436fSDmitry Kozlyuk    if fmt == "coff":
2425031436fSDmitry Kozlyuk        return COFFImage(read_input(path))
2435031436fSDmitry Kozlyuk    raise Exception("unsupported object file format")
2445031436fSDmitry Kozlyuk
2455031436fSDmitry Kozlyuk
2466c4bf8f4SDmitry Kozlyukdef open_output(path):
2476c4bf8f4SDmitry Kozlyuk    if path == "-":
2486c4bf8f4SDmitry Kozlyuk        return sys.stdout
2496c4bf8f4SDmitry Kozlyuk    return open(path, "w")
2506c4bf8f4SDmitry Kozlyuk
2516c4bf8f4SDmitry Kozlyuk
252e6e9730cSDmitry Kozlyukdef write_header(output):
253e6e9730cSDmitry Kozlyuk    output.write(
254e6e9730cSDmitry Kozlyuk        "static __attribute__((unused)) const char *generator = \"%s\";\n" % sys.argv[0]
255e6e9730cSDmitry Kozlyuk    )
256e6e9730cSDmitry Kozlyuk
257e6e9730cSDmitry Kozlyuk
2586c4bf8f4SDmitry Kozlyukdef main():
2596c4bf8f4SDmitry Kozlyuk    args = parse_args()
2600fe5c4e5SDmitry Kozlyuk    if args.input.count('-') > 1:
2610fe5c4e5SDmitry Kozlyuk        raise Exception("'-' input cannot be used multiple times")
2625031436fSDmitry Kozlyuk    if args.format == "elf" and "ELFFile" not in globals():
2635031436fSDmitry Kozlyuk        raise Exception("elftools module not found")
2645031436fSDmitry Kozlyuk
2656c4bf8f4SDmitry Kozlyuk    output = open_output(args.output)
266e6e9730cSDmitry Kozlyuk    write_header(output)
2670fe5c4e5SDmitry Kozlyuk    for path in args.input:
2680fe5c4e5SDmitry Kozlyuk        image = load_image(args.format, path)
2690fe5c4e5SDmitry Kozlyuk        drivers = load_drivers(image)
2706c4bf8f4SDmitry Kozlyuk        dump_drivers(drivers, output)
2716c4bf8f4SDmitry Kozlyuk
2726c4bf8f4SDmitry Kozlyuk
2736c4bf8f4SDmitry Kozlyukif __name__ == "__main__":
2746c4bf8f4SDmitry Kozlyuk    main()
275