mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 00:11:51 +01:00 
			
		
		
		
	Since the state module holds references to all the device trees used by binman, it must be updated when the device trees are updated. Add support for this. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			392 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			392 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # SPDX-License-Identifier: GPL-2.0+
 | |
| # Copyright 2018 Google, Inc
 | |
| # Written by Simon Glass <sjg@chromium.org>
 | |
| #
 | |
| # Holds and modifies the state information held by binman
 | |
| #
 | |
| 
 | |
| import hashlib
 | |
| import re
 | |
| 
 | |
| import fdt
 | |
| import os
 | |
| import tools
 | |
| import tout
 | |
| 
 | |
| # Records the device-tree files known to binman, keyed by entry type (e.g.
 | |
| # 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by
 | |
| # binman. They have been copied to <xxx>.out files.
 | |
| #
 | |
| #   key: entry type
 | |
| #   value: tuple:
 | |
| #       Fdt object
 | |
| #       Filename
 | |
| #       Entry object, or None if not known
 | |
| output_fdt_info = {}
 | |
| 
 | |
| # Prefix to add to an fdtmap path to turn it into a path to the /binman node
 | |
| fdt_path_prefix = ''
 | |
| 
 | |
| # Arguments passed to binman to provide arguments to entries
 | |
| entry_args = {}
 | |
| 
 | |
| # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
 | |
| # ftest.py)
 | |
| use_fake_dtb = False
 | |
| 
 | |
| # The DTB which contains the full image information
 | |
| main_dtb = None
 | |
| 
 | |
| # Allow entries to expand after they have been packed. This is detected and
 | |
| # forces a re-pack. If not allowed, any attempted expansion causes an error in
 | |
| # Entry.ProcessContentsUpdate()
 | |
| allow_entry_expansion = True
 | |
| 
 | |
| # Don't allow entries to contract after they have been packed. Instead just
 | |
| # leave some wasted space. If allowed, this is detected and forces a re-pack,
 | |
| # but may result in entries that oscillate in size, thus causing a pack error.
 | |
| # An example is a compressed device tree where the original offset values
 | |
| # result in a larger compressed size than the new ones, but then after updating
 | |
| # to the new ones, the compressed size increases, etc.
 | |
| allow_entry_contraction = False
 | |
| 
 | |
| def GetFdtForEtype(etype):
 | |
|     """Get the Fdt object for a particular device-tree entry
 | |
| 
 | |
|     Binman keeps track of at least one device-tree file called u-boot.dtb but
 | |
|     can also have others (e.g. for SPL). This function looks up the given
 | |
|     entry and returns the associated Fdt object.
 | |
| 
 | |
|     Args:
 | |
|         etype: Entry type of device tree (e.g. 'u-boot-dtb')
 | |
| 
 | |
|     Returns:
 | |
|         Fdt object associated with the entry type
 | |
|     """
 | |
|     value = output_fdt_info.get(etype);
 | |
|     if not value:
 | |
|         return None
 | |
|     return value[0]
 | |
| 
 | |
| def GetFdtPath(etype):
 | |
|     """Get the full pathname of a particular Fdt object
 | |
| 
 | |
|     Similar to GetFdtForEtype() but returns the pathname associated with the
 | |
|     Fdt.
 | |
| 
 | |
|     Args:
 | |
|         etype: Entry type of device tree (e.g. 'u-boot-dtb')
 | |
| 
 | |
|     Returns:
 | |
|         Full path name to the associated Fdt
 | |
|     """
 | |
|     return output_fdt_info[etype][0]._fname
 | |
| 
 | |
| def GetFdtContents(etype='u-boot-dtb'):
 | |
|     """Looks up the FDT pathname and contents
 | |
| 
 | |
|     This is used to obtain the Fdt pathname and contents when needed by an
 | |
|     entry. It supports a 'fake' dtb, allowing tests to substitute test data for
 | |
|     the real dtb.
 | |
| 
 | |
|     Args:
 | |
|         etype: Entry type to look up (e.g. 'u-boot.dtb').
 | |
| 
 | |
|     Returns:
 | |
|         tuple:
 | |
|             pathname to Fdt
 | |
|             Fdt data (as bytes)
 | |
|     """
 | |
