Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 | # SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2016 Google, Inc # # Base class for all entries # from collections import namedtuple import importlib import os import sys from dtoc import fdt_util from patman import tools from patman.tools import ToHex, ToHexSize from patman import tout modules = {} # An argument which can be passed to entries on the command line, in lieu of # device-tree properties. EntryArg = namedtuple('EntryArg', ['name', 'datatype']) # Information about an entry for use when displaying summaries EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size', 'image_pos', 'uncomp_size', 'offset', 'entry']) class Entry(object): """An Entry in the section An entry corresponds to a single node in the device-tree description of the section. Each entry ends up being a part of the final section. Entries can be placed either right next to each other, or with padding between them. The type of the entry determines the data that is in it. This class is not used by itself. All entry objects are subclasses of Entry. Attributes: section: Section object containing this entry node: The node that created this entry offset: Offset of entry within the section, None if not known yet (in which case it will be calculated by Pack()) size: Entry size in bytes, None if not known pre_reset_size: size as it was before ResetForPack(). This allows us to keep track of the size we started with and detect size changes uncomp_size: Size of uncompressed data in bytes, if the entry is compressed, else None contents_size: Size of contents in bytes, 0 by default align: Entry start offset alignment relative to the start of the containing section, or None align_size: Entry size alignment, or None align_end: Entry end offset alignment relative to the start of the containing section, or None pad_before: Number of pad bytes before the contents when it is placed in the containing section, 0 if none. The pad bytes become part of the entry. pad_after: Number of pad bytes after the contents when it is placed in the containing section, 0 if none. The pad bytes become part of the entry. data: Contents of entry (string of bytes). This does not include padding created by pad_before or pad_after. If the entry is compressed, this contains the compressed data. uncomp_data: Original uncompressed data, if this entry is compressed, else None compress: Compression algoithm used (e.g. 'lz4'), 'none' if none orig_offset: Original offset value read from node orig_size: Original size value read from node missing: True if this entry is missing its contents allow_missing: Allow children of this entry to be missing (used by subclasses such as Entry_section) external: True if this entry contains an external binary blob """ def __init__(self, section, etype, node, name_prefix=''): # Put this here to allow entry-docs and help to work without libfdt global state from binman import state self.section = section self.etype = etype self._node = node self.name = node and (name_prefix + node.name) or 'none' self.offset = None self.size = None self.pre_reset_size = None self.uncomp_size = None self.data = None self.uncomp_data = None self.contents_size = 0 self.align = None self.align_size = None self.align_end = None self.pad_before = 0 self.pad_after = 0 self.offset_unset = False self.image_pos = None self._expand_size = False self.compress = 'none' self.missing = False self.external = False self.allow_missing = False @staticmethod def Lookup(node_path, etype, expanded): """Look up the entry class for a node. Args: node_node: Path name of Node object containing information about the entry to create (used for errors) etype: Entry type to use expanded: Use the expanded version of etype Returns: The entry class object if found, else None if not found and expanded is True Raise: ValueError if expanded is False and the class is not found """ # Convert something like 'u-boot@0' to 'u_boot' since we are only # interested in the type. module_name = etype.replace('-', '_') if '@' in module_name: module_name = module_name.split('@')[0] if expanded: module_name += '_expanded' module = modules.get(module_name) # Also allow entry-type modules to be brought in from the etype directory. # Import the module if we have not already done so. if not module: try: module = importlib.import_module('binman.etype.' + module_name) except ImportError as e: if expanded: return None raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" % (etype, node_path, module_name, e)) modules[module_name] = module # Look up the expected class name return getattr(module, 'Entry_%s' % module_name) @staticmethod def Create(section, node, etype=None, expanded=False): """Create a new entry for a node. Args: section: Section object containing this node node: Node object containing information about the entry to create etype: Entry type to use, or None to work it out (used for tests) expanded: True to use expanded versions of entries, where available Returns: A new Entry object of the correct type (a subclass of Entry) """ if not etype: etype = fdt_util.GetString(node, 'type', node.name) obj = Entry.Lookup(node.path, etype, expanded) if obj and expanded: # Check whether to use the expanded entry new_etype = etype + '-expanded' can_expand = not fdt_util.GetBool(node, 'no-expanded') if can_expand and obj.UseExpanded(node, etype, new_etype): etype = new_etype else: obj = None if not obj: obj = Entry.Lookup(node.path, etype, False) # Call its constructor to get the object we want. return obj(section, etype, node) def ReadNode(self): """Read entry information from the node This must be called as the first thing after the Entry is created. This reads all the fields we recognise from the node, ready for use. """ if 'pos' in self._node.props: self.Raise("Please use 'offset' instead of 'pos'") self.offset = fdt_util.GetInt(self._node, 'offset') self.size = fdt_util.GetInt(self._node, 'size') self.orig_offset = fdt_util.GetInt(self._node, 'orig-offset') self.orig_size = fdt_util.GetInt(self._node, 'orig-size') if self.GetImage().copy_to_orig: self.orig_offset = self.offset self.orig_size = self.size # These should not be set in input files, but are set in an FDT map, # which is also read by this code. self.image_pos = fdt_util.GetInt(self._node, 'image-pos') self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size') self.align = fdt_util.GetInt(self._node, 'align') if tools.NotPowerOfTwo(self.align): raise ValueError("Node '%s': Alignment %s must be a power of two" % (self._node.path, self.align)) if self.section and self.align is None: self.align = self.section.align_default self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) self.align_size = fdt_util.GetInt(self._node, 'align-size') if tools.NotPowerOfTwo(self.align_size): self.Raise("Alignment size %s must be a power of two" % self.align_size) self.align_end = fdt_util.GetInt(self._node, 'align-end') self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset') self.expand_size = fdt_util.GetBool(self._node, 'expand-size') self.missing_msg = fdt_util.GetString(self._node, 'missing-msg') # This is only supported by blobs and sections at present self.compress = fdt_util.GetString(self._node, 'compress', 'none') def GetDefaultFilename(self): return None def GetFdts(self): """Get the device trees used by this entry Returns: Empty dict, if this entry is not a .dtb, otherwise: Dict: key: Filename from this entry (without the path) value: Tuple: Entry object for this dtb Filename of file containing this dtb """ return {} def ExpandEntries(self): """Expand out entries which produce other entries Some entries generate subnodes automatically, from which sub-entries are then created. This method allows those to be added to the binman definition for the current image. An entry which implements this method should call state.AddSubnode() to add a subnode and can add properties with state.AddString(), etc. An example is 'files', which produces a section containing a list of files. """ pass def AddMissingProperties(self, have_image_pos): """Add new properties to the device tree as needed for this entry Args: have_image_pos: True if this entry has an image position. This can be False if its parent section is compressed, since compression groups all entries together into a compressed block of data, obscuring the start of each individual child entry """ for prop in ['offset', 'size']: if not prop in self._node.props: state.AddZeroProp(self._node, prop) if have_image_pos and 'image-pos' not in self._node.props: state.AddZeroProp(self._node, 'image-pos') if self.GetImage().allow_repack: if self.orig_offset is not None: state.AddZeroProp(self._node, 'orig-offset', True) if self.orig_size is not None: state.AddZeroProp(self._node, 'orig-size', True) if self.compress != 'none': state.AddZeroProp(self._node, 'uncomp-size') err = state.CheckAddHashProp(self._node) if err: self.Raise(err) def SetCalculatedProperties(self): """Set the value of device-tree properties calculated by binman""" state.SetInt(self._node, 'offset', self.offset) state.SetInt(self._node, 'size', self.size) base = self.section.GetRootSkipAtStart() if self.section else 0 if self.image_pos is not None: state.SetInt(self._node, 'image-pos', self.image_pos - base) if self.GetImage().allow_repack: if self.orig_offset is not None: state.SetInt(self._node, 'orig-offset', self.orig_offset, True) if self.orig_size is not None: state.SetInt(self._node, 'orig-size', self.orig_size, True) if self.uncomp_size is not None: state.SetInt(self._node, 'uncomp-size', self.uncomp_size) state.CheckSetHashValue(self._node, self.GetData) def ProcessFdt(self, fdt): """Allow entries to adjust the device tree Some entries need to adjust the device tree for their purposes. This may involve adding or deleting properties. Returns: True if processing is complete False if processing could not be completed due to a dependency. This will cause the entry to be retried after others have been called """ return True def SetPrefix(self, prefix): """Set the name prefix for a node Args: prefix: Prefix to set, or '' to not use a prefix """ if prefix: self.name = prefix + self.name def SetContents(self, data): """Set the contents of an entry This sets both the data and content_size properties Args: data: Data to set to the contents (bytes) """ self.data = data self.contents_size = len(self.data) def ProcessContentsUpdate(self, data): """Update the contents of an entry, after the size is fixed This checks that the new data is the same size as the old. If the size has changed, this triggers a re-run of the packing algorithm. Args: data: Data to set to the contents (bytes) Raises: ValueError if the new data size is not the same as the old """ size_ok = True new_size = len(data) if state.AllowEntryExpansion() and new_size > self.contents_size: # self.data will indicate the new size needed size_ok = False elif state.AllowEntryContraction() and new_size < self.contents_size: size_ok = False # If not allowed to change, try to deal with it or give up if size_ok: if new_size > self.contents_size: self.Raise('Cannot update entry size from %d to %d' % (self.contents_size, new_size)) # Don't let the data shrink. Pad it if necessary if size_ok and new_size < self.contents_size: data += tools.GetBytes(0, self.contents_size - new_size) if not size_ok: tout.Debug("Entry '%s' size change from %s to %s" % ( self._node.path, ToHex(self.contents_size), ToHex(new_size))) self.SetContents(data) return size_ok def ObtainContents(self): """Figure out the contents of an entry. Returns: True if the contents were found, False if another call is needed after the other entries are processed. """ # No contents by default: subclasses can implement this return True def ResetForPack(self): """Reset offset/size fields so that packing can be done again""" self.Detail('ResetForPack: offset %s->%s, size %s->%s' % (ToHex(self.offset), ToHex(self.orig_offset), ToHex(self.size), ToHex(self.orig_size))) self.pre_reset_size = self.size self.offset = self.orig_offset self.size = self.orig_size def Pack(self, offset): """Figure out how to pack the entry into the section Most of the time the entries are not fully specified. There may be an alignment but no size. In that case we take the size from the contents of the entry. If an entry has no hard-coded offset, it will be placed at @offset. Once this function is complete, both the offset and size of the entry will be know. Args: Current section offset pointer Returns: New section offset pointer (after this entry) """ self.Detail('Packing: offset=%s, size=%s, content_size=%x' % (ToHex(self.offset), ToHex(self.size), self.contents_size)) if self.offset is None: if self.offset_unset: self.Raise('No offset set with offset-unset: should another ' 'entry provide this correct offset?') self.offset = tools.Align(offset, self.align) needed = self.pad_before + self.contents_size + self.pad_after needed = tools.Align(needed, self.align_size) size = self.size if not size: size = needed new_offset = self.offset + size aligned_offset = tools.Align(new_offset, self.align_end) if aligned_offset != new_offset: size = aligned_offset - self.offset new_offset = aligned_offset if not self.size: self.size = size if self.size < needed: self.Raise("Entry contents size is %#x (%d) but entry size is " "%#x (%d)" % (needed, needed, self.size, self.size)) # Check that the alignment is correct. It could be wrong if the # and offset or size values were provided (i.e. not calculated), but # conflict with the provided alignment values if self.size != tools.Align(self.size, self.align_size): self.Raise("Size %#x (%d) does not match align-size %#x (%d)" % (self.size, self.size, self.align_size, self.align_size)) if self.offset != tools.Align(self.offset, self.align): self.Raise("Offset %#x (%d) does not match align %#x (%d)" % (self.offset, self.offset, self.align, self.align)) self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' % (self.offset, self.size, self.contents_size, new_offset)) return new_offset def Raise(self, msg): """Convenience function to raise an error referencing a node""" raise ValueError("Node '%s': %s" % (self._node.path, msg)) def Info(self, msg): """Convenience function to log info referencing a node""" tag = "Info '%s'" % self._node.path tout.Detail('%30s: %s' % (tag, msg)) def Detail(self, msg): """Convenience function to log detail referencing a node""" tag = "Node '%s'" % self._node.path tout.Detail('%30s: %s' % (tag, msg)) def GetEntryArgsOrProps(self, props, required=False): """Return the values of a set of properties Args: props: List of EntryArg objects Raises: ValueError if a property is not found """ values = [] missing = [] for prop in props: python_prop = prop.name.replace('-', '_') if hasattr(self, python_prop): value = getattr(self, python_prop) else: value = None if value is None: value = self.GetArg(prop.name, prop.datatype) if value is None and required: missing.append(prop.name) values.append(value) if missing: self.GetImage().MissingArgs(self, missing) return values def GetPath(self): """Get the path of a node Returns: Full path of the node for this entry """ return self._node.path def GetData(self, required=True): """Get the contents of an entry Args: required: True if the data must be present, False if it is OK to return None Returns: bytes content of the entry, excluding any padding. If the entry is compressed, the compressed data is returned """ self.Detail('GetData: size %s' % ToHexSize(self.data)) return self.data def GetPaddedData(self, data=None): """Get the data for an entry including any padding Gets the entry data and uses its section's pad-byte value to add padding before and after as defined by the pad-before and pad-after properties. This does not consider alignment. Returns: Contents of the entry along with any pad bytes before and after it (bytes) """ if data is None: data = self.GetData() return self.section.GetPaddedDataForEntry(self, data) def GetOffsets(self): """Get the offsets for siblings Some entry types can contain information about the position or size of other entries. An example of this is the Intel Flash Descriptor, which knows where the Intel Management Engine section should go. If this entry knows about the position of other entries, it can specify this by returning values here Returns: Dict: key: Entry type value: List containing position and size of the given entry type. Either can be None if not known """ return {} def SetOffsetSize(self, offset, size): """Set the offset and/or size of an entry Args: offset: New offset, or None to leave alone size: New size, or None to leave alone """ if offset is not None: self.offset = offset if size is not None: self.size = size def SetImagePos(self, image_pos): """Set the position in the image Args: image_pos: Position of this entry in the image """ self.image_pos = image_pos + self.offset def ProcessContents(self): """Do any post-packing updates of entry contents This function should call ProcessContentsUpdate() to update the entry contents, if necessary, returning its return value here. Args: data: Data to set to the contents (bytes) Returns: True if the new data size is OK, False if expansion is needed Raises: ValueError if the new data size is not the same as the old and state.AllowEntryExpansion() is False """ return True def WriteSymbols(self, section): """Write symbol values into binary files for access at run time Args: section: Section containing the entry """ pass def CheckEntries(self): """Check that the entry offsets are correct This is used for entries which have extra offset requirements (other than having to be fully inside their section). Sub-classes can implement this function and raise if there is a problem. """ pass @staticmethod def GetStr(value): if value is None: return '<none> ' return '%08x' % value @staticmethod def WriteMapLine(fd, indent, name, offset, size, image_pos): print('%s %s%s %s %s' % (Entry.GetStr(image_pos), ' ' * indent, Entry.GetStr(offset), Entry.GetStr(size), name), file=fd) def WriteMap(self, fd, indent): """Write a map of the entry to a .map file Args: fd: File to write the map to indent: Curent indent level of map (0=none, 1=one level, etc.) """ self.WriteMapLine(fd, indent, self.name, self.offset, self.size, self.image_pos) def GetEntries(self): """Return a list of entries contained by this entry Returns: List of entries, or None if none. A normal entry has no entries within it so will return None """ return None def GetArg(self, name, datatype=str): """Get the value of an entry argument or device-tree-node property Some node properties can be provided as arguments to binman. First check the entry arguments, and fall back to the device tree if not found Args: name: Argument name datatype: Data type (str or int) Returns: Value of argument as a string or int, or None if no value Raises: ValueError if the argument cannot be converted to in """ value = state.GetEntryArg(name) if value is not None: if datatype == int: try: value = int(value) except ValueError: self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" % (name, value)) elif datatype == str: pass else: raise ValueError("GetArg() internal error: Unknown data type '%s'" % datatype) else: value = fdt_util.GetDatatype(self._node, name, datatype) return value @staticmethod def WriteDocs(modules, test_missing=None): """Write out documentation about the various entry types to stdout Args: modules: List of modules to include test_missing: Used for testing. This is a module to report as missing """ print('''Binman Entry Documentation =========================== This file describes the entry types supported by binman. These entry types can be placed in an image one by one to build up a final firmware image. It is fairly easy to create new entry types. Just add a new file to the 'etype' directory. You can use the existing entries as examples. Note that some entries are subclasses of others, using and extending their features to produce new behaviours. ''') modules = sorted(modules) # Don't show the test entry if '_testing' in modules: modules.remove('_testing') missing = [] for name in modules: module = Entry.Lookup('WriteDocs', name, False) docs = getattr(module, '__doc__') if test_missing == name: docs = None if docs: lines = docs.splitlines() first_line = lines[0] rest = [line[4:] for line in lines[1:]] hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line) print(hdr) print('-' * len(hdr)) print('\n'.join(rest)) print() print() else: missing.append(name) if missing: raise ValueError('Documentation is missing for modules: %s' % ', '.join(missing)) def GetUniqueName(self): """Get a unique name for a node Returns: String containing a unique name for a node, consisting of the name of all ancestors (starting from within the 'binman' node) separated by a dot ('.'). This can be useful for generating unique filesnames in the output directory. """ name = self.name node = self._node while node.parent: node = node.parent if node.name == 'binman': break name = '%s.%s' % (node.name, name) return name def ExpandToLimit(self, limit): """Expand an entry so that it ends at the given offset limit""" if self.offset + self.size < limit: self.size = limit - self.offset # Request the contents again, since changing the size requires that # the data grows. This should not fail, but check it to be sure. if not self.ObtainContents(): self.Raise('Cannot obtain contents when expanding entry') def HasSibling(self, name): """Check if there is a sibling of a given name Returns: True if there is an entry with this name in the the same section, else False """ return name in self.section.GetEntries() def GetSiblingImagePos(self, name): """Return the image position of the given sibling Returns: Image position of sibling, or None if the sibling has no position, or False if there is no such sibling """ if not self.HasSibling(name): return False return self.section.GetEntries()[name].image_pos @staticmethod def AddEntryInfo(entries, indent, name, etype, size, image_pos, uncomp_size, offset, entry): """Add a new entry to the entries list Args: entries: List (of EntryInfo objects) to add to indent: Current indent level to add to list name: Entry name (string) etype: Entry type (string) size: Entry size in bytes (int) image_pos: Position within image in bytes (int) uncomp_size: Uncompressed size if the entry uses compression, else None offset: Entry offset within parent in bytes (int) entry: Entry object """ entries.append(EntryInfo(indent, name, etype, size, image_pos, uncomp_size, offset, entry)) def ListEntries(self, entries, indent): """Add files in this entry to the list of entries This can be overridden by subclasses which need different behaviour. Args: entries: List (of EntryInfo objects) to add to indent: Current indent level to add to list """ self.AddEntryInfo(entries, indent, self.name, self.etype, self.size, self.image_pos, self.uncomp_size, self.offset, self) def ReadData(self, decomp=True): """Read the data for an entry from the image This is used when the image has been read in and we want to extract the data for a particular entry from that image. Args: decomp: True to decompress any compressed data before returning it; False to return the raw, uncompressed data Returns: Entry data (bytes) """ # Use True here so that we get an uncompressed section to work from, # although compressed sections are currently not supported tout.Debug("ReadChildData section '%s', entry '%s'" % (self.section.GetPath(), self.GetPath())) data = self.section.ReadChildData(self, decomp) return data def ReadChildData(self, child, decomp=True): """Read the data for a particular child entry This reads data from the parent and extracts the piece that relates to the given child. Args: child: Child entry to read data for (must be valid) decomp: True to decompress any compressed data before returning it; False to return the raw, uncompressed data Returns: Data for the child (bytes) """ pass def LoadData(self, decomp=True): data = self.ReadData(decomp) self.contents_size = len(data) self.ProcessContentsUpdate(data) self.Detail('Loaded data size %x' % len(data)) def GetImage(self): """Get the image containing this entry Returns: Image object containing this entry """ return self.section.GetImage() def WriteData(self, data, decomp=True): """Write the data to an entry in the image This is used when the image has been read in and we want to replace the data for a particular entry in that image. The image must be re-packed and written out afterwards. Args: data: Data to replace it with decomp: True to compress the data if needed, False if data is already compressed so should be used as is Returns: True if the data did not result in a resize of this entry, False if the entry must be resized """ if self.size is not None: self.contents_size = self.size else: self.contents_size = self.pre_reset_size ok = self.ProcessContentsUpdate(data) self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok)) section_ok = self.section.WriteChildData(self) return ok and section_ok def WriteChildData(self, child): """Handle writing the data in a child entry This should be called on the child's parent section after the child's data has been updated. It This base-class implementation does nothing, since the base Entry object does not have any children. Args: child: Child Entry that was written Returns: True if the section could be updated successfully, False if the data is such that the section could not updat """ return True def GetSiblingOrder(self): """Get the relative order of an entry amoung its siblings Returns: 'start' if this entry is first among siblings, 'end' if last, otherwise None """ entries = list(self.section.GetEntries().values()) if entries: if self == entries[0]: return 'start' elif self == entries[-1]: return 'end' return 'middle' def SetAllowMissing(self, allow_missing): """Set whether a section allows missing external blobs Args: allow_missing: True if allowed, False if not allowed """ # This is meaningless for anything other than sections pass def CheckMissing(self, missing_list): """Check if any entries in this section have missing external blobs If there are missing blobs, the entries are added to the list Args: missing_list: List of Entry objects to be added to """ if self.missing: missing_list.append(self) def GetAllowMissing(self): """Get whether a section allows missing external blobs Returns: True if allowed, False if not allowed """ return self.allow_missing def GetHelpTags(self): """Get the tags use for missing-blob help Returns: list of possible tags, most desirable first """ return list(filter(None, [self.missing_msg, self.name, self.etype])) def CompressData(self, indata): """Compress data according to the entry's compression method Args: indata: Data to compress Returns: Compressed data (first word is the compressed size) """ self.uncomp_data = indata if self.compress != 'none': self.uncomp_size = len(indata) data = tools.Compress(indata, self.compress) return data @classmethod def UseExpanded(cls, node, etype, new_etype): """Check whether to use an expanded entry type This is called by Entry.Create() when it finds an expanded version of an entry type (e.g. 'u-boot-expanded'). If this method returns True then it will be used (e.g. in place of 'u-boot'). If it returns False, it is ignored. Args: node: Node object containing information about the entry to create etype: Original entry type being used new_etype: New entry type proposed Returns: True to use this entry type, False to use the original one """ tout.Info("Node '%s': etype '%s': %s selected" % (node.path, etype, new_etype)) return True |