blob: 6058fe15302ef19d9a494f1d50019bbe5ed4d64c [file] [log] [blame]
Rustam Ismayilovf3a57022023-11-27 21:19:43 +01001# Copyright 2023-2024 Arm Limited
David Vinczeca561352023-01-27 15:39:12 +01002#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Parse and print header, TLV area and trailer information of a signed image.
19"""
20from imgtool import image
21import click
22import struct
23import yaml
24import os.path
25import sys
26
27HEADER_ITEMS = ("magic", "load_addr", "hdr_size", "protected_tlv_size",
28 "img_size", "flags", "version")
29TLV_TYPES = dict((value, key) for key, value in image.TLV_VALUES.items())
30BOOT_MAGIC = bytes([
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010031 0x77, 0xc2, 0x95, 0xf3,
32 0x60, 0xd2, 0xef, 0x7f,
33 0x35, 0x52, 0x50, 0x0f,
34 0x2c, 0xb6, 0x79, 0x80, ])
David Vinczeca561352023-01-27 15:39:12 +010035BOOT_MAGIC_2 = bytes([
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010036 0x2d, 0xe1, 0x5d, 0x29,
37 0x41, 0x0b, 0x8d, 0x77,
38 0x67, 0x9c, 0x11, 0x0f,
39 0x1f, 0x8a, ])
David Vinczeca561352023-01-27 15:39:12 +010040BOOT_MAGIC_SIZE = len(BOOT_MAGIC)
41_LINE_LENGTH = 60
42
43
44def print_tlv_records(tlv_list):
45 indent = _LINE_LENGTH // 8
46 for tlv in tlv_list:
47 print(" " * indent, "-" * 45)
48 tlv_type, tlv_length, tlv_data = tlv.keys()
49
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010050 if tlv[tlv_type] in TLV_TYPES:
51 print(" " * indent, "{}: {} ({})".format(
David Vinczeca561352023-01-27 15:39:12 +010052 tlv_type, TLV_TYPES[tlv[tlv_type]], hex(tlv[tlv_type])))
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010053 else:
54 print(" " * indent, "{}: {} ({})".format(
55 tlv_type, "UNKNOWN", hex(tlv[tlv_type])))
David Vinczeca561352023-01-27 15:39:12 +010056 print(" " * indent, "{}: ".format(tlv_length), hex(tlv[tlv_length]))
57 print(" " * indent, "{}: ".format(tlv_data), end="")
58
59 for j, data in enumerate(tlv[tlv_data]):
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010060 print("{0:#04x}".format(data), end=" ")
61 if ((j + 1) % 8 == 0) and ((j + 1) != len(tlv[tlv_data])):
62 print("\n", end=" " * (indent + 7))
David Vinczeca561352023-01-27 15:39:12 +010063 print()
64
65
66def dump_imginfo(imgfile, outfile=None, silent=False):
Rustam Ismayilovf3a57022023-11-27 21:19:43 +010067 """Parse a signed image binary and print/save the available information."""
68 trailer_magic = None
69 swap_size = 0
70 swap_info = 0
71 copy_done = 0
72 image_ok = 0
73 trailer = {}
74 key_field_len = None
75
David Vinczeca561352023-01-27 15:39:12 +010076 try:
77 with open(imgfile, "rb") as f:
78 b = f.read()
79 except FileNotFoundError:
80 raise click.UsageError("Image file not found ({})".format(imgfile))
81
82 # Parsing the image header
83 _header = struct.unpack('IIHHIIBBHI', b[:28])
84 # Image version consists of the last 4 item ('BBHI')
85 _version = _header[-4:]
86 header = {}
87 for i, key in enumerate(HEADER_ITEMS):
88 if key == "version":
89 header[key] = "{}.{}.{}+{}".format(*_version)
90 else:
91 header[key] = _header[i]
92
93 # Parsing the TLV area
94 tlv_area = {"tlv_hdr_prot": {},
95 "tlvs_prot": [],
96 "tlv_hdr": {},
97 "tlvs": []}
98 tlv_off = header["hdr_size"] + header["img_size"]
99 protected_tlv_size = header["protected_tlv_size"]
100
101 if protected_tlv_size != 0:
102 _tlv_prot_head = struct.unpack(
103 'HH',
104 b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)])
105 tlv_area["tlv_hdr_prot"]["magic"] = _tlv_prot_head[0]
106 tlv_area["tlv_hdr_prot"]["tlv_tot"] = _tlv_prot_head[1]
107 tlv_end = tlv_off + tlv_area["tlv_hdr_prot"]["tlv_tot"]
108 tlv_off += image.TLV_INFO_SIZE
109
110 # Iterating through the protected TLV area
111 while tlv_off < tlv_end:
112 tlv_type, tlv_len = struct.unpack(
113 'HH',
114 b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)])
115 tlv_off += image.TLV_INFO_SIZE
116 tlv_data = b[tlv_off:(tlv_off + tlv_len)]
117 tlv_area["tlvs_prot"].append(
118 {"type": tlv_type, "len": tlv_len, "data": tlv_data})
119 tlv_off += tlv_len
120
121 _tlv_head = struct.unpack('HH', b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)])
122 tlv_area["tlv_hdr"]["magic"] = _tlv_head[0]
123 tlv_area["tlv_hdr"]["tlv_tot"] = _tlv_head[1]
124
125 tlv_end = tlv_off + tlv_area["tlv_hdr"]["tlv_tot"]
126 tlv_off += image.TLV_INFO_SIZE
127
128 # Iterating through the TLV area
129 while tlv_off < tlv_end:
130 tlv_type, tlv_len = struct.unpack(
131 'HH',
132 b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)])
133 tlv_off += image.TLV_INFO_SIZE
134 tlv_data = b[tlv_off:(tlv_off + tlv_len)]
135 tlv_area["tlvs"].append(
136 {"type": tlv_type, "len": tlv_len, "data": tlv_data})
137 tlv_off += tlv_len
138
139 _img_pad_size = len(b) - tlv_end
140
141 if _img_pad_size:
142 # Parsing the image trailer
David Vinczeca561352023-01-27 15:39:12 +0100143 trailer_off = -BOOT_MAGIC_SIZE
144 trailer_magic = b[trailer_off:]
145 trailer["magic"] = trailer_magic
146 max_align = None
147 if trailer_magic == BOOT_MAGIC:
148 # The maximum supported write alignment is the default 8 Bytes
149 max_align = 8
150 elif trailer_magic[-len(BOOT_MAGIC_2):] == BOOT_MAGIC_2:
151 # The alignment value is encoded in the magic field
Rustam Ismayilovf3a57022023-11-27 21:19:43 +0100152 max_align = int.from_bytes(trailer_magic[:2], "little")
David Vinczeca561352023-01-27 15:39:12 +0100153 else:
154 # Invalid magic: the rest of the image trailer cannot be processed.
155 print("Warning: the trailer magic value is invalid!")
156
157 if max_align is not None:
158 if max_align > BOOT_MAGIC_SIZE:
159 trailer_off -= max_align - BOOT_MAGIC_SIZE
160 # Parsing rest of the trailer fields
161 trailer_off -= max_align
162 image_ok = b[trailer_off]
163 trailer["image_ok"] = image_ok
164
165 trailer_off -= max_align
166 copy_done = b[trailer_off]
167 trailer["copy_done"] = copy_done
168
169 trailer_off -= max_align
170 swap_info = b[trailer_off]
171 trailer["swap_info"] = swap_info
172
173 trailer_off -= max_align
174 swap_size = int.from_bytes(b[trailer_off:(trailer_off + 4)],
175 "little")
176 trailer["swap_size"] = swap_size
177
178 # Encryption key 0/1
David Vinczeca561352023-01-27 15:39:12 +0100179 if ((header["flags"] & image.IMAGE_F["ENCRYPTED_AES128"]) or
180 (header["flags"] & image.IMAGE_F["ENCRYPTED_AES256"])):
181 # The image is encrypted
182 # Estimated value of key_field_len is correct if
183 # BOOT_SWAP_SAVE_ENCTLV is unset
184 key_field_len = image.align_up(16, max_align) * 2
185
186 # Generating output yaml file
187 if outfile is not None:
188 imgdata = {"header": header,
189 "tlv_area": tlv_area,
190 "trailer": trailer}
191 with open(outfile, "w") as outf:
192 # sort_keys - from pyyaml 5.1
193 yaml.dump(imgdata, outf, sort_keys=False)
194
195###############################################################################
196
197 if silent:
198 sys.exit(0)
199
200 print("Printing content of signed image:", os.path.basename(imgfile), "\n")
201
202 # Image header
203 str1 = "#### Image header (offset: 0x0) "
204 str2 = "#" * (_LINE_LENGTH - len(str1))
205 print(str1 + str2)
206 for key, value in header.items():
207 if key == "flags":
208 if not value:
209 flag_string = hex(value)
210 else:
211 flag_string = ""
212 for flag in image.IMAGE_F.keys():
Rustam Ismayilovf3a57022023-11-27 21:19:43 +0100213 if value & image.IMAGE_F[flag]:
David Vinczeca561352023-01-27 15:39:12 +0100214 if flag_string:
215 flag_string += ("\n" + (" " * 20))
216 flag_string += "{} ({})".format(
217 flag, hex(image.IMAGE_F[flag]))
218 value = flag_string
219
220 if type(value) != str:
221 value = hex(value)
Rustam Ismayilovf3a57022023-11-27 21:19:43 +0100222 print(key, ":", " " * (19 - len(key)), value, sep="")
David Vinczeca561352023-01-27 15:39:12 +0100223 print("#" * _LINE_LENGTH)
224
225 # Image payload
226 _sectionoff = header["hdr_size"]
227 sepc = " "
228 str1 = "#### Payload (offset: {}) ".format(hex(_sectionoff))
229 str2 = "#" * (_LINE_LENGTH - len(str1))
230 print(str1 + str2)
231 print("|", sepc * (_LINE_LENGTH - 2), "|", sep="")
232 str1 = "FW image (size: {} Bytes)".format(hex(header["img_size"]))
233 numc = (_LINE_LENGTH - len(str1)) // 2
234 str2 = "|" + (sepc * (numc - 1))
235 str3 = sepc * (_LINE_LENGTH - len(str2) - len(str1) - 1) + "|"
236 print(str2, str1, str3, sep="")
237 print("|", sepc * (_LINE_LENGTH - 2), "|", sep="")
238 print("#" * _LINE_LENGTH)
239
240 # TLV area
241 _sectionoff += header["img_size"]
242 if protected_tlv_size != 0:
243 # Protected TLV area
244 str1 = "#### Protected TLV area (offset: {}) ".format(hex(_sectionoff))
245 str2 = "#" * (_LINE_LENGTH - len(str1))
246 print(str1 + str2)
247 print("magic: ", hex(tlv_area["tlv_hdr_prot"]["magic"]))
248 print("area size:", hex(tlv_area["tlv_hdr_prot"]["tlv_tot"]))
249 print_tlv_records(tlv_area["tlvs_prot"])
250 print("#" * _LINE_LENGTH)
251
252 _sectionoff += protected_tlv_size
253 str1 = "#### TLV area (offset: {}) ".format(hex(_sectionoff))
254 str2 = "#" * (_LINE_LENGTH - len(str1))
255 print(str1 + str2)
256 print("magic: ", hex(tlv_area["tlv_hdr"]["magic"]))
257 print("area size:", hex(tlv_area["tlv_hdr"]["tlv_tot"]))
258 print_tlv_records(tlv_area["tlvs"])
259 print("#" * _LINE_LENGTH)
260
261 if _img_pad_size:
262 _sectionoff += tlv_area["tlv_hdr"]["tlv_tot"]
263 _erased_val = b[_sectionoff]
264 str1 = "#### Image padding (offset: {}) ".format(hex(_sectionoff))
265 str2 = "#" * (_LINE_LENGTH - len(str1))
266 print(str1 + str2)
267 print("|", sepc * (_LINE_LENGTH - 2), "|", sep="")
268 str1 = "padding ({})".format(hex(_erased_val))
269 numc = (_LINE_LENGTH - len(str1)) // 2
270 str2 = "|" + (sepc * (numc - 1))
271 str3 = sepc * (_LINE_LENGTH - len(str2) - len(str1) - 1) + "|"
272 print(str2, str1, str3, sep="")
273 print("|", sepc * (_LINE_LENGTH - 2), "|", sep="")
274 print("#" * _LINE_LENGTH)
275
276 # Image trailer
277 str1 = "#### Image trailer (offset: unknown) "
278 str2 = "#" * (_LINE_LENGTH - len(str1))
279 print(str1 + str2)
280 print("(Note: some field may not be used, \n"
281 " depending on the update strategy)\n")
282
283 print("swap status: (len: unknown)")
284 if key_field_len is not None:
285 print("enc. keys: (len: {}, if BOOT_SWAP_SAVE_ENCTLV is unset)"
286 .format(hex(key_field_len)))
287 print("swap size: ", hex(swap_size))
288 print("swap_info: ", hex(swap_info))
289 print("copy_done: ", hex(copy_done))
290 print("image_ok: ", hex(image_ok))
291 print("boot magic: ", end="")
292 for i in range(BOOT_MAGIC_SIZE):
293 print("{0:#04x}".format(trailer_magic[i]), end=" ")
Rustam Ismayilovf3a57022023-11-27 21:19:43 +0100294 if i == (BOOT_MAGIC_SIZE / 2 - 1):
David Vinczeca561352023-01-27 15:39:12 +0100295 print("\n", end=" ")
296 print()
297
298 str1 = "#### End of Image "
299 str2 = "#" * (_LINE_LENGTH - len(str1))
300 print(str1 + str2)