|     if etype not in output_fdt_info:
 | |
|         return None, None
 | |
|     if not use_fake_dtb:
 | |
|         pathname = GetFdtPath(etype)
 | |
|         data = GetFdtForEtype(etype).GetContents()
 | |
|     else:
 | |
|         fname = output_fdt_info[etype][1]
 | |
|         pathname = tools.GetInputFilename(fname)
 | |
|         data = tools.ReadFile(pathname)
 | |
|     return pathname, data
 | |
| 
 | |
| def UpdateFdtContents(etype, data):
 | |
|     """Update the contents of a particular device tree
 | |
| 
 | |
|     The device tree is updated and written back to its file. This affects what
 | |
|     is returned from future called to GetFdtContents(), etc.
 | |
| 
 | |
|     Args:
 | |
|         etype: Entry type (e.g. 'u-boot-dtb')
 | |
|         data: Data to replace the DTB with
 | |
|     """
 | |
|     dtb, fname, entry = output_fdt_info[etype]
 | |
|     dtb_fname = dtb.GetFilename()
 | |
|     tools.WriteFile(dtb_fname, data)
 | |
|     dtb = fdt.FdtScan(dtb_fname)
 | |
|     output_fdt_info[etype] = [dtb, fname, entry]
 | |
| 
 | |
| def SetEntryArgs(args):
 | |
|     """Set the value of the entry args
 | |
| 
 | |
|     This sets up the entry_args dict which is used to supply entry arguments to
 | |
|     entries.
 | |
| 
 | |
|     Args:
 | |
|         args: List of entry arguments, each in the format "name=value"
 | |
|     """
 | |
|     global entry_args
 | |
| 
 | |
|     entry_args = {}
 | |
|     if args:
 | |
|         for arg in args:
 | |
|             m = re.match('([^=]*)=(.*)', arg)
 | |
|             if not m:
 | |
|                 raise ValueError("Invalid entry arguemnt '%s'" % arg)
 | |
|             entry_args[m.group(1)] = m.group(2)
 | |
| 
 | |
| def GetEntryArg(name):
 | |
|     """Get the value of an entry argument
 | |
| 
 | |
|     Args:
 | |
|         name: Name of argument to retrieve
 | |
| 
 | |
|     Returns:
 | |
|         String value of argument
 | |
|     """
 | |
|     return entry_args.get(name)
 | |
| 
 | |
| def Prepare(images, dtb):
 | |
|     """Get device tree files ready for use
 | |
| 
 | |
|     This sets up a set of device tree files that can be retrieved by
 | |
|     GetAllFdts(). This includes U-Boot proper and any SPL device trees.
 | |
| 
 | |
|     Args:
 | |
|         images: List of images being used
 | |
|         dtb: Main dtb
 | |
|     """
 | |
|     global output_fdt_info, main_dtb, fdt_path_prefix
 | |
|     # Import these here in case libfdt.py is not available, in which case
 | |
|     # the above help option still works.
 | |
|     import fdt
 | |
|     import fdt_util
 | |
| 
 | |
|     # If we are updating the DTBs we need to put these updated versions
 | |
|     # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
 | |
|     # since it is assumed to be the one passed in with options.dt, and
 | |
|     # was handled just above.
 | |
|     main_dtb = dtb
 | |
|     output_fdt_info.clear()
 | |
|     fdt_path_prefix = ''
 | |
|     output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
 | |
|     output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
 | |
|     output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
 | |
|     if not use_fake_dtb:
 | |
|         fdt_set = {}
 | |
|         for image in images.values():
 | |
|             fdt_set.update(image.GetFdts())
 | |
|         for etype, other in fdt_set.items():
 | |
|             entry, other_fname = other
 | |
|             infile = tools.GetInputFilename(other_fname)
 | |
|             other_fname_dtb = fdt_util.EnsureCompiled(infile)
 | |
|             out_fname = tools.GetOutputFilename('%s.out' %
 | |
|                     os.path.split(other_fname)[1])
 | |
|             tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
 | |
|             other_dtb = fdt.FdtScan(out_fname)
 | |
|             output_fdt_info[etype] = [other_dtb, out_fname, entry]
 | |
