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