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