dpdk/buildtools/pmdinfogen.py
<<
>>
Prefs
   1#!/usr/bin/env python3
   2# SPDX-License-Identifier: BSD-3-Clause
   3# Copyright (c) 2016 Neil Horman <nhorman@tuxdriver.com>
   4# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
   5
   6import argparse
   7import ctypes
   8import json
   9import sys
  10import tempfile
  11
  12try:
  13    import elftools
  14    from elftools.elf.elffile import ELFFile
  15    from elftools.elf.sections import SymbolTableSection
  16except ImportError:
  17    pass
  18
  19import coff
  20
  21
  22class ELFSymbol:
  23    def __init__(self, image, symbol):
  24        self._image = image
  25        self._symbol = symbol
  26
  27    @property
  28    def string_value(self):
  29        size = self._symbol["st_size"]
  30        value = self.get_value(0, size)
  31        return coff.decode_asciiz(value)  # not COFF-specific
  32
  33    def get_value(self, offset, size):
  34        section = self._symbol["st_shndx"]
  35        data = self._image.get_section(section).data()
  36        base = self._symbol["st_value"] + offset
  37        return data[base : base + size]
  38
  39
  40class ELFImage:
  41    def __init__(self, data):
  42        version = tuple(int(c) for c in elftools.__version__.split("."))
  43        self._legacy_elftools = version < (0, 24)
  44
  45        self._image = ELFFile(data)
  46
  47        section = b".symtab" if self._legacy_elftools else ".symtab"
  48        self._symtab = self._image.get_section_by_name(section)
  49        if not isinstance(self._symtab, SymbolTableSection):
  50            raise Exception(".symtab section is not a symbol table")
  51
  52    @property
  53    def is_big_endian(self):
  54        return not self._image.little_endian
  55
  56    def find_by_name(self, name):
  57        symbol = self._get_symbol_by_name(name)
  58        return ELFSymbol(self._image, symbol[0]) if symbol else None
  59
  60    def _get_symbol_by_name(self, name):
  61        if not self._legacy_elftools:
  62            return self._symtab.get_symbol_by_name(name)
  63        name = name.encode("utf-8")
  64        for symbol in self._symtab.iter_symbols():
  65            if symbol.name == name:
  66                return [symbol]
  67        return None
  68
  69    def find_by_prefix(self, prefix):
  70        prefix = prefix.encode("utf-8") if self._legacy_elftools else prefix
  71        for i in range(self._symtab.num_symbols()):
  72            symbol = self._symtab.get_symbol(i)
  73            if symbol.name.startswith(prefix):
  74                yield ELFSymbol(self._image, symbol)
  75
  76
  77class COFFSymbol:
  78    def __init__(self, image, symbol):
  79        self._image = image
  80        self._symbol = symbol
  81
  82    def get_value(self, offset, size):
  83        value = self._symbol.get_value(offset)
  84        return value[:size] if value else value
  85
  86    @property
  87    def string_value(self):
  88        value = self._symbol.get_value(0)
  89        return coff.decode_asciiz(value) if value else ''
  90
  91
  92class COFFImage:
  93    def __init__(self, data):
  94        self._image = coff.Image(data)
  95
  96    @property
  97    def is_big_endian(self):
  98        return False
  99
 100    def find_by_prefix(self, prefix):
 101        for symbol in self._image.symbols:
 102            if symbol.name.startswith(prefix):
 103                yield COFFSymbol(self._image, symbol)
 104
 105    def find_by_name(self, name):
 106        for symbol in self._image.symbols:
 107            if symbol.name == name:
 108                return COFFSymbol(self._image, symbol)
 109        return None
 110
 111
 112def define_rte_pci_id(is_big_endian):
 113    base_type = ctypes.LittleEndianStructure
 114    if is_big_endian:
 115        base_type = ctypes.BigEndianStructure
 116
 117    class rte_pci_id(base_type):
 118        _pack_ = True
 119        _fields_ = [
 120            ("class_id", ctypes.c_uint32),
 121            ("vendor_id", ctypes.c_uint16),
 122            ("device_id", ctypes.c_uint16),
 123            ("subsystem_vendor_id", ctypes.c_uint16),
 124            ("subsystem_device_id", ctypes.c_uint16),
 125        ]
 126
 127    return rte_pci_id
 128
 129
 130class Driver:
 131    OPTIONS = [
 132        ("params", "_param_string_export"),
 133        ("kmod", "_kmod_dep_export"),
 134    ]
 135
 136    def __init__(self, name, options):
 137        self.name = name
 138        for key, value in options.items():
 139            setattr(self, key, value)
 140        self.pci_ids = []
 141
 142    @classmethod
 143    def load(cls, image, symbol):
 144        name = symbol.string_value
 145
 146        options = {}
 147        for key, suffix in cls.OPTIONS:
 148            option_symbol = image.find_by_name("__%s%s" % (name, suffix))
 149            if option_symbol:
 150                value = option_symbol.string_value
 151                options[key] = value
 152
 153        driver = cls(name, options)
 154
 155        pci_table_name_symbol = image.find_by_name("__%s_pci_tbl_export" % name)
 156        if pci_table_name_symbol:
 157            driver.pci_ids = cls._load_pci_ids(image, pci_table_name_symbol)
 158
 159        return driver
 160
 161    @staticmethod
 162    def _load_pci_ids(image, table_name_symbol):
 163        table_name = table_name_symbol.string_value
 164        table_symbol = image.find_by_name(table_name)
 165        if not table_symbol:
 166            raise Exception("PCI table declared but not defined: %d" % table_name)
 167
 168        rte_pci_id = define_rte_pci_id(image.is_big_endian)
 169
 170        result = []
 171        while True:
 172            size = ctypes.sizeof(rte_pci_id)
 173            offset = size * len(result)
 174            data = table_symbol.get_value(offset, size)
 175            if not data:
 176                break
 177            pci_id = rte_pci_id.from_buffer_copy(data)
 178            if not pci_id.device_id:
 179                break
 180            result.append(
 181                [
 182                    pci_id.vendor_id,
 183                    pci_id.device_id,
 184                    pci_id.subsystem_vendor_id,
 185                    pci_id.subsystem_device_id,
 186                ]
 187            )
 188        return result
 189
 190    def dump(self, file):
 191        dumped = json.dumps(self.__dict__)
 192        escaped = dumped.replace('"', '\\"')
 193        print(
 194            'const char %s_pmd_info[] __attribute__((used)) = "PMD_INFO_STRING= %s";'
 195            % (self.name, escaped),
 196            file=file,
 197        )
 198
 199
 200def load_drivers(image):
 201    drivers = []
 202    for symbol in image.find_by_prefix("this_pmd_name"):
 203        drivers.append(Driver.load(image, symbol))
 204    return drivers
 205
 206
 207def dump_drivers(drivers, file):
 208    # Keep legacy order of definitions.
 209    for driver in reversed(drivers):
 210        driver.dump(file)
 211
 212
 213def parse_args():
 214    parser = argparse.ArgumentParser()
 215    parser.add_argument("format", help="object file format, 'elf' or 'coff'")
 216    parser.add_argument(
 217        "input", nargs='+', help="input object file path or '-' for stdin"
 218    )
 219    parser.add_argument("output", help="output C file path or '-' for stdout")
 220    return parser.parse_args()
 221
 222
 223def open_input(path):
 224    if path == "-":
 225        temp = tempfile.TemporaryFile()
 226        temp.write(sys.stdin.buffer.read())
 227        return temp
 228    return open(path, "rb")
 229
 230
 231def read_input(path):
 232    if path == "-":
 233        return sys.stdin.buffer.read()
 234    with open(path, "rb") as file:
 235        return file.read()
 236
 237
 238def load_image(fmt, path):
 239    if fmt == "elf":
 240        return ELFImage(open_input(path))
 241    if fmt == "coff":
 242        return COFFImage(read_input(path))
 243    raise Exception("unsupported object file format")
 244
 245
 246def open_output(path):
 247    if path == "-":
 248        return sys.stdout
 249    return open(path, "w")
 250
 251
 252def write_header(output):
 253    output.write(
 254        "static __attribute__((unused)) const char *generator = \"%s\";\n" % sys.argv[0]
 255    )
 256
 257
 258def main():
 259    args = parse_args()
 260    if args.input.count('-') > 1:
 261        raise Exception("'-' input cannot be used multiple times")
 262    if args.format == "elf" and "ELFFile" not in globals():
 263        raise Exception("elftools module not found")
 264
 265    output = open_output(args.output)
 266    write_header(output)
 267    for path in args.input:
 268        image = load_image(args.format, path)
 269        drivers = load_drivers(image)
 270        dump_drivers(drivers, output)
 271
 272
 273if __name__ == "__main__":
 274    main()
 275