1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright 2018 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Holds and modifies the state information held by binman
16 # Records the device-tree files known to binman, keyed by entry type (e.g.
17 # 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by
18 # binman. They have been copied to <xxx>.out files.
24 # Entry object, or None if not known
27 # Prefix to add to an fdtmap path to turn it into a path to the /binman node
30 # Arguments passed to binman to provide arguments to entries
33 # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
37 # The DTB which contains the full image information
40 # Allow entries to expand after they have been packed. This is detected and
41 # forces a re-pack. If not allowed, any attempted expansion causes an error in
42 # Entry.ProcessContentsUpdate()
43 allow_entry_expansion = True
45 def GetFdtForEtype(etype):
46 """Get the Fdt object for a particular device-tree entry
48 Binman keeps track of at least one device-tree file called u-boot.dtb but
49 can also have others (e.g. for SPL). This function looks up the given
50 entry and returns the associated Fdt object.
53 etype: Entry type of device tree (e.g. 'u-boot-dtb')
56 Fdt object associated with the entry type
58 value = output_fdt_info.get(etype);
63 def GetFdtPath(etype):
64 """Get the full pathname of a particular Fdt object
66 Similar to GetFdtForEtype() but returns the pathname associated with the
70 etype: Entry type of device tree (e.g. 'u-boot-dtb')
73 Full path name to the associated Fdt
75 return output_fdt_info[etype][0]._fname
77 def GetFdtContents(etype='u-boot-dtb'):
78 """Looks up the FDT pathname and contents
80 This is used to obtain the Fdt pathname and contents when needed by an
81 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
85 etype: Entry type to look up (e.g. 'u-boot.dtb').
92 if etype not in output_fdt_info:
95 pathname = GetFdtPath(etype)
96 data = GetFdtForEtype(etype).GetContents()
98 fname = output_fdt_info[etype][1]
99 pathname = tools.GetInputFilename(fname)
100 data = tools.ReadFile(pathname)
101 return pathname, data
103 def SetEntryArgs(args):
104 """Set the value of the entry args
106 This sets up the entry_args dict which is used to supply entry arguments to
110 args: List of entry arguments, each in the format "name=value"
117 m = re.match('([^=]*)=(.*)', arg)
119 raise ValueError("Invalid entry arguemnt '%s'" % arg)
120 entry_args[m.group(1)] = m.group(2)
122 def GetEntryArg(name):
123 """Get the value of an entry argument
126 name: Name of argument to retrieve
129 String value of argument
131 return entry_args.get(name)
133 def Prepare(images, dtb):
134 """Get device tree files ready for use
136 This sets up a set of device tree files that can be retrieved by
137 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
140 images: List of images being used
143 global output_fdt_info, main_dtb, fdt_path_prefix
144 # Import these here in case libfdt.py is not available, in which case
145 # the above help option still works.
149 # If we are updating the DTBs we need to put these updated versions
150 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
151 # since it is assumed to be the one passed in with options.dt, and
152 # was handled just above.
154 output_fdt_info.clear()
156 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
157 output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
158 output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
161 for image in images.values():
162 fdt_set.update(image.GetFdts())
163 for etype, other in fdt_set.items():
164 entry, other_fname = other
165 infile = tools.GetInputFilename(other_fname)
166 other_fname_dtb = fdt_util.EnsureCompiled(infile)
167 out_fname = tools.GetOutputFilename('%s.out' %
168 os.path.split(other_fname)[1])
169 tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
170 other_dtb = fdt.FdtScan(out_fname)
171 output_fdt_info[etype] = [other_dtb, out_fname, entry]
173 def PrepareFromLoadedData(image):
174 """Get device tree files ready for use with a loaded image
176 Loaded images are different from images that are being created by binman,
177 since there is generally already an fdtmap and we read the description from
178 that. This provides the position and size of every entry in the image with
179 no calculation required.
181 This function uses the same output_fdt_info[] as Prepare(). It finds the
182 device tree files, adds a reference to the fdtmap and sets the FDT path
183 prefix to translate from the fdtmap (where the root node is the image node)
184 to the normal device tree (where the image node is under a /binman node).
187 images: List of images being used
189 global output_fdt_info, main_dtb, fdt_path_prefix
191 tout.Info('Preparing device trees')
192 output_fdt_info.clear()
194 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
196 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
197 for etype, value in image.GetFdts().items():
199 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
200 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
201 (etype, out_fname, entry.GetPath()))
202 entry._filename = entry.GetDefaultFilename()
203 data = entry.ReadData()
205 tools.WriteFile(out_fname, data)
206 dtb = fdt.Fdt(out_fname)
208 image_node = dtb.GetNode('/binman')
209 if 'multiple-images' in image_node.props:
210 image_node = dtb.GetNode('/binman/%s' % image.image_node)
211 fdt_path_prefix = image_node.path
212 output_fdt_info[etype] = [dtb, None, entry]
213 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
217 """Yield all device tree files being used by binman
220 Device trees being used (U-Boot proper, SPL, TPL)
224 for etype in output_fdt_info:
225 dtb = output_fdt_info[etype][0]
229 def GetUpdateNodes(node, for_repack=False):
230 """Yield all the nodes that need to be updated in all device trees
232 The property referenced by this node is added to any device trees which
233 have the given node. Due to removable of unwanted notes, SPL and TPL may
237 node: Node object in the main device tree to look up
238 for_repack: True if we want only nodes which need 'repack' properties
239 added to them (e.g. 'orig-offset'), False to return all nodes. We
240 don't add repack properties to SPL/TPL device trees.
243 Node objects in each device tree that is in use (U-Boot proper, which
244 is node, SPL and TPL)
247 for dtb, fname, entry in output_fdt_info.values():
248 if dtb != node.GetFdt():
249 if for_repack and entry.etype != 'u-boot-dtb':
251 other_node = dtb.GetNode(fdt_path_prefix + node.path)
255 def AddZeroProp(node, prop, for_repack=False):
256 """Add a new property to affected device trees with an integer value of 0.
259 prop_name: Name of property
260 for_repack: True is this property is only needed for repacking
262 for n in GetUpdateNodes(node, for_repack):
265 def AddSubnode(node, name):
266 """Add a new subnode to a node in affected device trees
270 name: name of node to add
273 New subnode that was created in main tree
276 for n in GetUpdateNodes(node):
277 subnode = n.AddSubnode(name)
282 def AddString(node, prop, value):
283 """Add a new string property to affected device trees
286 prop_name: Name of property
287 value: String value (which will be \0-terminated in the DT)
289 for n in GetUpdateNodes(node):
290 n.AddString(prop, value)
292 def SetInt(node, prop, value, for_repack=False):
293 """Update an integer property in affected device trees with an integer value
295 This is not allowed to change the size of the FDT.
298 prop_name: Name of property
299 for_repack: True is this property is only needed for repacking
301 for n in GetUpdateNodes(node, for_repack):
302 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
303 (node.GetFdt().name, node.path, prop, value))
304 n.SetInt(prop, value)
306 def CheckAddHashProp(node):
307 hash_node = node.FindNode('hash')
309 algo = hash_node.props.get('algo')
311 return "Missing 'algo' property for hash node"
312 if algo.value == 'sha256':
315 return "Unknown hash algorithm '%s'" % algo
316 for n in GetUpdateNodes(hash_node):
317 n.AddEmptyProp('value', size)
319 def CheckSetHashValue(node, get_data_func):
320 hash_node = node.FindNode('hash')
322 algo = hash_node.props.get('algo').value
325 m.update(get_data_func())
327 for n in GetUpdateNodes(hash_node):
328 n.SetData('value', data)
330 def SetAllowEntryExpansion(allow):
331 """Set whether post-pack expansion of entries is allowed
334 allow: True to allow expansion, False to raise an exception
336 global allow_entry_expansion
338 allow_entry_expansion = allow
340 def AllowEntryExpansion():
341 """Check whether post-pack expansion of entries is allowed
344 True if expansion should be allowed, False if an exception should be
347 return allow_entry_expansion