1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0+ 3# Copyright 2021 Google LLC 4# Written by Simon Glass <sjg@chromium.org> 5 6"""Support for ARM's Firmware Image Package (FIP) format 7 8FIP is a format similar to FMAP[1] but with fewer features and an obscure UUID 9instead of the region name. 10 11It consists of a header and a table of entries, each pointing to a place in the 12firmware image where something can be found. 13 14[1] https://chromium.googlesource.com/chromiumos/third_party/flashmap/+/refs/heads/master/lib/fmap.h 15 16If ATF updates, run this program to update the FIT_TYPE_LIST. 17 18ARM Trusted Firmware is available at: 19 20https://github.com/ARM-software/arm-trusted-firmware.git 21""" 22 23from argparse import ArgumentParser 24import collections 25import io 26import os 27import re 28import struct 29import sys 30from uuid import UUID 31 32OUR_FILE = os.path.realpath(__file__) 33OUR_PATH = os.path.dirname(OUR_FILE) 34 35# Bring in the patman and dtoc libraries (but don't override the first path 36# in PYTHONPATH) 37sys.path.insert(2, os.path.join(OUR_PATH, '..')) 38 39# pylint: disable=C0413 40from u_boot_pylib import command 41from u_boot_pylib import tools 42 43# The TOC header, at the start of the FIP 44HEADER_FORMAT = '<IIQ' 45HEADER_LEN = 0x10 46HEADER_MAGIC = 0xaA640001 47HEADER_SERIAL = 0x12345678 48 49# The entry header (a table of these comes after the TOC header) 50UUID_LEN = 16 51ENTRY_FORMAT = f'<{UUID_LEN}sQQQ' 52ENTRY_SIZE = 0x28 53 54HEADER_NAMES = ( 55 'name', 56 'serial', 57 'flags', 58) 59 60ENTRY_NAMES = ( 61 'uuid', 62 'offset', 63 'size', 64 'flags', 65) 66 67# Set to True to enable output from running fiptool for debugging 68VERBOSE = False 69 70# Use a class so we can convert the bytes, making the table more readable 71# pylint: disable=R0903 72class FipType: 73 """A FIP entry type that we understand""" 74 def __init__(self, name, desc, uuid_bytes): 75 """Create up a new type 76 77 Args: 78 name (str): Short name for the type 79 desc (str): Longer description for the type 80 uuid_bytes (bytes): List of 16 bytes for the UUID 81 """ 82 self.name = name 83 self.desc = desc 84 self.uuid = bytes(uuid_bytes) 85 86# This is taken from tbbr_config.c in ARM Trusted Firmware 87FIP_TYPE_LIST = [ 88 # ToC Entry UUIDs 89 FipType('scp-fwu-cfg', 'SCP Firmware Updater Configuration FWU SCP_BL2U', 90 [0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, 91 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), 92 FipType('ap-fwu-cfg', 'AP Firmware Updater Configuration BL2U', 93 [0x60, 0xb3, 0xeb, 0x37, 0xc1, 0xe5, 0xea, 0x41, 94 0x9d, 0xf3, 0x19, 0xed, 0xa1, 0x1f, 0x68, 0x01]), 95 FipType('fwu', 'Firmware Updater NS_BL2U', 96 [0x4f, 0x51, 0x1d, 0x11, 0x2b, 0xe5, 0x4e, 0x49, 97 0xb4, 0xc5, 0x83, 0xc2, 0xf7, 0x15, 0x84, 0x0a]), 98 FipType('fwu-cert', 'Non-Trusted Firmware Updater certificate', 99 [0x71, 0x40, 0x8a, 0xb2, 0x18, 0xd6, 0x87, 0x4c, 100 0x8b, 0x2e, 0xc6, 0xdc, 0xcd, 0x50, 0xf0, 0x96]), 101 FipType('tb-fw', 'Trusted Boot Firmware BL2', 102 [0x5f, 0xf9, 0xec, 0x0b, 0x4d, 0x22, 0x3e, 0x4d, 103 0xa5, 0x44, 0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a]), 104 FipType('scp-fw', 'SCP Firmware SCP_BL2', 105 [0x97, 0x66, 0xfd, 0x3d, 0x89, 0xbe, 0xe8, 0x49, 106 0xae, 0x5d, 0x78, 0xa1, 0x40, 0x60, 0x82, 0x13]), 107 FipType('soc-fw', 'EL3 Runtime Firmware BL31', 108 [0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, 109 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x00]), 110 FipType('tos-fw', 'Secure Payload BL32 (Trusted OS)', 111 [0x05, 0xd0, 0xe1, 0x89, 0x53, 0xdc, 0x13, 0x47, 112 0x8d, 0x2b, 0x50, 0x0a, 0x4b, 0x7a, 0x3e, 0x38]), 113 FipType('tos-fw-extra1', 'Secure Payload BL32 Extra1 (Trusted OS Extra1)', 114 [0x0b, 0x70, 0xc2, 0x9b, 0x2a, 0x5a, 0x78, 0x40, 115 0x9f, 0x65, 0x0a, 0x56, 0x82, 0x73, 0x82, 0x88]), 116 FipType('tos-fw-extra2', 'Secure Payload BL32 Extra2 (Trusted OS Extra2)', 117 [0x8e, 0xa8, 0x7b, 0xb1, 0xcf, 0xa2, 0x3f, 0x4d, 118 0x85, 0xfd, 0xe7, 0xbb, 0xa5, 0x02, 0x20, 0xd9]), 119 FipType('nt-fw', 'Non-Trusted Firmware BL33', 120 [0xd6, 0xd0, 0xee, 0xa7, 0xfc, 0xea, 0xd5, 0x4b, 121 0x97, 0x82, 0x99, 0x34, 0xf2, 0x34, 0xb6, 0xe4]), 122 FipType('rmm-fw', 'Realm Monitor Management Firmware', 123 [0x6c, 0x07, 0x62, 0xa6, 0x12, 0xf2, 0x4b, 0x56, 124 0x92, 0xcb, 0xba, 0x8f, 0x63, 0x36, 0x06, 0xd9]), 125 # Key certificates 126 FipType('rot-cert', 'Root Of Trust key certificate', 127 [0x86, 0x2d, 0x1d, 0x72, 0xf8, 0x60, 0xe4, 0x11, 128 0x92, 0x0b, 0x8b, 0xe7, 0x62, 0x16, 0x0f, 0x24]), 129 FipType('trusted-key-cert', 'Trusted key certificate', 130 [0x82, 0x7e, 0xe8, 0x90, 0xf8, 0x60, 0xe4, 0x11, 131 0xa1, 0xb4, 0x77, 0x7a, 0x21, 0xb4, 0xf9, 0x4c]), 132 FipType('scp-fw-key-cert', 'SCP Firmware key certificate', 133 [0x02, 0x42, 0x21, 0xa1, 0xf8, 0x60, 0xe4, 0x11, 134 0x8d, 0x9b, 0xf3, 0x3c, 0x0e, 0x15, 0xa0, 0x14]), 135 FipType('soc-fw-key-cert', 'SoC Firmware key certificate', 136 [0x8a, 0xb8, 0xbe, 0xcc, 0xf9, 0x60, 0xe4, 0x11, 137 0x9a, 0xd0, 0xeb, 0x48, 0x22, 0xd8, 0xdc, 0xf8]), 138 FipType('tos-fw-key-cert', 'Trusted OS Firmware key certificate', 139 [0x94, 0x77, 0xd6, 0x03, 0xfb, 0x60, 0xe4, 0x11, 140 0x85, 0xdd, 0xb7, 0x10, 0x5b, 0x8c, 0xee, 0x04]), 141 FipType('nt-fw-key-cert', 'Non-Trusted Firmware key certificate', 142 [0x8a, 0xd5, 0x83, 0x2a, 0xfb, 0x60, 0xe4, 0x11, 143 0x8a, 0xaf, 0xdf, 0x30, 0xbb, 0xc4, 0x98, 0x59]), 144 # Content certificates 145 FipType('tb-fw-cert', 'Trusted Boot Firmware BL2 certificate', 146 [0xd6, 0xe2, 0x69, 0xea, 0x5d, 0x63, 0xe4, 0x11, 147 0x8d, 0x8c, 0x9f, 0xba, 0xbe, 0x99, 0x56, 0xa5]), 148 FipType('scp-fw-cert', 'SCP Firmware content certificate', 149 [0x44, 0xbe, 0x6f, 0x04, 0x5e, 0x63, 0xe4, 0x11, 150 0xb2, 0x8b, 0x73, 0xd8, 0xea, 0xae, 0x96, 0x56]), 151 FipType('soc-fw-cert', 'SoC Firmware content certificate', 152 [0xe2, 0xb2, 0x0c, 0x20, 0x5e, 0x63, 0xe4, 0x11, 153 0x9c, 0xe8, 0xab, 0xcc, 0xf9, 0x2b, 0xb6, 0x66]), 154 FipType('tos-fw-cert', 'Trusted OS Firmware content certificate', 155 [0xa4, 0x9f, 0x44, 0x11, 0x5e, 0x63, 0xe4, 0x11, 156 0x87, 0x28, 0x3f, 0x05, 0x72, 0x2a, 0xf3, 0x3d]), 157 FipType('nt-fw-cert', 'Non-Trusted Firmware content certificate', 158 [0x8e, 0xc4, 0xc1, 0xf3, 0x5d, 0x63, 0xe4, 0x11, 159 0xa7, 0xa9, 0x87, 0xee, 0x40, 0xb2, 0x3f, 0xa7]), 160 FipType('sip-sp-cert', 'SiP owned Secure Partition content certificate', 161 [0x77, 0x6d, 0xfd, 0x44, 0x86, 0x97, 0x4c, 0x3b, 162 0x91, 0xeb, 0xc1, 0x3e, 0x02, 0x5a, 0x2a, 0x6f]), 163 FipType('plat-sp-cert', 'Platform owned Secure Partition content certificate', 164 [0xdd, 0xcb, 0xbf, 0x4a, 0xca, 0xd6, 0x11, 0xea, 165 0x87, 0xd0, 0x02, 0x42, 0xac, 0x13, 0x00, 0x03]), 166 # Dynamic configs 167 FipType('hw-config', 'HW_CONFIG', 168 [0x08, 0xb8, 0xf1, 0xd9, 0xc9, 0xcf, 0x93, 0x49, 169 0xa9, 0x62, 0x6f, 0xbc, 0x6b, 0x72, 0x65, 0xcc]), 170 FipType('tb-fw-config', 'TB_FW_CONFIG', 171 [0x6c, 0x04, 0x58, 0xff, 0xaf, 0x6b, 0x7d, 0x4f, 172 0x82, 0xed, 0xaa, 0x27, 0xbc, 0x69, 0xbf, 0xd2]), 173 FipType('soc-fw-config', 'SOC_FW_CONFIG', 174 [0x99, 0x79, 0x81, 0x4b, 0x03, 0x76, 0xfb, 0x46, 175 0x8c, 0x8e, 0x8d, 0x26, 0x7f, 0x78, 0x59, 0xe0]), 176 FipType('tos-fw-config', 'TOS_FW_CONFIG', 177 [0x26, 0x25, 0x7c, 0x1a, 0xdb, 0xc6, 0x7f, 0x47, 178 0x8d, 0x96, 0xc4, 0xc4, 0xb0, 0x24, 0x80, 0x21]), 179 FipType('nt-fw-config', 'NT_FW_CONFIG', 180 [0x28, 0xda, 0x98, 0x15, 0x93, 0xe8, 0x7e, 0x44, 181 0xac, 0x66, 0x1a, 0xaf, 0x80, 0x15, 0x50, 0xf9]), 182 FipType('fw-config', 'FW_CONFIG', 183 [0x58, 0x07, 0xe1, 0x6a, 0x84, 0x59, 0x47, 0xbe, 184 0x8e, 0xd5, 0x64, 0x8e, 0x8d, 0xdd, 0xab, 0x0e]), 185 ] # end 186 187FIP_TYPES = {ftype.name: ftype for ftype in FIP_TYPE_LIST} 188 189 190def get_type_uuid(fip_type_or_uuid): 191 """get_type_uuid() - Convert a type or uuid into both 192 193 This always returns a UUID, but may not return a type since it does not do 194 the reverse lookup. 195 196 Args: 197 fip_type_or_uuid (str or bytes): Either a string containing the name of 198 an entry (e.g. 'soc-fw') or a bytes(16) containing the UUID 199 200 Returns: 201 tuple: 202 str: fip type (None if not known) 203 bytes(16): uuid 204 205 Raises: 206 ValueError: An unknown type was requested 207 """ 208 if isinstance(fip_type_or_uuid, str): 209 fip_type = fip_type_or_uuid 210 lookup = FIP_TYPES.get(fip_type) 211 if not lookup: 212 raise ValueError(f"Unknown FIP entry type '{fip_type}'") 213 uuid = lookup.uuid 214 else: 215 fip_type = None 216 uuid = fip_type_or_uuid 217 return fip_type, uuid 218 219 220# pylint: disable=R0903 221class FipHeader: 222 """Class to represent a FIP header""" 223 def __init__(self, name, serial, flags): 224 """Set up a new header object 225 226 Args: 227 name (str): Name, i.e. HEADER_MAGIC 228 serial (str): Serial value, i.e. HEADER_SERIAL 229 flags (int64): Flags value 230 """ 231 self.name = name 232 self.serial = serial 233 self.flags = flags 234 235 236# pylint: disable=R0903 237class FipEntry: 238 """Class to represent a single FIP entry 239 240 This is used to hold the information about an entry, including its contents. 241 Use the get_data() method to obtain the raw output for writing to the FIP 242 file. 243 """ 244 def __init__(self, uuid, offset, size, flags): 245 self.uuid = uuid 246 self.offset = offset 247 self.size = size 248 self.flags = flags 249 self.fip_type = None 250 self.data = None 251 self.valid = uuid != tools.get_bytes(0, UUID_LEN) 252 if self.valid: 253 # Look up the friendly name 254 matches = {val for (key, val) in FIP_TYPES.items() 255 if val.uuid == uuid} 256 if len(matches) == 1: 257 self.fip_type = matches.pop().name 258 259 @classmethod 260 def from_type(cls, fip_type_or_uuid, data, flags): 261 """Create a FipEntry from a type name 262 263 Args: 264 cls (class): This class 265 fip_type_or_uuid (str or bytes): Name of the type to create, or 266 bytes(16) uuid 267 data (bytes): Contents of entry 268 flags (int64): Flags value 269 270 Returns: 271 FipEntry: Created 241 272 """ 273 fip_type, uuid = get_type_uuid(fip_type_or_uuid) 274 fent = FipEntry(uuid, None, len(data), flags) 275 fent.fip_type = fip_type 276 fent.data = data 277 return fent 278 279 280def decode_fip(data): 281 """Decode a FIP into a header and list of FIP entries 282 283 Args: 284 data (bytes): Data block containing the FMAP 285 286 Returns: 287 Tuple: 288 header: FipHeader object 289 List of FipArea objects 290 """ 291 fields = list(struct.unpack(HEADER_FORMAT, data[:HEADER_LEN])) 292 header = FipHeader(*fields) 293 fents = [] 294 pos = HEADER_LEN 295 while True: 296 fields = list(struct.unpack(ENTRY_FORMAT, data[pos:pos + ENTRY_SIZE])) 297 fent = FipEntry(*fields) 298 if not fent.valid: 299 break 300 fent.data = data[fent.offset:fent.offset + fent.size] 301 fents.append(fent) 302 pos += ENTRY_SIZE 303 return header, fents 304 305 306class FipWriter: 307 """Class to handle writing a ARM Trusted Firmware's Firmware Image Package 308 309 Usage is something like: 310 311 fip = FipWriter(size) 312 fip.add_entry('scp-fwu-cfg', tools.read_file('something.bin')) 313 ... 314 data = cbw.get_data() 315 316 Attributes: 317 """ 318 def __init__(self, flags, align): 319 self._fip_entries = [] 320 self._flags = flags 321 self._align = align 322 323 def add_entry(self, fip_type, data, flags): 324 """Add a new entry to the FIP 325 326 Args: 327 fip_type (str): Type to add, e.g. 'tos-fw-config' 328 data (bytes): Contents of entry 329 flags (int64): Entry flags 330 331 Returns: 332 FipEntry: entry that was added 333 """ 334 fent = FipEntry.from_type(fip_type, data, flags) 335 self._fip_entries.append(fent) 336 return fent 337 338 def get_data(self): 339 """Obtain the full contents of the FIP 340 341 Thhis builds the FIP with headers and all required FIP entries. 342 343 Returns: 344 bytes: data resulting from building the FIP 345 """ 346 buf = io.BytesIO() 347 hdr = struct.pack(HEADER_FORMAT, HEADER_MAGIC, HEADER_SERIAL, 348 self._flags) 349 buf.write(hdr) 350 351 # Calculate the position fo the first entry 352 offset = len(hdr) 353 offset += len(self._fip_entries) * ENTRY_SIZE 354 offset += ENTRY_SIZE # terminating entry 355 356 for fent in self._fip_entries: 357 offset = tools.align(offset, self._align) 358 fent.offset = offset 359 offset += fent.size 360 361 # Write out the TOC 362 for fent in self._fip_entries: 363 hdr = struct.pack(ENTRY_FORMAT, fent.uuid, fent.offset, fent.size, 364 fent.flags) 365 buf.write(hdr) 366 367 # Write out the entries 368 for fent in self._fip_entries: 369 buf.seek(fent.offset) 370 buf.write(fent.data) 371 372 return buf.getvalue() 373 374 375class FipReader(): 376 """Class to handle reading a Firmware Image Package (FIP) 377 378 Usage is something like: 379 fip = fip_util.FipReader(data) 380 fent = fip.get_entry('fwu') 381 self.WriteFile('ufwu.bin', fent.data) 382 blob = fip.get_entry( 383 bytes([0xe3, 0xb7, 0x8d, 0x9e, 0x4a, 0x64, 0x11, 0xec, 384 0xb4, 0x5c, 0xfb, 0xa2, 0xb9, 0xb4, 0x97, 0x88])) 385 self.WriteFile('blob.bin', blob.data) 386 """ 387 def __init__(self, data, read=True): 388 """Set up a new FitReader 389 390 Args: 391 data (bytes): data to read 392 read (bool): True to read the data now 393 """ 394 self.fents = collections.OrderedDict() 395 self.data = data 396 if read: 397 self.read() 398 399 def read(self): 400 """Read all the files in the FIP and add them to self.files""" 401 self.header, self.fents = decode_fip(self.data) 402 403 def get_entry(self, fip_type_or_uuid): 404 """get_entry() - Find an entry by type or UUID 405 406 Args: 407 fip_type_or_uuid (str or bytes): Name of the type to create, or 408 bytes(16) uuid 409 410 Returns: 411 FipEntry: if found 412 413 Raises: 414 ValueError: entry type not found 415 """ 416 fip_type, uuid = get_type_uuid(fip_type_or_uuid) 417 for fent in self.fents: 418 if fent.uuid == uuid: 419 return fent 420 label = fip_type 421 if not label: 422 label = UUID(bytes=uuid) 423 raise ValueError(f"Cannot find FIP entry '{label}'") 424 425 426def parse_macros(srcdir): 427 """parse_macros: Parse the firmware_image_package.h file 428 429 Args: 430 srcdir (str): 'arm-trusted-firmware' source directory 431 432 Returns: 433 dict: 434 key: UUID macro name, e.g. 'UUID_TRUSTED_FWU_CERT' 435 value: list: 436 file comment, e.g. 'ToC Entry UUIDs' 437 macro name, e.g. 'UUID_TRUSTED_FWU_CERT' 438 uuid as bytes(16) 439 440 Raises: 441 ValueError: a line cannot be parsed 442 """ 443 re_uuid = re.compile('0x[0-9a-fA-F]{2}') 444 re_comment = re.compile(r'^/\* (.*) \*/$') 445 fname = os.path.join(srcdir, 'include/tools_share/firmware_image_package.h') 446 data = tools.read_file(fname, binary=False) 447 macros = collections.OrderedDict() 448 comment = None 449 for linenum, line in enumerate(data.splitlines()): 450 if line.startswith('/*'): 451 mat = re_comment.match(line) 452 if mat: 453 comment = mat.group(1) 454 else: 455 # Example: #define UUID_TOS_FW_CONFIG \ 456 if 'UUID' in line: 457 macro = line.split()[1] 458 elif '{{' in line: 459 mat = re_uuid.findall(line) 460 if not mat or len(mat) != 16: 461 raise ValueError( 462 f'{fname}: Cannot parse UUID line {linenum + 1}: Got matches: {mat}') 463 464 uuid = bytes([int(val, 16) for val in mat]) 465 macros[macro] = comment, macro, uuid 466 if not macros: 467 raise ValueError(f'{fname}: Cannot parse file') 468 return macros 469 470 471def parse_names(srcdir): 472 """parse_names: Parse the tbbr_config.c file 473 474 Args: 475 srcdir (str): 'arm-trusted-firmware' source directory 476 477 Returns: 478 tuple: dict of entries: 479 key: UUID macro, e.g. 'UUID_NON_TRUSTED_FIRMWARE_BL33' 480 tuple: entry information 481 Description of entry, e.g. 'Non-Trusted Firmware BL33' 482 UUID macro, e.g. 'UUID_NON_TRUSTED_FIRMWARE_BL33' 483 Name of entry, e.g. 'nt-fw' 484 485 Raises: 486 ValueError: the file cannot be parsed 487 """ 488 # Extract the .name, .uuid and .cmdline_name values 489 re_data = re.compile(r'\.name = "([^"]*)",\s*\.uuid = (UUID_\w*),\s*\.cmdline_name = "([^"]+)"', 490 re.S) 491 fname = os.path.join(srcdir, 'tools/fiptool/tbbr_config.c') 492 data = tools.read_file(fname, binary=False) 493 494 # Example entry: 495 # { 496 # .name = "Secure Payload BL32 Extra2 (Trusted OS Extra2)", 497 # .uuid = UUID_SECURE_PAYLOAD_BL32_EXTRA2, 498 # .cmdline_name = "tos-fw-extra2" 499 # }, 500 mat = re_data.findall(data) 501 if not mat: 502 raise ValueError(f'{fname}: Cannot parse file') 503 names = {uuid: (desc, uuid, name) for desc, uuid, name in mat} 504 return names 505 506 507def create_code_output(macros, names): 508 """create_code_output() - Create the new version of this Python file 509 510 Args: 511 macros (dict): 512 key (str): UUID macro name, e.g. 'UUID_TRUSTED_FWU_CERT' 513 value: list: 514 file comment, e.g. 'ToC Entry UUIDs' 515 macro name, e.g. 'UUID_TRUSTED_FWU_CERT' 516 uuid as bytes(16) 517 518 names (dict): list of entries, each 519 tuple: entry information 520 Description of entry, e.g. 'Non-Trusted Firmware BL33' 521 UUID macro, e.g. 'UUID_NON_TRUSTED_FIRMWARE_BL33' 522 Name of entry, e.g. 'nt-fw' 523 524 Returns: 525 str: Table of FipType() entries 526 """ 527 def _to_hex_list(data): 528 """Convert bytes into C code 529 530 Args: 531 bytes to convert 532 533 Returns: 534 str: in the format '0x12, 0x34, 0x56...' 535 """ 536 # Use 0x instead of %# since the latter ignores the 0 modifier in 537 # Python 3.8.10 538 return ', '.join(['0x%02x' % byte for byte in data]) 539 540 out = '' 541 last_comment = None 542 for comment, macro, uuid in macros.values(): 543 name_entry = names.get(macro) 544 if not name_entry: 545 print(f"Warning: UUID '{macro}' is not mentioned in tbbr_config.c file") 546 continue 547 desc, _, name = name_entry 548 if last_comment != comment: 549 out += f' # {comment}\n' 550 last_comment = comment 551 out += """ FipType('%s', '%s', 552 [%s, 553 %s]), 554""" % (name, desc, _to_hex_list(uuid[:8]), _to_hex_list(uuid[8:])) 555 return out 556 557 558def parse_atf_source(srcdir, dstfile, oldfile): 559 """parse_atf_source(): Parse the ATF source tree and update this file 560 561 Args: 562 srcdir (str): Path to 'arm-trusted-firmware' directory. Get this from: 563 https://github.com/ARM-software/arm-trusted-firmware.git 564 dstfile (str): File to write new code to, if an update is needed 565 oldfile (str): Python source file to compare against 566 567 Raises: 568 ValueError: srcdir readme.rst is missing or the first line does not 569 match what is expected 570 """ 571 # We expect a readme file 572 readme_fname = os.path.join(srcdir, 'readme.rst') 573 if not os.path.exists(readme_fname): 574 raise ValueError( 575 f"Expected file '{readme_fname}' - try using -s to specify the " 576 'arm-trusted-firmware directory') 577 readme = tools.read_file(readme_fname, binary=False) 578 first_line = 'Trusted Firmware-A' 579 if readme.splitlines()[0] != first_line: 580 raise ValueError(f"'{readme_fname}' does not start with '{first_line}'") 581 macros = parse_macros(srcdir) 582 names = parse_names(srcdir) 583 output = create_code_output(macros, names) 584 orig = tools.read_file(oldfile, binary=False) 585 re_fip_list = re.compile(r'(.*FIP_TYPE_LIST = \[).*?( ] # end.*)', re.S) 586 mat = re_fip_list.match(orig) 587 new_code = mat.group(1) + '\n' + output + mat.group(2) if mat else output 588 if new_code == orig: 589 print(f"Existing code in '{oldfile}' is up-to-date") 590 else: 591 tools.write_file(dstfile, new_code, binary=False) 592 print(f'Needs update, try:\n\tmeld {dstfile} {oldfile}') 593 594 595def main(argv, oldfile): 596 """Main program for this tool 597 598 Args: 599 argv (list): List of str command-line arguments 600 oldfile (str): Python source file to compare against 601 602 Returns: 603 int: 0 (exit code) 604 """ 605 parser = ArgumentParser(epilog='''Creates an updated version of this code, 606with a table of FIP-entry types parsed from the arm-trusted-firmware source 607directory''') 608 parser.add_argument( 609 '-D', '--debug', action='store_true', 610 help='Enabling debugging (provides a full traceback on error)') 611 parser.add_argument( 612 '-o', '--outfile', type=str, default='fip_util.py.out', 613 help='Output file to write new fip_util.py file to') 614 parser.add_argument( 615 '-s', '--src', type=str, default='.', 616 help='Directory containing the arm-trusted-firmware source') 617 args = parser.parse_args(argv) 618 619 if not args.debug: 620 sys.tracebacklimit = 0 621 622 parse_atf_source(args.src, args.outfile, oldfile) 623 return 0 624 625 626if __name__ == "__main__": 627 sys.exit(main(sys.argv[1:], OUR_FILE)) # pragma: no cover 628