blob: eb1deac951aff627348b97cbcb95f63feed72852 [file] [log] [blame]
Mate Toth-Pale60deeb2024-08-01 10:19:20 +02001#!/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 | | |
41 | RMM binary content | +rmm_bin
42 | | |
43 +------------------------------+ -+
44```
45"""
46
47from argparse import ArgumentParser
48import logging
49import struct
50
51logger = None
52
53
54def 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
66def 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
149if __name__ == "__main__":
150 main()