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