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