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