Mate Toth-Pal | e60deeb | 2024-08-01 10:19:20 +0200 | [diff] [blame^] | 1 | #!/usr/bin/env python3 |
| 2 | # SPDX-License-Identifier: BSD-3-Clause |
| 3 | # SPDX-FileCopyrightText: Copyright TF-RMM Contributors. |
| 4 | # |
| 5 | |
| 6 | """ |
| 7 | Script creating a bundle from app binaries and RMM binary |
| 8 | |
| 9 | The script prepends the app binaries to the RMM binary, and generates a branch |
| 10 | instruction at the beginning of the binary file. This way the RMM code can start |
| 11 | running when the execution reaches the beginning of the bundled binary. |
| 12 | |
| 13 | A bundled RMM binary has the following structure: |
| 14 | ``` |
| 15 | +------------------------------+ -+ |
| 16 | | BL rmm_bin_offset | Generated by this script | |
| 17 | | | | |
| 18 | | Rest of the header unchanged | | |
| 19 | |..............................| | |
| 20 | | | + app_bin_file_1 |
| 21 | | App binary content | | |
| 22 | | | | |
| 23 | +------------------------------+ -+ |
| 24 | | Unchanged bin header | | |
| 25 | |..............................| | |
| 26 | | | + app_bin_file_2 |
| 27 | | App binary content | | |
| 28 | | | | |
| 29 | +------------------------------+ -+ |
| 30 | | | | |
| 31 | ... ... |
| 32 | | | | |
| 33 | +------------------------------+ -+ |
| 34 | | Unchanged bin header | | |
| 35 | |..............................| | |
| 36 | | | + app_bin_file_n |
| 37 | | App binary content | | |
| 38 | | | | |
| 39 | +------------------------------+ -+ |
| 40 | | | | |
| 41 | | RMM binary content | +rmm_bin |
| 42 | | | | |
| 43 | +------------------------------+ -+ |
| 44 | ``` |
| 45 | """ |
| 46 | |
| 47 | from argparse import ArgumentParser |
| 48 | import logging |
| 49 | import struct |
| 50 | |
| 51 | logger = None |
| 52 | |
| 53 | |
| 54 | def initial_branch_instruction(offset): |
| 55 | """Generate the initial branch instruction to jump to RMM text""" |
| 56 | assert offset > 0 |
| 57 | assert offset % 4 == 0 |
| 58 | imm = offset // 4 |
| 59 | assert imm < (1 << 26) # imm can be at most 25 bits |
| 60 | template = 0x94000000 |
| 61 | # Use struct to make sure that the result is a 4 byte integer in |
| 62 | # little-endian byte order |
| 63 | return struct.pack("<I", template | imm) |
| 64 | |
| 65 | |
| 66 | def main(): |
| 67 | """Main function of the script""" |
| 68 | |
| 69 | parser = ArgumentParser( |
| 70 | description="Create a bundle from the app and RMM binaries." |
| 71 | ) |
| 72 | parser.add_argument( |
| 73 | "app_bin_files", |
| 74 | metavar="APP_BIN_FILE", |
| 75 | type=str, |
| 76 | nargs="+", |
| 77 | help="input application data file(s) for bin generation", |
| 78 | ) |
| 79 | parser.add_argument( |
| 80 | "--out-bin", |
| 81 | metavar="FILE", |
| 82 | type=str, |
| 83 | required=True, |
| 84 | help="the output bin file generated by gen_app_bin.py", |
| 85 | ) |
| 86 | parser.add_argument( |
| 87 | "--rmm-bin", |
| 88 | metavar="FILE", |
| 89 | type=str, |
| 90 | required=True, |
| 91 | help="the RMM bin input file for bin generation", |
| 92 | ) |
| 93 | parser.add_argument( |
| 94 | "--log-file-name", |
| 95 | metavar="FILE", |
| 96 | type=str, |
| 97 | required=False, |
| 98 | default="", |
| 99 | help="write logs to 'FILE' as well", |
| 100 | ) |
| 101 | |
| 102 | args = parser.parse_args() |
| 103 | |
| 104 | global logger |
| 105 | logger = logging.getLogger() |
| 106 | logger.setLevel(logging.DEBUG) |
| 107 | fmt = logging.Formatter("%(levelname)s: %(message)s") |
| 108 | hdl = logging.StreamHandler() |
| 109 | hdl.setFormatter(fmt) |
| 110 | logger.addHandler(hdl) |
| 111 | |
| 112 | if args.log_file_name: |
| 113 | hdl = logging.FileHandler(args.log_file_name, mode="w") |
| 114 | hdl.setFormatter(fmt) |
| 115 | logger.addHandler(hdl) |
| 116 | |
| 117 | apps_size = 0 |
| 118 | app_bin_contents = [] |
| 119 | |
| 120 | # Collect the contents of the app bin files and concatenate them in a list. |
| 121 | for app_bin_file_name in args.app_bin_files: |
| 122 | with open(app_bin_file_name, "rb") as app_bin_file: |
| 123 | app_bin_content = app_bin_file.read() |
| 124 | apps_size += len(app_bin_content) |
| 125 | app_bin_contents.append(app_bin_content) |
| 126 | |
| 127 | # Create the bundled bin file |
| 128 | with open(args.out_bin, "wb") as out_file: |
| 129 | # Write the starting branch instruction |
| 130 | out_file.write(initial_branch_instruction(apps_size)) |
| 131 | # for the first entry, the Initial branch instruction is added in place |
| 132 | # the first 4 bytes of the padding in the app header. |
| 133 | start_offset = 4 |
| 134 | for app_bin_content in app_bin_contents: |
| 135 | out_file.write(app_bin_content[start_offset:]) |
| 136 | # For the rest of the files, write the full header |
| 137 | start_offset = 0 |
| 138 | |
| 139 | # Add the RMM bin file to the bundle |
| 140 | with open(args.rmm_bin, "rb") as rmm_bin_file: |
| 141 | out_file.write(rmm_bin_file.read()) |
| 142 | |
| 143 | logger.info( |
| 144 | f"{args.out_bin} was successfully created. Added {len(args.app_bin_files)} app(s)." |
| 145 | ) |
| 146 | logger.info(f"The offset of the RMM bin is {apps_size} (0x{apps_size:x}) bytes") |
| 147 | |
| 148 | |
| 149 | if __name__ == "__main__": |
| 150 | main() |