xref: /openbmc/u-boot/tools/binman/state.py (revision 5396b2a9)
1# SPDX-License-Identifier: GPL-2.0+
2# Copyright 2018 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5# Holds and modifies the state information held by binman
6#
7
8import hashlib
9import re
10from sets import Set
11
12import os
13import tools
14
15# Records the device-tree files known to binman, keyed by filename (e.g.
16# 'u-boot-spl.dtb')
17fdt_files = {}
18
19# Arguments passed to binman to provide arguments to entries
20entry_args = {}
21
22# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
23# ftest.py)
24use_fake_dtb = False
25
26# Set of all device tree files references by images
27fdt_set = Set()
28
29# Same as above, but excluding the main one
30fdt_subset = Set()
31
32# The DTB which contains the full image information
33main_dtb = None
34
35def GetFdt(fname):
36    """Get the Fdt object for a particular device-tree filename
37
38    Binman keeps track of at least one device-tree file called u-boot.dtb but
39    can also have others (e.g. for SPL). This function looks up the given
40    filename and returns the associated Fdt object.
41
42    Args:
43        fname: Filename to look up (e.g. 'u-boot.dtb').
44
45    Returns:
46        Fdt object associated with the filename
47    """
48    return fdt_files[fname]
49
50def GetFdtPath(fname):
51    """Get the full pathname of a particular Fdt object
52
53    Similar to GetFdt() but returns the pathname associated with the Fdt.
54
55    Args:
56        fname: Filename to look up (e.g. 'u-boot.dtb').
57
58    Returns:
59        Full path name to the associated Fdt
60    """
61    return fdt_files[fname]._fname
62
63def GetFdtContents(fname):
64    """Looks up the FDT pathname and contents
65
66    This is used to obtain the Fdt pathname and contents when needed by an
67    entry. It supports a 'fake' dtb, allowing tests to substitute test data for
68    the real dtb.
69
70    Args:
71        fname: Filename to look up (e.g. 'u-boot.dtb').
72
73    Returns:
74        tuple:
75            pathname to Fdt
76            Fdt data (as bytes)
77    """
78    if fname in fdt_files and not use_fake_dtb:
79        pathname = GetFdtPath(fname)
80        data = GetFdt(fname).GetContents()
81    else:
82        pathname = tools.GetInputFilename(fname)
83        data = tools.ReadFile(pathname)
84    return pathname, data
85
86def SetEntryArgs(args):
87    """Set the value of the entry args
88
89    This sets up the entry_args dict which is used to supply entry arguments to
90    entries.
91
92    Args:
93        args: List of entry arguments, each in the format "name=value"
94    """
95    global entry_args
96
97    entry_args = {}
98    if args:
99        for arg in args:
100            m = re.match('([^=]*)=(.*)', arg)
101            if not m:
102                raise ValueError("Invalid entry arguemnt '%s'" % arg)
103            entry_args[m.group(1)] = m.group(2)
104
105def GetEntryArg(name):
106    """Get the value of an entry argument
107
108    Args:
109        name: Name of argument to retrieve
110
111    Returns:
112        String value of argument
113    """
114    return entry_args.get(name)
115
116def Prepare(images, dtb):
117    """Get device tree files ready for use
118
119    This sets up a set of device tree files that can be retrieved by GetFdts().
120    At present there is only one, that for U-Boot proper.
121
122    Args:
123        images: List of images being used
124        dtb: Main dtb
125    """
126    global fdt_set, fdt_subset, fdt_files, main_dtb
127    # Import these here in case libfdt.py is not available, in which case
128    # the above help option still works.
129    import fdt
130    import fdt_util
131
132    # If we are updating the DTBs we need to put these updated versions
133    # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
134    # since it is assumed to be the one passed in with options.dt, and
135    # was handled just above.
136    main_dtb = dtb
137    fdt_files.clear()
138    fdt_files['u-boot.dtb'] = dtb
139    fdt_subset = Set()
140    if not use_fake_dtb:
141        for image in images.values():
142            fdt_subset.update(image.GetFdtSet())
143        fdt_subset.discard('u-boot.dtb')
144        for other_fname in fdt_subset:
145            infile = tools.GetInputFilename(other_fname)
146            other_fname_dtb = fdt_util.EnsureCompiled(infile)
147            out_fname = tools.GetOutputFilename('%s.out' %
148                    os.path.split(other_fname)[1])
149            tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
150            other_dtb = fdt.FdtScan(out_fname)
151            fdt_files[other_fname] = other_dtb
152
153def GetFdts():
154    """Yield all device tree files being used by binman
155
156    Yields:
157        Device trees being used (U-Boot proper, SPL, TPL)
158    """
159    yield main_dtb
160    for other_fname in fdt_subset:
161        yield fdt_files[other_fname]
162
163def GetUpdateNodes(node):
164    """Yield all the nodes that need to be updated in all device trees
165
166    The property referenced by this node is added to any device trees which
167    have the given node. Due to removable of unwanted notes, SPL and TPL may
168    not have this node.
169
170    Args:
171        node: Node object in the main device tree to look up
172
173    Yields:
174        Node objects in each device tree that is in use (U-Boot proper, which
175            is node, SPL and TPL)
176    """
177    yield node
178    for dtb in fdt_files.values():
179        if dtb != node.GetFdt():
180            other_node = dtb.GetNode(node.path)
181            if other_node:
182                yield other_node
183
184def AddZeroProp(node, prop):
185    """Add a new property to affected device trees with an integer value of 0.
186
187    Args:
188        prop_name: Name of property
189    """
190    for n in GetUpdateNodes(node):
191        n.AddZeroProp(prop)
192
193def AddSubnode(node, name):
194    """Add a new subnode to a node in affected device trees
195
196    Args:
197        node: Node to add to
198        name: name of node to add
199
200    Returns:
201        New subnode that was created in main tree
202    """
203    first = None
204    for n in GetUpdateNodes(node):
205        subnode = n.AddSubnode(name)
206        if not first:
207            first = subnode
208    return first
209
210def AddString(node, prop, value):
211    """Add a new string property to affected device trees
212
213    Args:
214        prop_name: Name of property
215        value: String value (which will be \0-terminated in the DT)
216    """
217    for n in GetUpdateNodes(node):
218        n.AddString(prop, value)
219
220def SetInt(node, prop, value):
221    """Update an integer property in affected device trees with an integer value
222
223    This is not allowed to change the size of the FDT.
224
225    Args:
226        prop_name: Name of property
227    """
228    for n in GetUpdateNodes(node):
229        n.SetInt(prop, value)
230
231def CheckAddHashProp(node):
232    hash_node = node.FindNode('hash')
233    if hash_node:
234        algo = hash_node.props.get('algo')
235        if not algo:
236            return "Missing 'algo' property for hash node"
237        if algo.value == 'sha256':
238            size = 32
239        else:
240            return "Unknown hash algorithm '%s'" % algo
241        for n in GetUpdateNodes(hash_node):
242            n.AddEmptyProp('value', size)
243
244def CheckSetHashValue(node, get_data_func):
245    hash_node = node.FindNode('hash')
246    if hash_node:
247        algo = hash_node.props.get('algo').value
248        if algo == 'sha256':
249            m = hashlib.sha256()
250            m.update(get_data_func())
251            data = m.digest()
252        for n in GetUpdateNodes(hash_node):
253            n.SetData('value', data)
254