| 
 | |
| def PrepareFromLoadedData(image):
 | |
|     """Get device tree files ready for use with a loaded image
 | |
| 
 | |
|     Loaded images are different from images that are being created by binman,
 | |
|     since there is generally already an fdtmap and we read the description from
 | |
|     that. This provides the position and size of every entry in the image with
 | |
|     no calculation required.
 | |
| 
 | |
|     This function uses the same output_fdt_info[] as Prepare(). It finds the
 | |
|     device tree files, adds a reference to the fdtmap and sets the FDT path
 | |
|     prefix to translate from the fdtmap (where the root node is the image node)
 | |
|     to the normal device tree (where the image node is under a /binman node).
 | |
| 
 | |
|     Args:
 | |
|         images: List of images being used
 | |
|     """
 | |
|     global output_fdt_info, main_dtb, fdt_path_prefix
 | |
| 
 | |
|     tout.Info('Preparing device trees')
 | |
|     output_fdt_info.clear()
 | |
|     fdt_path_prefix = ''
 | |
|     output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
 | |
|     main_dtb = None
 | |
|     tout.Info("   Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
 | |
|     for etype, value in image.GetFdts().items():
 | |
|         entry, fname = value
 | |
|         out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
 | |
|         tout.Info("   Found device tree type '%s' at '%s' path '%s'" %
 | |
|                   (etype, out_fname, entry.GetPath()))
 | |
|         entry._filename = entry.GetDefaultFilename()
 | |
|         data = entry.ReadData()
 | |
| 
 | |
|         tools.WriteFile(out_fname, data)
 | |
|         dtb = fdt.Fdt(out_fname)
 | |
|         dtb.Scan()
 | |
|         image_node = dtb.GetNode('/binman')
 | |
|         if 'multiple-images' in image_node.props:
 | |
|             image_node = dtb.GetNode('/binman/%s' % image.image_node)
 | |
|         fdt_path_prefix = image_node.path
 | |
|         output_fdt_info[etype] = [dtb, None, entry]
 | |
|     tout.Info("   FDT path prefix '%s'" % fdt_path_prefix)
 | |
| 
 | |
| 
 | |
| def GetAllFdts():
 | |
|     """Yield all device tree files being used by binman
 | |
| 
 | |
|     Yields:
 | |
|         Device trees being used (U-Boot proper, SPL, TPL)
 | |
|     """
 | |
|     if main_dtb:
 | |
|         yield main_dtb
 | |
|     for etype in output_fdt_info:
 | |
|         dtb = output_fdt_info[etype][0]
 | |
|         if dtb != main_dtb:
 | |
|             yield dtb
 | |
| 
 | |
| def GetUpdateNodes(node, for_repack=False):
 | |
|     """Yield all the nodes that need to be updated in all device trees
 | |
| 
 | |
|     The property referenced by this node is added to any device trees which
 | |
|     have the given node. Due to removable of unwanted notes, SPL and TPL may
 | |
|     not have this node.
 | |
| 
 | |
|     Args:
 | |
|         node: Node object in the main device tree to look up
 | |
|         for_repack: True if we want only nodes which need 'repack' properties
 | |
|             added to them (e.g. 'orig-offset'), False to return all nodes. We
 | |
|             don't add repack properties to SPL/TPL device trees.
 | |
| 
 | |
|     Yields:
 | |
|         Node objects in each device tree that is in use (U-Boot proper, which
 | |
|             is node, SPL and TPL)
 | |
|     """
 | |
|     yield node
 | |
|     for dtb, fname, entry in output_fdt_info.values():
 | |
|         if dtb != node.GetFdt():
 | |
|             if for_repack and entry.etype != 'u-boot-dtb':
 | |
|                 continue
 | |
|             other_node = dtb.GetNode(fdt_path_prefix + node.path)
 | |
|             #print('   try', fdt_path_prefix + node.path, other_node)
 | |
|             if other_node:
 | |
|                 yield other_node
 | |
| 
 | |
| def AddZeroProp(node, prop, for_repack=False):
 | |
|     """Add a new property to affected device trees with an integer value of 0.
 | |
| 
 | |
|     Args:
 | |
|         prop_name: Name of property
 | |
|         for_repack: True is this property is only needed for repacking
 | |
|     """
 | |
|     for n in GetUpdateNodes(node, for_repack):
 | |
|         n.AddZeroProp(prop)
 | |
| 
 | |
| def AddSubnode(node, name):
 | |
|     """Add a new subnode to a node in affected device trees
 | |
| 
 | |
|     Args:
 | |
|         node: Node to add to
 | |
|         name: name of node to add
 | |
| 
 | |
|     Returns:
 | |
|         New subnode that was created in main tree
 | |
|     """
 | |
|     first = None
 | |
|     for n in GetUpdateNodes(node):
 | |
|         subnode = n.AddSubnode(name)
 | |
|         if not first:
 | |
|             first = subnode
 | |
|     return first
 | |
| 
 | |
| def AddString(node, prop, value):
 | |
|     """Add a new string property to affected device trees
 | |
| 
 | |
|     Args:
 | |
|         prop_name: Name of property
 | |
|         value: String value (which will be \0-terminated in the DT)
 | |
|     """
 | |
|     for n in GetUpdateNodes(node):
 | |
|         n.AddString(prop, value)
 | |
| 
 | |
| def SetInt(node, prop, value, for_repack=False):
 | |
|     """Update an integer property in affected device trees with an integer value
 | |
| 
 | |
|     This is not allowed to change the size of the FDT.
 | |
| 
 | |
|     Args:
 | |
|         prop_name: Name of property
 | |
|         for_repack: True is this property is only needed for repacking
 | |
|     """
 | |
|     for n in GetUpdateNodes(node, for_repack):
 | |
|         tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
 | |
|                     (n.GetFdt().name, n.path, prop, value))
 | |
