examples/ioat: add option to control maximum frame size
[dpdk.git] / buildtools / pmdinfogen.py
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
6 import argparse
7 import ctypes
8 import json
9 import sys
10 import tempfile
11
12 try:
13     import elftools
14     from elftools.elf.elffile import ELFFile
15     from elftools.elf.sections import SymbolTableSection
16 except ImportError:
17     pass
18
19 import coff
20
21
22 class 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
40 class 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
77 class 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
92 class 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
112 def 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
130 class 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
200 def 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
207 def dump_drivers(drivers, file):
208     # Keep legacy order of definitions.
209     for driver in reversed(drivers):
210         driver.dump(file)
211
212
213 def 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
223 def 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
231 def 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
238 def 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
246 def open_output(path):
247     if path == "-":
248         return sys.stdout
249     return open(path, "w")
250
251
252 def write_header(output):
253     output.write(
254         "static __attribute__((unused)) const char *generator = \"%s\";\n" % sys.argv[0]
255     )
256
257
258 def 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
273 if __name__ == "__main__":
274     main()