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