binman: Support hashing entries
[oweals/u-boot.git] / tools / binman / state.py
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
8 import hashlib
9 import re
10 from sets import Set
11
12 import os
13 import tools
14
15 # Records the device-tree files known to binman, keyed by filename (e.g.
16 # 'u-boot-spl.dtb')
17 fdt_files = {}
18
19 # Arguments passed to binman to provide arguments to entries
20 entry_args = {}
21
22 # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
23 # ftest.py)
24 use_fake_dtb = False
25
26 # Set of all device tree files references by images
27 fdt_set = Set()
28
29 # Same as above, but excluding the main one
30 fdt_subset = Set()
31
32 # The DTB which contains the full image information
33 main_dtb = None
34
35 def 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
50 def 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
63 def 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
86 def 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
105 def 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
116 def 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
153 def 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
163 def 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
184 def 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
193 def 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
210 def 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
220 def 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
231 def 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
244 def 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)