You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
6.9 KiB
168 lines
6.9 KiB
#!/usr/bin/env python3 |
|
# |
|
# Copyright (c) 2017 Intel Corporation |
|
# Copyright (c) 2020 Nordic Semiconductor NA |
|
# |
|
# SPDX-License-Identifier: Apache-2.0 |
|
"""Translate generic handles into ones optimized for the application. |
|
|
|
Immutable device data includes information about dependencies, |
|
e.g. that a particular sensor is controlled through a specific I2C bus |
|
and that it signals event on a pin on a specific GPIO controller. |
|
This information is encoded in the first-pass binary using identifiers |
|
derived from the devicetree. This script extracts those identifiers |
|
and replaces them with ones optimized for use with the devices |
|
actually present. |
|
|
|
For example the sensor might have a first-pass handle defined by its |
|
devicetree ordinal 52, with the I2C driver having ordinal 24 and the |
|
GPIO controller ordinal 14. The runtime ordinal is the index of the |
|
corresponding device in the static devicetree array, which might be 6, |
|
5, and 3, respectively. |
|
|
|
The output is a C source file that provides alternative definitions |
|
for the array contents referenced from the immutable device objects. |
|
In the final link these definitions supersede the ones in the |
|
driver-specific object file. |
|
""" |
|
|
|
import sys |
|
import argparse |
|
import os |
|
import pickle |
|
|
|
from elf_parser import ZephyrElf |
|
|
|
# This is needed to load edt.pickle files. |
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', |
|
'dts', 'python-devicetree', 'src')) |
|
|
|
def parse_args(): |
|
global args |
|
|
|
parser = argparse.ArgumentParser( |
|
description=__doc__, |
|
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) |
|
|
|
parser.add_argument("-k", "--kernel", required=True, |
|
help="Input zephyr ELF binary") |
|
parser.add_argument("--dynamic-deps", action="store_true", |
|
help="Indicates if device dependencies are dynamic") |
|
parser.add_argument("-d", "--num-dynamic-devices", required=False, default=0, |
|
type=int, help="Input number of dynamic devices allowed") |
|
parser.add_argument("-o", "--output-source", required=True, |
|
help="Output source file") |
|
parser.add_argument("-g", "--output-graphviz", |
|
help="Output file for graphviz dependency graph") |
|
parser.add_argument("-z", "--zephyr-base", |
|
help="Path to current Zephyr base. If this argument \ |
|
is not provided the environment will be checked for \ |
|
the ZEPHYR_BASE environment variable.") |
|
parser.add_argument("-s", "--start-symbol", required=True, |
|
help="Symbol name of the section which contains the \ |
|
devices. The symbol name must point to the first \ |
|
device in that section.") |
|
|
|
args = parser.parse_args() |
|
|
|
ZEPHYR_BASE = args.zephyr_base or os.getenv("ZEPHYR_BASE") |
|
|
|
if ZEPHYR_BASE is None: |
|
sys.exit("-z / --zephyr-base not provided. Please provide " |
|
"--zephyr-base or set ZEPHYR_BASE in environment") |
|
|
|
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/dts")) |
|
|
|
def c_handle_comment(dev, handles): |
|
def dev_path_str(dev): |
|
return dev.edt_node and dev.edt_node.path or dev.sym.name |
|
lines = [ |
|
'', |
|
'/* {:d} : {:s}:'.format(dev.handle, (dev_path_str(dev))), |
|
] |
|
if len(handles["depends"]) > 0: |
|
lines.append(' * Direct Dependencies:') |
|
for dep in handles["depends"]: |
|
lines.append(' * - {:s}'.format(dev_path_str(dep))) |
|
if len(handles["injected"]) > 0: |
|
lines.append(' * Injected Dependencies:') |
|
for dep in handles["injected"]: |
|
lines.append(' * - {:s}'.format(dev_path_str(dep))) |
|
if len(handles["supports"]) > 0: |
|
lines.append(' * Supported:') |
|
for sup in handles["supports"]: |
|
lines.append(' * - {:s}'.format(dev_path_str(sup))) |
|
lines.append(' */') |
|
return lines |
|
|
|
def c_handle_array(dev, handles, dynamic_deps, extra_support_handles=0): |
|
handles = [ |
|
*[str(d.handle) for d in handles["depends"]], |
|
'Z_DEVICE_DEPS_SEP', |
|
*[str(d.handle) for d in handles["injected"]], |
|
'Z_DEVICE_DEPS_SEP', |
|
*[str(d.handle) for d in handles["supports"]], |
|
*(extra_support_handles * ['DEVICE_HANDLE_NULL']), |
|
'Z_DEVICE_DEPS_ENDS', |
|
] |
|
ctype = ( |
|
'{:s}Z_DECL_ALIGN(device_handle_t) ' |
|
'__attribute__((__section__(".__device_deps_pass2")))' |
|
).format('const ' if not dynamic_deps else '') |
|
return [ |
|
# The `extern` line pretends this was first declared in some .h |
|
# file to silence "should it be static?" warnings in some |
|
# compilers and static analyzers. |
|
'extern {:s} {:s}[{:d}];'.format(ctype, dev.ordinals.sym.name, len(handles)), |
|
ctype, |
|
'{:s}[] = {{ {:s} }};'.format(dev.ordinals.sym.name, ', '.join(handles)), |
|
] |
|
|
|
def main(): |
|
parse_args() |
|
|
|
edtser = os.path.join(os.path.split(args.kernel)[0], "edt.pickle") |
|
with open(edtser, 'rb') as f: |
|
edt = pickle.load(f) |
|
|
|
parsed_elf = ZephyrElf(args.kernel, edt, args.start_symbol) |
|
if parsed_elf.relocatable: |
|
# While relocatable elf files will load cleanly, the pointers pulled from |
|
# the symbol table are invalid (as expected, because the structures have not |
|
# yet been allocated addresses). Fixing this will require iterating over |
|
# the relocation sections to find the symbols those pointers will end up |
|
# referring to. |
|
sys.exit('Relocatable elf files are not yet supported') |
|
|
|
if args.output_graphviz: |
|
# Try and output the dependency tree |
|
try: |
|
dot = parsed_elf.device_dependency_graph('Device dependency graph', args.kernel) |
|
with open(args.output_graphviz, 'w') as f: |
|
f.write(dot.source) |
|
except ImportError: |
|
pass |
|
|
|
with open(args.output_source, "w") as fp: |
|
fp.write('#include <zephyr/device.h>\n') |
|
fp.write('#include <zephyr/toolchain.h>\n') |
|
for dev in parsed_elf.devices: |
|
# The device handle are collected up in a set, which has no |
|
# specified order. Sort each sub-category of device handle types |
|
# separately, so that the generated C array is reproducible across |
|
# builds. |
|
sorted_handles = { |
|
"depends": sorted(dev.devs_depends_on, key=lambda d: d.handle), |
|
"injected": sorted(dev.devs_depends_on_injected, key=lambda d: d.handle), |
|
"supports": sorted(dev.devs_supports, key=lambda d: d.handle), |
|
} |
|
extra_sups = args.num_dynamic_devices if dev.pm and dev.pm.is_power_domain else 0 |
|
lines = c_handle_comment(dev, sorted_handles) |
|
lines.extend( |
|
c_handle_array(dev, sorted_handles, args.dynamic_deps, extra_sups) |
|
) |
|
lines.extend(['']) |
|
fp.write('\n'.join(lines)) |
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|