|         n.SetInt(prop, value)
 | |
| 
 | |
| def CheckAddHashProp(node):
 | |
|     hash_node = node.FindNode('hash')
 | |
|     if hash_node:
 | |
|         algo = hash_node.props.get('algo')
 | |
|         if not algo:
 | |
|             return "Missing 'algo' property for hash node"
 | |
|         if algo.value == 'sha256':
 | |
|             size = 32
 | |
|         else:
 | |
|             return "Unknown hash algorithm '%s'" % algo
 | |
|         for n in GetUpdateNodes(hash_node):
 | |
|             n.AddEmptyProp('value', size)
 | |
| 
 | |
| def CheckSetHashValue(node, get_data_func):
 | |
|     hash_node = node.FindNode('hash')
 | |
|     if hash_node:
 | |
|         algo = hash_node.props.get('algo').value
 | |
|         if algo == 'sha256':
 | |
|             m = hashlib.sha256()
 | |
|             m.update(get_data_func())
 | |
|             data = m.digest()
 | |
|         for n in GetUpdateNodes(hash_node):
 | |
|             n.SetData('value', data)
 | |
| 
 | |
| def SetAllowEntryExpansion(allow):
 | |
|     """Set whether post-pack expansion of entries is allowed
 | |
| 
 | |
|     Args:
 | |
|        allow: True to allow expansion, False to raise an exception
 | |
|     """
 | |
|     global allow_entry_expansion
 | |
| 
 | |
|     allow_entry_expansion = allow
 | |
| 
 | |
| def AllowEntryExpansion():
 | |
|     """Check whether post-pack expansion of entries is allowed
 | |
| 
 | |
|     Returns:
 | |
|         True if expansion should be allowed, False if an exception should be
 | |
|             raised
 | |
|     """
 | |
|     return allow_entry_expansion
 | |
| 
 | |
| def SetAllowEntryContraction(allow):
 | |
|     """Set whether post-pack contraction of entries is allowed
 | |
| 
 | |
|     Args:
 | |
|        allow: True to allow contraction, False to raise an exception
 | |
|     """
 | |
|     global allow_entry_contraction
 | |
| 
 | |
|     allow_entry_contraction = allow
 | |
| 
 | |
| def AllowEntryContraction():
 | |
|     """Check whether post-pack contraction of entries is allowed
 | |
| 
 | |
|     Returns:
 | |
|         True if contraction should be allowed, False if an exception should be
 | |
|             raised
 | |
|     """
 | |
|     return allow_entry_contraction
 |