Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (c) 2017 Intel Corporation |
| 4 | # Copyright (c) 2020 Nordic Semiconductor NA |
| 5 | # |
| 6 | # SPDX-License-Identifier: Apache-2.0 |
| 7 | """Translate generic handles into ones optimized for the application. |
| 8 | |
| 9 | Immutable device data includes information about dependencies, |
| 10 | e.g. that a particular sensor is controlled through a specific I2C bus |
| 11 | and that it signals event on a pin on a specific GPIO controller. |
| 12 | This information is encoded in the first-pass binary using identifiers |
| 13 | derived from the devicetree. This script extracts those identifiers |
| 14 | and replaces them with ones optimized for use with the devices |
| 15 | actually present. |
| 16 | |
| 17 | For example the sensor might have a first-pass handle defined by its |
| 18 | devicetree ordinal 52, with the I2C driver having ordinal 24 and the |
| 19 | GPIO controller ordinal 14. The runtime ordinal is the index of the |
| 20 | corresponding device in the static devicetree array, which might be 6, |
| 21 | 5, and 3, respectively. |
| 22 | |
| 23 | The output is a C source file that provides alternative definitions |
| 24 | for the array contents referenced from the immutable device objects. |
| 25 | In the final link these definitions supersede the ones in the |
| 26 | driver-specific object file. |
| 27 | """ |
| 28 | |
| 29 | import sys |
| 30 | import argparse |
| 31 | import os |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 32 | import pickle |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 33 | |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 34 | from elf_parser import ZephyrElf |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 35 | |
Martí Bolívar | 5332847 | 2021-03-26 16:18:58 -0700 | [diff] [blame] | 36 | # This is needed to load edt.pickle files. |
Anas Nashif | 80f4b5d | 2022-07-11 10:48:04 -0400 | [diff] [blame] | 37 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', |
Jordan Yates | 8e4107f | 2022-04-30 21:13:52 +1000 | [diff] [blame] | 38 | 'dts', 'python-devicetree', 'src')) |
Martí Bolívar | 5332847 | 2021-03-26 16:18:58 -0700 | [diff] [blame] | 39 | |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 40 | def parse_args(): |
| 41 | global args |
| 42 | |
| 43 | parser = argparse.ArgumentParser( |
| 44 | description=__doc__, |
Jamie McCrae | ec70444 | 2023-01-04 16:08:36 +0000 | [diff] [blame] | 45 | formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 46 | |
| 47 | parser.add_argument("-k", "--kernel", required=True, |
| 48 | help="Input zephyr ELF binary") |
Gerard Marull-Paretas | b6d5d24 | 2023-06-14 17:12:55 +0200 | [diff] [blame] | 49 | parser.add_argument("--dynamic-deps", action="store_true", |
| 50 | help="Indicates if device dependencies are dynamic") |
Flavio Ceolin | 0b13b44 | 2022-01-05 17:19:53 -0800 | [diff] [blame] | 51 | parser.add_argument("-d", "--num-dynamic-devices", required=False, default=0, |
| 52 | type=int, help="Input number of dynamic devices allowed") |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 53 | parser.add_argument("-o", "--output-source", required=True, |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 54 | help="Output source file") |
Jordan Yates | 2994247 | 2022-07-10 13:46:17 +1000 | [diff] [blame] | 55 | parser.add_argument("-g", "--output-graphviz", |
| 56 | help="Output file for graphviz dependency graph") |
Torsten Rasmussen | b92b580 | 2021-03-12 15:28:54 +0100 | [diff] [blame] | 57 | parser.add_argument("-z", "--zephyr-base", |
| 58 | help="Path to current Zephyr base. If this argument \ |
| 59 | is not provided the environment will be checked for \ |
| 60 | the ZEPHYR_BASE environment variable.") |
Torsten Rasmussen | c9804d2 | 2021-05-21 21:34:58 +0200 | [diff] [blame] | 61 | parser.add_argument("-s", "--start-symbol", required=True, |
| 62 | help="Symbol name of the section which contains the \ |
| 63 | devices. The symbol name must point to the first \ |
| 64 | device in that section.") |
| 65 | |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 66 | args = parser.parse_args() |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 67 | |
Torsten Rasmussen | b92b580 | 2021-03-12 15:28:54 +0100 | [diff] [blame] | 68 | ZEPHYR_BASE = args.zephyr_base or os.getenv("ZEPHYR_BASE") |
| 69 | |
| 70 | if ZEPHYR_BASE is None: |
| 71 | sys.exit("-z / --zephyr-base not provided. Please provide " |
| 72 | "--zephyr-base or set ZEPHYR_BASE in environment") |
| 73 | |
| 74 | sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/dts")) |
| 75 | |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 76 | def c_handle_comment(dev, handles): |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 77 | def dev_path_str(dev): |
| 78 | return dev.edt_node and dev.edt_node.path or dev.sym.name |
| 79 | lines = [ |
| 80 | '', |
| 81 | '/* {:d} : {:s}:'.format(dev.handle, (dev_path_str(dev))), |
| 82 | ] |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 83 | if len(handles["depends"]) > 0: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 84 | lines.append(' * Direct Dependencies:') |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 85 | for dep in handles["depends"]: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 86 | lines.append(' * - {:s}'.format(dev_path_str(dep))) |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 87 | if len(handles["injected"]) > 0: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 88 | lines.append(' * Injected Dependencies:') |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 89 | for dep in handles["injected"]: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 90 | lines.append(' * - {:s}'.format(dev_path_str(dep))) |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 91 | if len(handles["supports"]) > 0: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 92 | lines.append(' * Supported:') |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 93 | for sup in handles["supports"]: |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 94 | lines.append(' * - {:s}'.format(dev_path_str(sup))) |
| 95 | lines.append(' */') |
| 96 | return lines |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 97 | |
Gerard Marull-Paretas | b6d5d24 | 2023-06-14 17:12:55 +0200 | [diff] [blame] | 98 | def c_handle_array(dev, handles, dynamic_deps, extra_support_handles=0): |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 99 | handles = [ |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 100 | *[str(d.handle) for d in handles["depends"]], |
Gerard Marull-Paretas | 4eb406e | 2023-06-14 12:35:14 +0200 | [diff] [blame] | 101 | 'Z_DEVICE_DEPS_SEP', |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 102 | *[str(d.handle) for d in handles["injected"]], |
Gerard Marull-Paretas | 4eb406e | 2023-06-14 12:35:14 +0200 | [diff] [blame] | 103 | 'Z_DEVICE_DEPS_SEP', |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 104 | *[str(d.handle) for d in handles["supports"]], |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 105 | *(extra_support_handles * ['DEVICE_HANDLE_NULL']), |
Gerard Marull-Paretas | 4eb406e | 2023-06-14 12:35:14 +0200 | [diff] [blame] | 106 | 'Z_DEVICE_DEPS_ENDS', |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 107 | ] |
Gerard Marull-Paretas | e5335f3 | 2023-06-14 10:15:12 +0200 | [diff] [blame] | 108 | ctype = ( |
| 109 | '{:s}Z_DECL_ALIGN(device_handle_t) ' |
Gerard Marull-Paretas | 8bee39e | 2023-06-14 12:10:40 +0200 | [diff] [blame] | 110 | '__attribute__((__section__(".__device_deps_pass2")))' |
Gerard Marull-Paretas | b6d5d24 | 2023-06-14 17:12:55 +0200 | [diff] [blame] | 111 | ).format('const ' if not dynamic_deps else '') |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 112 | return [ |
Marc Herbert | 16880cc | 2023-04-24 18:53:22 +0000 | [diff] [blame] | 113 | # The `extern` line pretends this was first declared in some .h |
| 114 | # file to silence "should it be static?" warnings in some |
| 115 | # compilers and static analyzers. |
| 116 | 'extern {:s} {:s}[{:d}];'.format(ctype, dev.ordinals.sym.name, len(handles)), |
| 117 | ctype, |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 118 | '{:s}[] = {{ {:s} }};'.format(dev.ordinals.sym.name, ', '.join(handles)), |
| 119 | ] |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 120 | |
| 121 | def main(): |
| 122 | parse_args() |
| 123 | |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 124 | edtser = os.path.join(os.path.split(args.kernel)[0], "edt.pickle") |
| 125 | with open(edtser, 'rb') as f: |
| 126 | edt = pickle.load(f) |
| 127 | |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 128 | parsed_elf = ZephyrElf(args.kernel, edt, args.start_symbol) |
Jordan Yates | 86dd23e | 2023-05-30 17:48:26 +1000 | [diff] [blame] | 129 | if parsed_elf.relocatable: |
| 130 | # While relocatable elf files will load cleanly, the pointers pulled from |
| 131 | # the symbol table are invalid (as expected, because the structures have not |
| 132 | # yet been allocated addresses). Fixing this will require iterating over |
| 133 | # the relocation sections to find the symbols those pointers will end up |
| 134 | # referring to. |
| 135 | sys.exit('Relocatable elf files are not yet supported') |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 136 | |
Jordan Yates | 2994247 | 2022-07-10 13:46:17 +1000 | [diff] [blame] | 137 | if args.output_graphviz: |
| 138 | # Try and output the dependency tree |
| 139 | try: |
| 140 | dot = parsed_elf.device_dependency_graph('Device dependency graph', args.kernel) |
| 141 | with open(args.output_graphviz, 'w') as f: |
| 142 | f.write(dot.source) |
| 143 | except ImportError: |
| 144 | pass |
| 145 | |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 146 | with open(args.output_source, "w") as fp: |
Gerard Marull-Paretas | 8f09118 | 2022-05-09 13:58:12 +0200 | [diff] [blame] | 147 | fp.write('#include <zephyr/device.h>\n') |
| 148 | fp.write('#include <zephyr/toolchain.h>\n') |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 149 | for dev in parsed_elf.devices: |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 150 | # The device handle are collected up in a set, which has no |
| 151 | # specified order. Sort each sub-category of device handle types |
| 152 | # separately, so that the generated C array is reproducible across |
| 153 | # builds. |
| 154 | sorted_handles = { |
| 155 | "depends": sorted(dev.devs_depends_on, key=lambda d: d.handle), |
| 156 | "injected": sorted(dev.devs_depends_on_injected, key=lambda d: d.handle), |
| 157 | "supports": sorted(dev.devs_supports, key=lambda d: d.handle), |
| 158 | } |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 159 | extra_sups = args.num_dynamic_devices if dev.pm and dev.pm.is_power_domain else 0 |
Keith Short | f896fc2 | 2022-07-22 15:28:22 -0600 | [diff] [blame] | 160 | lines = c_handle_comment(dev, sorted_handles) |
Gerard Marull-Paretas | e5335f3 | 2023-06-14 10:15:12 +0200 | [diff] [blame] | 161 | lines.extend( |
Gerard Marull-Paretas | b6d5d24 | 2023-06-14 17:12:55 +0200 | [diff] [blame] | 162 | c_handle_array(dev, sorted_handles, args.dynamic_deps, extra_sups) |
Gerard Marull-Paretas | e5335f3 | 2023-06-14 10:15:12 +0200 | [diff] [blame] | 163 | ) |
Jordan Yates | 8d17e85 | 2022-07-10 13:43:24 +1000 | [diff] [blame] | 164 | lines.extend(['']) |
Peter Bigot | d554d34 | 2020-06-30 10:05:35 -0500 | [diff] [blame] | 165 | fp.write('\n'.join(lines)) |
| 166 | |
| 167 | if __name__ == "__main__": |
| 168 | main() |