Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright 2023 Google LLC |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | |
| 6 | """ |
| 7 | Checks the initialization priorities |
| 8 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 9 | This script parses a Zephyr executable file, creates a list of known devices |
| 10 | and their effective initialization priorities and compares that with the device |
| 11 | dependencies inferred from the devicetree hierarchy. |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 12 | |
| 13 | This can be used to detect devices that are initialized in the incorrect order, |
| 14 | but also devices that are initialized at the same priority but depends on each |
| 15 | other, which can potentially break if the linking order is changed. |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 16 | |
| 17 | Optionally, it can also produce a human readable list of the initialization |
| 18 | calls for the various init levels. |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 19 | """ |
| 20 | |
| 21 | import argparse |
| 22 | import logging |
| 23 | import os |
| 24 | import pathlib |
| 25 | import pickle |
| 26 | import sys |
| 27 | |
| 28 | from elftools.elf.elffile import ELFFile |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 29 | from elftools.elf.sections import SymbolTableSection |
| 30 | |
| 31 | # This is needed to load edt.pickle files. |
| 32 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", |
| 33 | "dts", "python-devicetree", "src")) |
| 34 | from devicetree import edtlib # pylint: disable=unused-import |
| 35 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 36 | # Prefix used for "struct device" reference initialized based on devicetree |
| 37 | # entries with a known ordinal. |
| 38 | _DEVICE_ORD_PREFIX = "__device_dts_ord_" |
| 39 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 40 | # Defined init level in order of priority. |
| 41 | _DEVICE_INIT_LEVELS = ["EARLY", "PRE_KERNEL_1", "PRE_KERNEL_2", "POST_KERNEL", |
| 42 | "APPLICATION", "SMP"] |
| 43 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 44 | # List of compatibles for node where the initialization priority should be the |
| 45 | # opposite of the device tree inferred dependency. |
| 46 | _INVERTED_PRIORITY_COMPATIBLES = frozenset() |
| 47 | |
Fabio Baltieri | 6d99e38 | 2023-07-21 15:20:43 +0000 | [diff] [blame] | 48 | # List of compatibles for nodes where we don't check the priority. |
| 49 | _IGNORE_COMPATIBLES = frozenset([ |
| 50 | # There is no direct dependency between the CDC ACM UART and the USB |
| 51 | # device controller, the logical connection is established after USB |
| 52 | # device support is enabled. |
| 53 | "zephyr,cdc-acm-uart", |
| 54 | ]) |
| 55 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 56 | class Priority: |
| 57 | """Parses and holds a device initialization priority. |
| 58 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 59 | The object can be used for comparing levels with one another. |
| 60 | |
| 61 | Attributes: |
| 62 | name: the section name |
| 63 | """ |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 64 | def __init__(self, level, priority): |
| 65 | for idx, level_name in enumerate(_DEVICE_INIT_LEVELS): |
| 66 | if level_name == level: |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 67 | self._level = idx |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 68 | self._priority = priority |
Jordan Yates | 23280f4 | 2023-07-19 21:20:18 +1000 | [diff] [blame] | 69 | # Tuples compare elementwise in order |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 70 | self._level_priority = (self._level, self._priority) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 71 | return |
| 72 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 73 | raise ValueError("Unknown level in %s" % level) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 74 | |
| 75 | def __repr__(self): |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 76 | return "<%s %s %d>" % (self.__class__.__name__, |
| 77 | _DEVICE_INIT_LEVELS[self._level], self._priority) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 78 | |
| 79 | def __str__(self): |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 80 | return "%s+%d" % (_DEVICE_INIT_LEVELS[self._level], self._priority) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 81 | |
| 82 | def __lt__(self, other): |
| 83 | return self._level_priority < other._level_priority |
| 84 | |
| 85 | def __eq__(self, other): |
| 86 | return self._level_priority == other._level_priority |
| 87 | |
| 88 | def __hash__(self): |
| 89 | return self._level_priority |
| 90 | |
| 91 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 92 | class ZephyrInitLevels: |
| 93 | """Load an executable file and find the initialization calls and devices. |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 94 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 95 | Load a Zephyr executable file and scan for the list of initialization calls |
| 96 | and defined devices. |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 97 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 98 | The list of devices is available in the "devices" class variable in the |
| 99 | {ordinal: Priority} format, the list of initilevels is in the "initlevels" |
| 100 | class variables in the {"level name": ["call", ...]} format. |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 101 | |
| 102 | Attributes: |
| 103 | file_path: path of the file to be loaded. |
| 104 | """ |
| 105 | def __init__(self, file_path): |
| 106 | self.file_path = file_path |
| 107 | self._elf = ELFFile(open(file_path, "rb")) |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 108 | self._load_objects() |
| 109 | self._load_level_addr() |
| 110 | self._process_initlevels() |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 111 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 112 | def _load_objects(self): |
| 113 | """Initialize the object table.""" |
| 114 | self._objects = {} |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 115 | |
| 116 | for section in self._elf.iter_sections(): |
| 117 | if not isinstance(section, SymbolTableSection): |
| 118 | continue |
| 119 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 120 | for sym in section.iter_symbols(): |
| 121 | if (sym.name and |
| 122 | sym.entry.st_size > 0 and |
| 123 | sym.entry.st_info.type in ["STT_OBJECT", "STT_FUNC"]): |
| 124 | self._objects[sym.entry.st_value] = ( |
| 125 | sym.name, sym.entry.st_size, sym.entry.st_shndx) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 126 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 127 | def _load_level_addr(self): |
| 128 | """Find the address associated with known init levels.""" |
| 129 | self._init_level_addr = {} |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 130 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 131 | for section in self._elf.iter_sections(): |
| 132 | if not isinstance(section, SymbolTableSection): |
| 133 | continue |
| 134 | |
| 135 | for sym in section.iter_symbols(): |
| 136 | for level in _DEVICE_INIT_LEVELS: |
| 137 | name = f"__init_{level}_start" |
| 138 | if sym.name == name: |
| 139 | self._init_level_addr[level] = sym.entry.st_value |
| 140 | elif sym.name == "__init_end": |
| 141 | self._init_level_end = sym.entry.st_value |
| 142 | |
| 143 | if len(self._init_level_addr) != len(_DEVICE_INIT_LEVELS): |
| 144 | raise ValueError(f"Missing init symbols, found: {self._init_level_addr}") |
| 145 | |
| 146 | if not self._init_level_end: |
| 147 | raise ValueError(f"Missing init section end symbol") |
| 148 | |
| 149 | def _device_ord_from_name(self, sym_name): |
| 150 | """Find a device ordinal from a symbol name.""" |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 151 | if not sym_name: |
| 152 | return None |
| 153 | |
| 154 | if not sym_name.startswith(_DEVICE_ORD_PREFIX): |
| 155 | return None |
| 156 | |
| 157 | _, device_ord = sym_name.split(_DEVICE_ORD_PREFIX) |
| 158 | return int(device_ord) |
| 159 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 160 | def _object_name(self, addr): |
| 161 | if not addr: |
| 162 | return "NULL" |
| 163 | elif addr in self._objects: |
| 164 | return self._objects[addr][0] |
| 165 | else: |
| 166 | return "unknown" |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 167 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 168 | def _initlevel_pointer(self, addr, idx, shidx): |
| 169 | elfclass = self._elf.elfclass |
| 170 | if elfclass == 32: |
| 171 | ptrsize = 4 |
| 172 | elif elfclass == 64: |
| 173 | ptrsize = 8 |
| 174 | else: |
| 175 | ValueError(f"Unknown pointer size for ELF class f{elfclass}") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 176 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 177 | section = self._elf.get_section(shidx) |
| 178 | start = section.header.sh_addr |
| 179 | data = section.data() |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 180 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 181 | offset = addr - start |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 182 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 183 | start = offset + ptrsize * idx |
| 184 | stop = offset + ptrsize * (idx + 1) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 185 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 186 | return int.from_bytes(data[start:stop], byteorder="little") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 187 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 188 | def _process_initlevels(self): |
| 189 | """Process the init level and find the init functions and devices.""" |
| 190 | self.devices = {} |
| 191 | self.initlevels = {} |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 192 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 193 | for i, level in enumerate(_DEVICE_INIT_LEVELS): |
| 194 | start = self._init_level_addr[level] |
| 195 | if i + 1 == len(_DEVICE_INIT_LEVELS): |
| 196 | stop = self._init_level_end |
| 197 | else: |
| 198 | stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]] |
| 199 | |
| 200 | self.initlevels[level] = [] |
| 201 | |
| 202 | priority = 0 |
| 203 | addr = start |
| 204 | while addr < stop: |
| 205 | if addr not in self._objects: |
| 206 | raise ValueError(f"no symbol at addr {addr:08x}") |
| 207 | obj, size, shidx = self._objects[addr] |
| 208 | |
| 209 | arg0_name = self._object_name(self._initlevel_pointer(addr, 0, shidx)) |
| 210 | arg1_name = self._object_name(self._initlevel_pointer(addr, 1, shidx)) |
| 211 | |
| 212 | self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})") |
| 213 | |
| 214 | ordinal = self._device_ord_from_name(arg1_name) |
| 215 | if ordinal: |
| 216 | prio = Priority(level, priority) |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 217 | self.devices[ordinal] = (prio, arg0_name) |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 218 | |
| 219 | addr += size |
| 220 | priority += 1 |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 221 | |
| 222 | class Validator(): |
| 223 | """Validates the initialization priorities. |
| 224 | |
| 225 | Scans through a build folder for object files and list all the device |
| 226 | initialization priorities. Then compares that against the EDT derived |
| 227 | dependency list and log any found priority issue. |
| 228 | |
| 229 | Attributes: |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 230 | elf_file_path: path of the ELF file |
| 231 | edt_pickle: name of the EDT pickle file |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 232 | log: a logging.Logger object |
| 233 | """ |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 234 | def __init__(self, elf_file_path, edt_pickle, log): |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 235 | self.log = log |
| 236 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 237 | edt_pickle_path = pathlib.Path( |
| 238 | pathlib.Path(elf_file_path).parent, |
| 239 | edt_pickle) |
| 240 | with open(edt_pickle_path, "rb") as f: |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 241 | edt = pickle.load(f) |
| 242 | |
| 243 | self._ord2node = edt.dep_ord2node |
| 244 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 245 | self._obj = ZephyrInitLevels(elf_file_path) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 246 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 247 | self.errors = 0 |
| 248 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 249 | def _check_dep(self, dev_ord, dep_ord): |
| 250 | """Validate the priority between two devices.""" |
| 251 | if dev_ord == dep_ord: |
| 252 | return |
| 253 | |
| 254 | dev_node = self._ord2node[dev_ord] |
| 255 | dep_node = self._ord2node[dep_ord] |
| 256 | |
Fabio Baltieri | 6d99e38 | 2023-07-21 15:20:43 +0000 | [diff] [blame] | 257 | if dev_node._binding: |
| 258 | dev_compat = dev_node._binding.compatible |
| 259 | if dev_compat in _IGNORE_COMPATIBLES: |
| 260 | self.log.info(f"Ignoring priority: {dev_node._binding.compatible}") |
| 261 | return |
| 262 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 263 | if dev_node._binding and dep_node._binding: |
| 264 | dev_compat = dev_node._binding.compatible |
| 265 | dep_compat = dep_node._binding.compatible |
| 266 | if (dev_compat, dep_compat) in _INVERTED_PRIORITY_COMPATIBLES: |
| 267 | self.log.info(f"Swapped priority: {dev_compat}, {dep_compat}") |
| 268 | dev_ord, dep_ord = dep_ord, dev_ord |
| 269 | |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 270 | dev_prio, dev_init = self._obj.devices.get(dev_ord, (None, None)) |
| 271 | dep_prio, dep_init = self._obj.devices.get(dep_ord, (None, None)) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 272 | |
| 273 | if not dev_prio or not dep_prio: |
| 274 | return |
| 275 | |
| 276 | if dev_prio == dep_prio: |
Fabio Baltieri | 2a70c31 | 2023-10-27 09:27:48 +0000 | [diff] [blame] | 277 | raise ValueError(f"{dev_node.path} and {dep_node.path} have the " |
| 278 | f"same priority: {dev_prio}") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 279 | elif dev_prio < dep_prio: |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 280 | if not self.errors: |
| 281 | self.log.error("Device initialization priority validation failed, " |
| 282 | "the sequence of initialization calls does not match " |
| 283 | "the devicetree dependencies.") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 284 | self.errors += 1 |
| 285 | self.log.error( |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 286 | f"{dev_node.path} <{dev_init}> is initialized before its dependency " |
| 287 | f"{dep_node.path} <{dep_init}> ({dev_prio} < {dep_prio})") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 288 | else: |
| 289 | self.log.info( |
Fabio Baltieri | dd178ce | 2023-10-27 10:47:37 +0000 | [diff] [blame] | 290 | f"{dev_node.path} <{dev_init}> {dev_prio} > " |
| 291 | f"{dep_node.path} <{dep_init}> {dep_prio}") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 292 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 293 | def check_edt(self): |
| 294 | """Scan through all known devices and validate the init priorities.""" |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 295 | for dev_ord in self._obj.devices: |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 296 | dev = self._ord2node[dev_ord] |
Fabio Baltieri | 73d5ecc | 2023-09-07 16:27:32 +0000 | [diff] [blame] | 297 | for dep in dev.depends_on: |
| 298 | self._check_dep(dev_ord, dep.dep_ordinal) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 299 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 300 | def print_initlevels(self): |
| 301 | for level, calls in self._obj.initlevels.items(): |
| 302 | print(level) |
| 303 | for call in calls: |
| 304 | print(f" {call}") |
| 305 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 306 | def _parse_args(argv): |
| 307 | """Parse the command line arguments.""" |
| 308 | parser = argparse.ArgumentParser( |
| 309 | description=__doc__, |
| 310 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 311 | allow_abbrev=False) |
| 312 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 313 | parser.add_argument("-f", "--elf-file", default=pathlib.Path("build", "zephyr", "zephyr.elf"), |
| 314 | help="ELF file to use") |
Fabio Baltieri | 44e691e | 2023-06-01 15:11:19 +0000 | [diff] [blame] | 315 | parser.add_argument("-v", "--verbose", action="count", |
| 316 | help=("enable verbose output, can be used multiple times " |
| 317 | "to increase verbosity level")) |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 318 | parser.add_argument("--always-succeed", action="store_true", |
| 319 | help="always exit with a return code of 0, used for testing") |
| 320 | parser.add_argument("-o", "--output", |
| 321 | help="write the output to a file in addition to stdout") |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 322 | parser.add_argument("-i", "--initlevels", action="store_true", |
| 323 | help="print the initlevel functions instead of checking the device dependencies") |
| 324 | parser.add_argument("--edt-pickle", default=pathlib.Path("edt.pickle"), |
| 325 | help="name of the the pickled edtlib.EDT file", |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 326 | type=pathlib.Path) |
| 327 | |
| 328 | return parser.parse_args(argv) |
| 329 | |
| 330 | def _init_log(verbose, output): |
| 331 | """Initialize a logger object.""" |
| 332 | log = logging.getLogger(__file__) |
| 333 | |
| 334 | console = logging.StreamHandler() |
| 335 | console.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) |
| 336 | log.addHandler(console) |
| 337 | |
| 338 | if output: |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 339 | file = logging.FileHandler(output, mode="w") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 340 | file.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) |
| 341 | log.addHandler(file) |
| 342 | |
Fabio Baltieri | 44e691e | 2023-06-01 15:11:19 +0000 | [diff] [blame] | 343 | if verbose and verbose > 1: |
| 344 | log.setLevel(logging.DEBUG) |
| 345 | elif verbose and verbose > 0: |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 346 | log.setLevel(logging.INFO) |
| 347 | else: |
| 348 | log.setLevel(logging.WARNING) |
| 349 | |
| 350 | return log |
| 351 | |
| 352 | def main(argv=None): |
| 353 | args = _parse_args(argv) |
| 354 | |
| 355 | log = _init_log(args.verbose, args.output) |
| 356 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 357 | log.info(f"check_init_priorities: {args.elf_file}") |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 358 | |
Fabio Baltieri | 5212a4c | 2023-09-08 18:10:13 +0000 | [diff] [blame] | 359 | validator = Validator(args.elf_file, args.edt_pickle, log) |
| 360 | if args.initlevels: |
| 361 | validator.print_initlevels() |
| 362 | else: |
| 363 | validator.check_edt() |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 364 | |
| 365 | if args.always_succeed: |
| 366 | return 0 |
| 367 | |
Fabio Baltieri | ef1bd08 | 2023-03-08 17:50:56 +0000 | [diff] [blame] | 368 | if validator.errors: |
| 369 | return 1 |
| 370 | |
| 371 | return 0 |
| 372 | |
| 373 | if __name__ == "__main__": |
| 374 | sys.exit(main(sys.argv[1:])) |