1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2016 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5 6"""Entry-type module for producing a FIT""" 7 8import libfdt 9 10from binman.entry import Entry, EntryArg 11from binman.etype.section import Entry_section 12from binman import elf 13from dtoc import fdt_util 14from dtoc.fdt import Fdt 15from u_boot_pylib import tools 16 17# Supported operations, with the fit,operation property 18OP_GEN_FDT_NODES, OP_SPLIT_ELF = range(2) 19OPERATIONS = { 20 'gen-fdt-nodes': OP_GEN_FDT_NODES, 21 'split-elf': OP_SPLIT_ELF, 22 } 23 24class Entry_fit(Entry_section): 25 26 """Flat Image Tree (FIT) 27 28 This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the 29 input provided. 30 31 Nodes for the FIT should be written out in the binman configuration just as 32 they would be in a file passed to mkimage. 33 34 For example, this creates an image containing a FIT with U-Boot SPL:: 35 36 binman { 37 fit { 38 description = "Test FIT"; 39 fit,fdt-list = "of-list"; 40 41 images { 42 kernel@1 { 43 description = "SPL"; 44 os = "u-boot"; 45 type = "rkspi"; 46 arch = "arm"; 47 compression = "none"; 48 load = <0>; 49 entry = <0>; 50 51 u-boot-spl { 52 }; 53 }; 54 }; 55 }; 56 }; 57 58 More complex setups can be created, with generated nodes, as described 59 below. 60 61 Properties (in the 'fit' node itself) 62 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 63 64 Special properties have a `fit,` prefix, indicating that they should be 65 processed but not included in the final FIT. 66 67 The top-level 'fit' node supports the following special properties: 68 69 fit,external-offset 70 Indicates that the contents of the FIT are external and provides the 71 external offset. This is passed to mkimage via the -E and -p flags. 72 73 fit,align 74 Indicates what alignment to use for the FIT and its external data, 75 and provides the alignment to use. This is passed to mkimage via 76 the -B flag. 77 78 fit,fdt-list 79 Indicates the entry argument which provides the list of device tree 80 files for the gen-fdt-nodes operation (as below). This is often 81 `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed 82 to binman. 83 84 Substitutions 85 ~~~~~~~~~~~~~ 86 87 Node names and property values support a basic string-substitution feature. 88 Available substitutions for '@' nodes (and property values) are: 89 90 SEQ: 91 Sequence number of the generated fdt (1, 2, ...) 92 NAME 93 Name of the dtb as provided (i.e. without adding '.dtb') 94 95 The `default` property, if present, will be automatically set to the name 96 if of configuration whose devicetree matches the `default-dt` entry 97 argument, e.g. with `-a default-dt=sun50i-a64-pine64-lts`. 98 99 Available substitutions for property values in these nodes are: 100 101 DEFAULT-SEQ: 102 Sequence number of the default fdt, as provided by the 'default-dt' 103 entry argument 104 105 Available operations 106 ~~~~~~~~~~~~~~~~~~~~ 107 108 You can add an operation to an '@' node to indicate which operation is 109 required:: 110 111 @fdt-SEQ { 112 fit,operation = "gen-fdt-nodes"; 113 ... 114 }; 115 116 Available operations are: 117 118 gen-fdt-nodes 119 Generate FDT nodes as above. This is the default if there is no 120 `fit,operation` property. 121 122 split-elf 123 Split an ELF file into a separate node for each segment. 124 125 Generating nodes from an FDT list (gen-fdt-nodes) 126 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 127 128 U-Boot supports creating fdt and config nodes automatically. To do this, 129 pass an `of-list` property (e.g. `-a of-list=file1 file2`). This tells 130 binman that you want to generates nodes for two files: `file1.dtb` and 131 `file2.dtb`. The `fit,fdt-list` property (see above) indicates that 132 `of-list` should be used. If the property is missing you will get an error. 133 134 Then add a 'generator node', a node with a name starting with '@':: 135 136 images { 137 @fdt-SEQ { 138 description = "fdt-NAME"; 139 type = "flat_dt"; 140 compression = "none"; 141 }; 142 }; 143 144 This tells binman to create nodes `fdt-1` and `fdt-2` for each of your two 145 files. All the properties you specify will be included in the node. This 146 node acts like a template to generate the nodes. The generator node itself 147 does not appear in the output - it is replaced with what binman generates. 148 A 'data' property is created with the contents of the FDT file. 149 150 You can create config nodes in a similar way:: 151 152 configurations { 153 default = "@config-DEFAULT-SEQ"; 154 @config-SEQ { 155 description = "NAME"; 156 firmware = "atf"; 157 loadables = "uboot"; 158 fdt = "fdt-SEQ"; 159 }; 160 }; 161 162 This tells binman to create nodes `config-1` and `config-2`, i.e. a config 163 for each of your two files. 164 165 Note that if no devicetree files are provided (with '-a of-list' as above) 166 then no nodes will be generated. 167 168 Generating nodes from an ELF file (split-elf) 169 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 170 171 This uses the node as a template to generate multiple nodes. The following 172 special properties are available: 173 174 split-elf 175 Split an ELF file into a separate node for each segment. This uses the 176 node as a template to generate multiple nodes. The following special 177 properties are available: 178 179 fit,load 180 Generates a `load = <...>` property with the load address of the 181 segment 182 183 fit,entry 184 Generates a `entry = <...>` property with the entry address of the 185 ELF. This is only produced for the first entry 186 187 fit,data 188 Generates a `data = <...>` property with the contents of the segment 189 190 fit,firmware 191 Generates a `firmware = <...>` property. Provides a list of possible 192 nodes to be used as the `firmware` property value. The first valid 193 node is picked as the firmware. Any remaining valid nodes is 194 prepended to the `loadable` property generated by `fit,loadables` 195 196 fit,loadables 197 Generates a `loadable = <...>` property with a list of the generated 198 nodes (including all nodes if this operation is used multiple times) 199 200 201 Here is an example showing ATF, TEE and a device tree all combined:: 202 203 fit { 204 description = "test-desc"; 205 #address-cells = <1>; 206 fit,fdt-list = "of-list"; 207 208 images { 209 u-boot { 210 description = "U-Boot (64-bit)"; 211 type = "standalone"; 212 os = "U-Boot"; 213 arch = "arm64"; 214 compression = "none"; 215 load = <CONFIG_TEXT_BASE>; 216 u-boot-nodtb { 217 }; 218 }; 219 @fdt-SEQ { 220 description = "fdt-NAME.dtb"; 221 type = "flat_dt"; 222 compression = "none"; 223 }; 224 @atf-SEQ { 225 fit,operation = "split-elf"; 226 description = "ARM Trusted Firmware"; 227 type = "firmware"; 228 arch = "arm64"; 229 os = "arm-trusted-firmware"; 230 compression = "none"; 231 fit,load; 232 fit,entry; 233 fit,data; 234 235 atf-bl31 { 236 }; 237 hash { 238 algo = "sha256"; 239 }; 240 }; 241 242 @tee-SEQ { 243 fit,operation = "split-elf"; 244 description = "TEE"; 245 type = "tee"; 246 arch = "arm64"; 247 os = "tee"; 248 compression = "none"; 249 fit,load; 250 fit,entry; 251 fit,data; 252 253 tee-os { 254 }; 255 hash { 256 algo = "sha256"; 257 }; 258 }; 259 }; 260 261 configurations { 262 default = "@config-DEFAULT-SEQ"; 263 @config-SEQ { 264 description = "conf-NAME.dtb"; 265 fdt = "fdt-SEQ"; 266 fit,firmware = "atf-1", "u-boot"; 267 fit,loadables; 268 }; 269 }; 270 }; 271 272 If ATF-BL31 is available, this generates a node for each segment in the 273 ELF file, for example:: 274 275 images { 276 atf-1 { 277 data = <...contents of first segment...>; 278 data-offset = <0x00000000>; 279 entry = <0x00040000>; 280 load = <0x00040000>; 281 compression = "none"; 282 os = "arm-trusted-firmware"; 283 arch = "arm64"; 284 type = "firmware"; 285 description = "ARM Trusted Firmware"; 286 hash { 287 algo = "sha256"; 288 value = <...hash of first segment...>; 289 }; 290 }; 291 atf-2 { 292 data = <...contents of second segment...>; 293 load = <0xff3b0000>; 294 compression = "none"; 295 os = "arm-trusted-firmware"; 296 arch = "arm64"; 297 type = "firmware"; 298 description = "ARM Trusted Firmware"; 299 hash { 300 algo = "sha256"; 301 value = <...hash of second segment...>; 302 }; 303 }; 304 }; 305 306 The same applies for OP-TEE if that is available. 307 308 If each binary is not available, the relevant template node (@atf-SEQ or 309 @tee-SEQ) is removed from the output. 310 311 This also generates a `config-xxx` node for each device tree in `of-list`. 312 Note that the U-Boot build system uses `-a of-list=$(CONFIG_OF_LIST)` 313 so you can use `CONFIG_OF_LIST` to define that list. In this example it is 314 set up for `firefly-rk3399` with a single device tree and the default set 315 with `-a default-dt=$(CONFIG_DEFAULT_DEVICE_TREE)`, so the resulting output 316 is:: 317 318 configurations { 319 default = "config-1"; 320 config-1 { 321 loadables = "u-boot", "atf-2", "atf-3", "tee-1", "tee-2"; 322 description = "rk3399-firefly.dtb"; 323 fdt = "fdt-1"; 324 firmware = "atf-1"; 325 }; 326 }; 327 328 U-Boot SPL can then load the firmware (ATF) and all the loadables (U-Boot 329 proper, ATF and TEE), then proceed with the boot. 330 """ 331 def __init__(self, section, etype, node): 332 """ 333 Members: 334 _fit: FIT file being built 335 _entries: dict from Entry_section: 336 key: relative path to entry Node (from the base of the FIT) 337 value: Entry_section object comprising the contents of this 338 node 339 _priv_entries: Internal copy of _entries which includes 'generator' 340 entries which are used to create the FIT, but should not be 341 processed as real entries. This is set up once we have the 342 entries 343 _loadables: List of generated split-elf nodes, each a node name 344 """ 345 super().__init__(section, etype, node) 346 self._fit = None 347 self._fit_props = {} 348 self._fdts = None 349 self.mkimage = None 350 self._priv_entries = {} 351 self._loadables = [] 352 353 def ReadNode(self): 354 super().ReadNode() 355 for pname, prop in self._node.props.items(): 356 if pname.startswith('fit,'): 357 self._fit_props[pname] = prop 358 self._fit_list_prop = self._fit_props.get('fit,fdt-list') 359 if self._fit_list_prop: 360 fdts, = self.GetEntryArgsOrProps( 361 [EntryArg(self._fit_list_prop.value, str)]) 362 if fdts is not None: 363 self._fdts = fdts.split() 364 self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', 365 str)])[0] 366 367 def _get_operation(self, base_node, node): 368 """Get the operation referenced by a subnode 369 370 Args: 371 node (Node): Subnode (of the FIT) to check 372 373 Returns: 374 int: Operation to perform 375 376 Raises: 377 ValueError: Invalid operation name 378 """ 379 oper_name = node.props.get('fit,operation') 380 if not oper_name: 381 return OP_GEN_FDT_NODES 382 oper = OPERATIONS.get(oper_name.value) 383 if oper is None: 384 self._raise_subnode(node, f"Unknown operation '{oper_name.value}'") 385 return oper 386 387 def ReadEntries(self): 388 def _add_entries(base_node, depth, node): 389 """Add entries for any nodes that need them 390 391 Args: 392 base_node: Base Node of the FIT (with 'description' property) 393 depth: Current node depth (0 is the base 'fit' node) 394 node: Current node to process 395 396 Here we only need to provide binman entries which are used to define 397 the 'data' for each image. We create an entry_Section for each. 398 """ 399 rel_path = node.path[len(base_node.path):] 400 in_images = rel_path.startswith('/images') 401 has_images = depth == 2 and in_images 402 if has_images: 403 # This node is a FIT subimage node (e.g. "/images/kernel") 404 # containing content nodes. We collect the subimage nodes and 405 # section entries for them here to merge the content subnodes 406 # together and put the merged contents in the subimage node's 407 # 'data' property later. 408 entry = Entry.Create(self, node, etype='section') 409 entry.ReadNode() 410 # The hash subnodes here are for mkimage, not binman. 411 entry.SetUpdateHash(False) 412 image_name = rel_path[len('/images/'):] 413 self._entries[image_name] = entry 414 415 for subnode in node.subnodes: 416 _add_entries(base_node, depth + 1, subnode) 417 418 _add_entries(self._node, 0, self._node) 419 420 # Keep a copy of all entries, including generator entries, since those 421 # are removed from self._entries later. 422 self._priv_entries = dict(self._entries) 423 424 def BuildSectionData(self, required): 425 """Build FIT entry contents 426 427 This adds the 'data' properties to the input ITB (Image-tree Binary) 428 then runs mkimage to process it. 429 430 Args: 431 required (bool): True if the data must be present, False if it is OK 432 to return None 433 434 Returns: 435 bytes: Contents of the section 436 """ 437 data = self._build_input() 438 uniq = self.GetUniqueName() 439 input_fname = tools.get_output_filename(f'{uniq}.itb') 440 output_fname = tools.get_output_filename(f'{uniq}.fit') 441 tools.write_file(input_fname, data) 442 tools.write_file(output_fname, data) 443 444 args = {} 445 ext_offset = self._fit_props.get('fit,external-offset') 446 if ext_offset is not None: 447 args = { 448 'external': True, 449 'pad': fdt_util.fdt32_to_cpu(ext_offset.value) 450 } 451 align = self._fit_props.get('fit,align') 452 if align is not None: 453 args.update({'align': fdt_util.fdt32_to_cpu(align.value)}) 454 if self.mkimage.run(reset_timestamp=True, output_fname=output_fname, 455 **args) is None: 456 if not self.GetAllowMissing(): 457 self.Raise("Missing tool: 'mkimage'") 458 # Bintool is missing; just use empty data as the output 459 self.record_missing_bintool(self.mkimage) 460 return tools.get_bytes(0, 1024) 461 462 return tools.read_file(output_fname) 463 464 def _raise_subnode(self, node, msg): 465 """Raise an error with a paticular FIT subnode 466 467 Args: 468 node (Node): FIT subnode containing the error 469 msg (str): Message to report 470 471 Raises: 472 ValueError, as requested 473 """ 474 rel_path = node.path[len(self._node.path) + 1:] 475 self.Raise(f"subnode '{rel_path}': {msg}") 476 477 def _build_input(self): 478 """Finish the FIT by adding the 'data' properties to it 479 480 Arguments: 481 fdt: FIT to update 482 483 Returns: 484 bytes: New fdt contents 485 """ 486 def _process_prop(pname, prop): 487 """Process special properties 488 489 Handles properties with generated values. At present the only 490 supported property is 'default', i.e. the default device tree in 491 the configurations node. 492 493 Args: 494 pname (str): Name of property 495 prop (Prop): Property to process 496 """ 497 if pname == 'default': 498 val = prop.value 499 # Handle the 'default' property 500 if val.startswith('@'): 501 if not self._fdts: 502 return 503 if not self._fit_default_dt: 504 self.Raise("Generated 'default' node requires default-dt entry argument") 505 if self._fit_default_dt not in self._fdts: 506 self.Raise( 507 f"default-dt entry argument '{self._fit_default_dt}' " 508 f"not found in fdt list: {', '.join(self._fdts)}") 509 seq = self._fdts.index(self._fit_default_dt) 510 val = val[1:].replace('DEFAULT-SEQ', str(seq + 1)) 511 fsw.property_string(pname, val) 512 return 513 elif pname.startswith('fit,'): 514 # Ignore these, which are commands for binman to process 515 return 516 elif pname in ['offset', 'size', 'image-pos']: 517 # Don't add binman's calculated properties 518 return 519 fsw.property(pname, prop.bytes) 520 521 def _process_firmware_prop(node): 522 """Process optional fit,firmware property 523 524 Picks the first valid entry for use as the firmware, remaining valid 525 entries is prepended to loadables 526 527 Args: 528 node (Node): Generator node to process 529 530 Returns: 531 firmware (str): Firmware or None 532 result (list): List of remaining loadables 533 """ 534 val = fdt_util.GetStringList(node, 'fit,firmware') 535 if val is None: 536 return None, self._loadables 537 valid_entries = list(self._loadables) 538 for name, entry in self.GetEntries().items(): 539 missing = [] 540 entry.CheckMissing(missing) 541 entry.CheckOptional(missing) 542 if not missing: 543 valid_entries.append(name) 544 firmware = None 545 result = [] 546 for name in val: 547 if name in valid_entries: 548 if not firmware: 549 firmware = name 550 elif name not in result: 551 result.append(name) 552 for name in self._loadables: 553 if name != firmware and name not in result: 554 result.append(name) 555 return firmware, result 556 557 def _gen_fdt_nodes(base_node, node, depth, in_images): 558 """Generate FDT nodes 559 560 This creates one node for each member of self._fdts using the 561 provided template. If a property value contains 'NAME' it is 562 replaced with the filename of the FDT. If a property value contains 563 SEQ it is replaced with the node sequence number, where 1 is the 564 first. 565 566 Args: 567 node (Node): Generator node to process 568 depth: Current node depth (0 is the base 'fit' node) 569 in_images: True if this is inside the 'images' node, so that 570 'data' properties should be generated 571 """ 572 if self._fdts: 573 firmware, fit_loadables = _process_firmware_prop(node) 574 # Generate nodes for each FDT 575 for seq, fdt_fname in enumerate(self._fdts): 576 node_name = node.name[1:].replace('SEQ', str(seq + 1)) 577 fname = tools.get_input_filename(fdt_fname + '.dtb') 578 with fsw.add_node(node_name): 579 for pname, prop in node.props.items(): 580 if pname == 'fit,firmware': 581 if firmware: 582 fsw.property_string('firmware', firmware) 583 elif pname == 'fit,loadables': 584 val = '\0'.join(fit_loadables) + '\0' 585 fsw.property('loadables', val.encode('utf-8')) 586 elif pname == 'fit,operation': 587 pass 588 elif pname.startswith('fit,'): 589 self._raise_subnode( 590 node, f"Unknown directive '{pname}'") 591 else: 592 val = prop.bytes.replace( 593 b'NAME', tools.to_bytes(fdt_fname)) 594 val = val.replace( 595 b'SEQ', tools.to_bytes(str(seq + 1))) 596 fsw.property(pname, val) 597 598 # Add data for 'images' nodes (but not 'config') 599 if depth == 1 and in_images: 600 fsw.property('data', tools.read_file(fname)) 601 602 for subnode in node.subnodes: 603 with fsw.add_node(subnode.name): 604 _add_node(node, depth + 1, subnode) 605 else: 606 if self._fdts is None: 607 if self._fit_list_prop: 608 self.Raise('Generator node requires ' 609 f"'{self._fit_list_prop.value}' entry argument") 610 else: 611 self.Raise("Generator node requires 'fit,fdt-list' property") 612 613 def _gen_split_elf(base_node, node, depth, segments, entry_addr): 614 """Add nodes for the ELF file, one per group of contiguous segments 615 616 Args: 617 base_node (Node): Template node from the binman definition 618 node (Node): Node to replace (in the FIT being built) 619 depth: Current node depth (0 is the base 'fit' node) 620 segments (list): list of segments, each: 621 int: Segment number (0 = first) 622 int: Start address of segment in memory 623 bytes: Contents of segment 624 entry_addr (int): entry address of ELF file 625 """ 626 for (seq, start, data) in segments: 627 node_name = node.name[1:].replace('SEQ', str(seq + 1)) 628 with fsw.add_node(node_name): 629 loadables.append(node_name) 630 for pname, prop in node.props.items(): 631 if not pname.startswith('fit,'): 632 fsw.property(pname, prop.bytes) 633 elif pname == 'fit,load': 634 fsw.property_u32('load', start) 635 elif pname == 'fit,entry': 636 if seq == 0: 637 fsw.property_u32('entry', entry_addr) 638 elif pname == 'fit,data': 639 fsw.property('data', bytes(data)) 640 elif pname != 'fit,operation': 641 self._raise_subnode( 642 node, f"Unknown directive '{pname}'") 643 644 for subnode in node.subnodes: 645 with fsw.add_node(subnode.name): 646 _add_node(node, depth + 1, subnode) 647 648 def _gen_node(base_node, node, depth, in_images, entry): 649 """Generate nodes from a template 650 651 This creates one or more nodes depending on the fit,operation being 652 used. 653 654 For OP_GEN_FDT_NODES it creates one node for each member of 655 self._fdts using the provided template. If a property value contains 656 'NAME' it is replaced with the filename of the FDT. If a property 657 value contains SEQ it is replaced with the node sequence number, 658 where 1 is the first. 659 660 For OP_SPLIT_ELF it emits one node for each section in the ELF file. 661 If the file is missing, nothing is generated. 662 663 Args: 664 base_node (Node): Base Node of the FIT (with 'description' 665 property) 666 node (Node): Generator node to process 667 depth (int): Current node depth (0 is the base 'fit' node) 668 in_images (bool): True if this is inside the 'images' node, so 669 that 'data' properties should be generated 670 entry (entry_Section): Entry for the section containing the 671 contents of this node 672 """ 673 oper = self._get_operation(base_node, node) 674 if oper == OP_GEN_FDT_NODES: 675 _gen_fdt_nodes(base_node, node, depth, in_images) 676 elif oper == OP_SPLIT_ELF: 677 # Entry_section.ObtainContents() either returns True or 678 # raises an exception. 679 data = None 680 missing_opt_list = [] 681 entry.ObtainContents() 682 entry.Pack(0) 683 entry.CheckMissing(missing_opt_list) 684 entry.CheckOptional(missing_opt_list) 685 686 # If any pieces are missing, skip this. The missing entries will 687 # show an error 688 if not missing_opt_list: 689 segs = entry.read_elf_segments() 690 if segs: 691 segments, entry_addr = segs 692 else: 693 elf_data = entry.GetData() 694 try: 695 segments, entry_addr = ( 696 elf.read_loadable_segments(elf_data)) 697 except ValueError as exc: 698 self._raise_subnode( 699 node, f'Failed to read ELF file: {str(exc)}') 700 701 _gen_split_elf(base_node, node, depth, segments, entry_addr) 702 703 def _add_node(base_node, depth, node): 704 """Add nodes to the output FIT 705 706 Args: 707 base_node (Node): Base Node of the FIT (with 'description' 708 property) 709 depth (int): Current node depth (0 is the base 'fit' node) 710 node (Node): Current node to process 711 712 There are two cases to deal with: 713 - hash and signature nodes which become part of the FIT 714 - binman entries which are used to define the 'data' for each 715 image, so don't appear in the FIT 716 """ 717 # Copy over all the relevant properties 718 for pname, prop in node.props.items(): 719 _process_prop(pname, prop) 720 721 rel_path = node.path[len(base_node.path):] 722 in_images = rel_path.startswith('/images') 723 724 has_images = depth == 2 and in_images 725 if has_images: 726 image_name = rel_path[len('/images/'):] 727 entry = self._priv_entries[image_name] 728 data = entry.GetData() 729 fsw.property('data', bytes(data)) 730 731 for subnode in node.subnodes: 732 subnode_path = f'{rel_path}/{subnode.name}' 733 if has_images and not self.IsSpecialSubnode(subnode): 734 # This subnode is a content node not meant to appear in 735 # the FIT (e.g. "/images/kernel/u-boot"), so don't call 736 # fsw.add_node() or _add_node() for it. 737 pass 738 elif self.GetImage().generate and subnode.name.startswith('@'): 739 entry = self._priv_entries.get(subnode.name) 740 _gen_node(base_node, subnode, depth, in_images, entry) 741 # This is a generator (template) entry, so remove it from 742 # the list of entries used by PackEntries(), etc. Otherwise 743 # it will appear in the binman output 744 to_remove.append(subnode.name) 745 else: 746 with fsw.add_node(subnode.name): 747 _add_node(base_node, depth + 1, subnode) 748 749 # Build a new tree with all nodes and properties starting from the 750 # entry node 751 fsw = libfdt.FdtSw() 752 fsw.INC_SIZE = 65536 753 fsw.finish_reservemap() 754 to_remove = [] 755 loadables = [] 756 with fsw.add_node(''): 757 _add_node(self._node, 0, self._node) 758 self._loadables = loadables 759 fdt = fsw.as_fdt() 760 761 # Remove generator entries from the main list 762 for path in to_remove: 763 if path in self._entries: 764 del self._entries[path] 765 766 # Pack this new FDT and scan it so we can add the data later 767 fdt.pack() 768 data = fdt.as_bytearray() 769 return data 770 771 def SetImagePos(self, image_pos): 772 """Set the position in the image 773 774 This sets each subentry's offsets, sizes and positions-in-image 775 according to where they ended up in the packed FIT file. 776 777 Args: 778 image_pos (int): Position of this entry in the image 779 """ 780 if self.build_done: 781 return 782 super().SetImagePos(image_pos) 783 784 # If mkimage is missing we'll have empty data, 785 # which will cause a FDT_ERR_BADMAGIC error 786 if self.mkimage in self.missing_bintools: 787 return 788 789 fdt = Fdt.FromData(self.GetData()) 790 fdt.Scan() 791 792 for image_name, section in self._entries.items(): 793 path = f"/images/{image_name}" 794 node = fdt.GetNode(path) 795 796 data_prop = node.props.get("data") 797 data_pos = fdt_util.GetInt(node, "data-position") 798 data_offset = fdt_util.GetInt(node, "data-offset") 799 data_size = fdt_util.GetInt(node, "data-size") 800 801 # Contents are inside the FIT 802 if data_prop is not None: 803 # GetOffset() returns offset of a fdt_property struct, 804 # which has 3 fdt32_t members before the actual data. 805 offset = data_prop.GetOffset() + 12 806 size = len(data_prop.bytes) 807 808 # External offset from the base of the FIT 809 elif data_pos is not None: 810 offset = data_pos 811 size = data_size 812 813 # External offset from the end of the FIT, not used in binman 814 elif data_offset is not None: # pragma: no cover 815 offset = fdt.GetFdtObj().totalsize() + data_offset 816 size = data_size 817 818 # This should never happen 819 else: # pragma: no cover 820 self.Raise(f'{path}: missing data properties') 821 822 section.SetOffsetSize(offset, size) 823 section.SetImagePos(self.image_pos) 824 825 def AddBintools(self, btools): 826 super().AddBintools(btools) 827 self.mkimage = self.AddBintool(btools, 'mkimage') 828 829 def CheckMissing(self, missing_list): 830 # We must use our private entry list for this since generator nodes 831 # which are removed from self._entries will otherwise not show up as 832 # missing 833 for entry in self._priv_entries.values(): 834 entry.CheckMissing(missing_list) 835 836 def CheckEntries(self): 837 pass 838 839 def UpdateSignatures(self, privatekey_fname, algo, input_fname): 840 uniq = self.GetUniqueName() 841 args = [ '-G', privatekey_fname, '-r', '-o', algo, '-F' ] 842 if input_fname: 843 fname = input_fname 844 else: 845 fname = tools.get_output_filename('%s.fit' % uniq) 846 tools.write_file(fname, self.GetData()) 847 args.append(fname) 848 849 if self.mkimage.run_cmd(*args) is None: 850 self.Raise("Missing tool: 'mkimage'") 851 852 data = tools.read_file(fname) 853 self.WriteData(data) 854