1#!/usr/bin/env python3 2# SPDX-License-Identifier: BSD-3-Clause 3# SPDX-FileCopyrightText: Copyright TF-RMM Contributors. 4# 5 6""" 7Script creating a bundle from app binaries and RMM binary 8 9The script prepends the app binaries to the RMM binary, and generates a branch 10instruction at the beginning of the binary file. This way the RMM code can start 11running when the execution reaches the beginning of the bundled binary. 12 13A 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 | Pad to RMM_BIN_ALIGNMENT | | 41 | (May be missing) | | 42 +------------------------------+ -+ 43 | | | 44 | RMM binary content | +rmm_bin 45 | | | 46 +------------------------------+ -+ 47``` 48""" 49 50from argparse import ArgumentParser 51import logging 52import struct 53import sys 54 55logger = None 56 57RMM_BIN_ALIGNMENT = 64 * 1024 58 59 60def initial_branch_instruction(offset): 61 """Generate the initial branch instruction to jump to RMM text""" 62 assert offset > 0 63 assert offset % 4 == 0 64 imm = offset // 4 65 assert imm < (1 << 26) # imm can be at most 25 bits 66 template = 0x94000000 67 # Use struct to make sure that the result is a 4 byte integer in 68 # little-endian byte order 69 return struct.pack("<I", template | imm) 70 71 72def main(): 73 """Main function of the script""" 74 75 parser = ArgumentParser( 76 description="Create a bundle from the app and RMM binaries." 77 ) 78 parser.add_argument( 79 "app_bin_files", 80 metavar="APP_BIN_FILE", 81 type=str, 82 nargs="+", 83 help="input application data file(s) for bin generation", 84 ) 85 parser.add_argument( 86 "--out-bin", 87 metavar="FILE", 88 type=str, 89 required=True, 90 help="the output bin file generated by gen_app_bin.py", 91 ) 92 parser.add_argument( 93 "--rmm-bin", 94 metavar="FILE", 95 type=str, 96 required=True, 97 help="the RMM bin input file for bin generation", 98 ) 99 parser.add_argument( 100 "--log-file-name", 101 metavar="FILE", 102 type=str, 103 required=False, 104 default="", 105 help="write logs to 'FILE' as well", 106 ) 107 108 args = parser.parse_args() 109 110 global logger 111 logger = logging.getLogger() 112 logger.setLevel(logging.DEBUG) 113 fmt = logging.Formatter("%(levelname)s: %(message)s") 114 console_hdl = logging.StreamHandler(sys.stdout) 115 console_hdl.setFormatter(fmt) 116 logger.addHandler(console_hdl) 117 118 if args.log_file_name: 119 file_hdl = logging.FileHandler(args.log_file_name, mode="w") 120 file_hdl.setFormatter(fmt) 121 logger.addHandler(file_hdl) 122 123 apps_size = 0 124 app_bin_contents = [] 125 126 # Collect the contents of the app bin files and concatenate them in a list. 127 for app_bin_file_name in args.app_bin_files: 128 with open(app_bin_file_name, "rb") as app_bin_file: 129 app_bin_content = app_bin_file.read() 130 apps_size += len(app_bin_content) 131 app_bin_contents.append(app_bin_content) 132 133 rmm_bin_padding = 0 134 if (apps_size % RMM_BIN_ALIGNMENT) != 0: 135 rmm_bin_padding = RMM_BIN_ALIGNMENT - (apps_size % RMM_BIN_ALIGNMENT) 136 137 rmm_bin_offset = apps_size + rmm_bin_padding 138 139 # Create the bundled bin file 140 with open(args.out_bin, "wb") as out_file: 141 # Write the starting branch instruction 142 out_file.write(initial_branch_instruction(rmm_bin_offset)) 143 # for the first entry, the Initial branch instruction is added in place 144 # the first 4 bytes of the padding in the app header. 145 start_offset = 4 146 for app_bin_content in app_bin_contents: 147 out_file.write(app_bin_content[start_offset:]) 148 # For the rest of the files, write the full header 149 start_offset = 0 150 151 # Add padding so that the RMM bin start is aligned at RMM_BIN_ALIGNMENT 152 out_file.write(bytearray(rmm_bin_padding)) 153 154 # Add the RMM bin file to the bundle 155 with open(args.rmm_bin, "rb") as rmm_bin_file: 156 out_file.write(rmm_bin_file.read()) 157 158 logger.info( 159 f"{args.out_bin} was successfully created. Added {len(args.app_bin_files)} app(s)." 160 ) 161 logger.info( 162 f"The offset of the RMM core is {rmm_bin_offset} (0x{rmm_bin_offset:x}) bytes from start of packaged bin." 163 ) 164 165 166if __name__ == "__main__": 167 main() 168