254 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			7.0 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
 | 
						|
from sets import Set
 | 
						|
 | 
						|
import os
 | 
						|
import tools
 | 
						|
 | 
						|
# Records the device-tree files known to binman, keyed by filename (e.g.
 | 
						|
# 'u-boot-spl.dtb')
 | 
						|
fdt_files = {}
 | 
						|
 | 
						|
# 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
 | 
						|
 | 
						|
# Set of all device tree files references by images
 | 
						|
fdt_set = Set()
 | 
						|
 | 
						|
# Same as above, but excluding the main one
 | 
						|
fdt_subset = Set()
 | 
						|
 | 
						|
# The DTB which contains the full image information
 | 
						|
main_dtb = None
 | 
						|
 | 
						|
def GetFdt(fname):
 | 
						|
    """Get the Fdt object for a particular device-tree filename
 | 
						|
 | 
						|
    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
 | 
						|
    filename and returns the associated Fdt object.
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname: Filename to look up (e.g. 'u-boot.dtb').
 | 
						|
 | 
						|
    Returns:
 | 
						|
        Fdt object associated with the filename
 | 
						|
    """
 | 
						|
    return fdt_files[fname]
 | 
						|
 | 
						|
def GetFdtPath(fname):
 | 
						|
    """Get the full pathname of a particular Fdt object
 | 
						|
 | 
						|
    Similar to GetFdt() but returns the pathname associated with the Fdt.
 | 
						|
 | 
						|
    Args:
 | 
						|
        fname: Filename to look up (e.g. 'u-boot.dtb').
 | 
						|
 | 
						|
    Returns:
 | 
						|
        Full path name to the associated Fdt
 | 
						|
    """
 | 
						|
    return fdt_files[fname]._fname
 | 
						|
 | 
						|
def GetFdtContents(fname):
 | 
						|
    """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:
 | 
						|
        fname: Filename to look up (e.g. 'u-boot.dtb').
 | 
						|
 | 
						|
    Returns:
 | 
						|
        tuple:
 | 
						|
            pathname to Fdt
 | 
						|
            Fdt data (as bytes)
 | 
						|
    """
 | 
						|
    if fname in fdt_files and not use_fake_dtb:
 | 
						|
        pathname = GetFdtPath(fname)
 | 
						|
        data = GetFdt(fname).GetContents()
 | 
						|
    else:
 | 
						|
        pathname = tools.GetInputFilename(fname)
 | 
						|
        data = tools.ReadFile(pathname)
 | 
						|
    return pathname, data
 | 
						|
 | 
						|
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 GetFdts().
 | 
						|
    At present there is only one, that for U-Boot proper.
 | 
						|
 | 
						|
    Args:
 | 
						|
        images: List of images being used
 | 
						|
        dtb: Main dtb
 | 
						|
    """
 | 
						|
    global fdt_set, fdt_subset, fdt_files, main_dtb
 | 
						|
    # 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
 | 
						|
    fdt_files.clear()
 | 
						|
    fdt_files['u-boot.dtb'] = dtb
 | 
						|
    fdt_subset = Set()
 | 
						|
    if not use_fake_dtb:
 | 
						|
        for image in images.values():
 | 
						|
            fdt_subset.update(image.GetFdtSet())
 | 
						|
        fdt_subset.discard('u-boot.dtb')
 | 
						|
        for other_fname in fdt_subset:
 | 
						|
            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)
 | 
						|
            fdt_files[other_fname] = other_dtb
 | 
						|
 | 
						|
def GetFdts():
 | 
						|
    """Yield all device tree files being used by binman
 | 
						|
 | 
						|
    Yields:
 | 
						|
        Device trees being used (U-Boot proper, SPL, TPL)
 | 
						|
    """
 | 
						|
    yield main_dtb
 | 
						|
    for other_fname in fdt_subset:
 | 
						|
        yield fdt_files[other_fname]
 | 
						|
 | 
						|
def GetUpdateNodes(node):
 | 
						|
    """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
 | 
						|
 | 
						|
    Yields:
 | 
						|
        Node objects in each device tree that is in use (U-Boot proper, which
 | 
						|
            is node, SPL and TPL)
 | 
						|
    """
 | 
						|
    yield node
 | 
						|
    for dtb in fdt_files.values():
 | 
						|
        if dtb != node.GetFdt():
 | 
						|
            other_node = dtb.GetNode(node.path)
 | 
						|
            if other_node:
 | 
						|
                yield other_node
 | 
						|
 | 
						|
def AddZeroProp(node, prop):
 | 
						|
    """Add a new property to affected device trees with an integer value of 0.
 | 
						|
 | 
						|
    Args:
 | 
						|
        prop_name: Name of property
 | 
						|
    """
 | 
						|
    for n in GetUpdateNodes(node):
 | 
						|
        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):
 | 
						|
    """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 n in GetUpdateNodes(node):
 | 
						|
        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)
 |