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
13 from patman import tools
14 from patman import tout
16 # Map an dtb etype to its expected filename
18 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb',
19 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb',
22 # Records the device-tree files known to binman, keyed by entry type (e.g.
23 # 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by
24 # binman. They have been copied to <xxx>.out files.
26 # key: entry type (e.g. 'u-boot-dtb)
32 # Prefix to add to an fdtmap path to turn it into a path to the /binman node
35 # Arguments passed to binman to provide arguments to entries
38 # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
42 # The DTB which contains the full image information
45 # Allow entries to expand after they have been packed. This is detected and
46 # forces a re-pack. If not allowed, any attempted expansion causes an error in
47 # Entry.ProcessContentsUpdate()
48 allow_entry_expansion = True
50 # Don't allow entries to contract after they have been packed. Instead just
51 # leave some wasted space. If allowed, this is detected and forces a re-pack,
52 # but may result in entries that oscillate in size, thus causing a pack error.
53 # An example is a compressed device tree where the original offset values
54 # result in a larger compressed size than the new ones, but then after updating
55 # to the new ones, the compressed size increases, etc.
56 allow_entry_contraction = False
58 def GetFdtForEtype(etype):
59 """Get the Fdt object for a particular device-tree entry
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 entry and returns the associated Fdt object.
66 etype: Entry type of device tree (e.g. 'u-boot-dtb')
69 Fdt object associated with the entry type
71 value = output_fdt_info.get(etype);
76 def GetFdtPath(etype):
77 """Get the full pathname of a particular Fdt object
79 Similar to GetFdtForEtype() but returns the pathname associated with the
83 etype: Entry type of device tree (e.g. 'u-boot-dtb')
86 Full path name to the associated Fdt
88 return output_fdt_info[etype][0]._fname
90 def GetFdtContents(etype='u-boot-dtb'):
91 """Looks up the FDT pathname and contents
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
98 etype: Entry type to look up (e.g. 'u-boot.dtb').
105 if etype not in output_fdt_info:
108 pathname = GetFdtPath(etype)
109 data = GetFdtForEtype(etype).GetContents()
111 fname = output_fdt_info[etype][1]
112 pathname = tools.GetInputFilename(fname)
113 data = tools.ReadFile(pathname)
114 return pathname, data
116 def UpdateFdtContents(etype, data):
117 """Update the contents of a particular device tree
119 The device tree is updated and written back to its file. This affects what
120 is returned from future called to GetFdtContents(), etc.
123 etype: Entry type (e.g. 'u-boot-dtb')
124 data: Data to replace the DTB with
126 dtb, fname = output_fdt_info[etype]
127 dtb_fname = dtb.GetFilename()
128 tools.WriteFile(dtb_fname, data)
129 dtb = fdt.FdtScan(dtb_fname)
130 output_fdt_info[etype] = [dtb, fname]
132 def SetEntryArgs(args):
133 """Set the value of the entry args
135 This sets up the entry_args dict which is used to supply entry arguments to
139 args: List of entry arguments, each in the format "name=value"
144 tout.Debug('Processing entry args:')
147 m = re.match('([^=]*)=(.*)', arg)
149 raise ValueError("Invalid entry arguemnt '%s'" % arg)
150 name, value = m.groups()
151 tout.Debug(' %20s = %s' % (name, value))
152 entry_args[name] = value
153 tout.Debug('Processing entry args done')
155 def GetEntryArg(name):
156 """Get the value of an entry argument
159 name: Name of argument to retrieve
162 String value of argument
164 return entry_args.get(name)
166 def GetEntryArgBool(name):
167 """Get the value of an entry argument as a boolean
170 name: Name of argument to retrieve
173 False if the entry argument is consider False (empty, '0' or 'n'), else
176 val = GetEntryArg(name)
177 return val and val not in ['n', '0']
179 def Prepare(images, dtb):
180 """Get device tree files ready for use
182 This sets up a set of device tree files that can be retrieved by
183 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
186 images: List of images being used
189 global output_fdt_info, main_dtb, fdt_path_prefix
190 # Import these here in case libfdt.py is not available, in which case
191 # the above help option still works.
193 from dtoc import fdt_util
195 # If we are updating the DTBs we need to put these updated versions
196 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
197 # since it is assumed to be the one passed in with options.dt, and
198 # was handled just above.
200 output_fdt_info.clear()
202 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb']
204 for etype, fname in DTB_TYPE_FNAME.items():
205 output_fdt_info[etype] = [dtb, fname]
208 for etype, fname in DTB_TYPE_FNAME.items():
209 infile = tools.GetInputFilename(fname, allow_missing=True)
210 if infile and os.path.exists(infile):
211 fname_dtb = fdt_util.EnsureCompiled(infile)
212 out_fname = tools.GetOutputFilename('%s.out' %
213 os.path.split(fname)[1])
214 tools.WriteFile(out_fname, tools.ReadFile(fname_dtb))
215 other_dtb = fdt.FdtScan(out_fname)
216 output_fdt_info[etype] = [other_dtb, out_fname]
219 def PrepareFromLoadedData(image):
220 """Get device tree files ready for use with a loaded image
222 Loaded images are different from images that are being created by binman,
223 since there is generally already an fdtmap and we read the description from
224 that. This provides the position and size of every entry in the image with
225 no calculation required.
227 This function uses the same output_fdt_info[] as Prepare(). It finds the
228 device tree files, adds a reference to the fdtmap and sets the FDT path
229 prefix to translate from the fdtmap (where the root node is the image node)
230 to the normal device tree (where the image node is under a /binman node).
233 images: List of images being used
235 global output_fdt_info, main_dtb, fdt_path_prefix
237 tout.Info('Preparing device trees')
238 output_fdt_info.clear()
240 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb']
242 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
243 for etype, value in image.GetFdts().items():
245 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
246 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
247 (etype, out_fname, entry.GetPath()))
248 entry._filename = entry.GetDefaultFilename()
249 data = entry.ReadData()
251 tools.WriteFile(out_fname, data)
252 dtb = fdt.Fdt(out_fname)
254 image_node = dtb.GetNode('/binman')
255 if 'multiple-images' in image_node.props:
256 image_node = dtb.GetNode('/binman/%s' % image.image_node)
257 fdt_path_prefix = image_node.path
258 output_fdt_info[etype] = [dtb, None]
259 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
263 """Yield all device tree files being used by binman
266 Device trees being used (U-Boot proper, SPL, TPL)
270 for etype in output_fdt_info:
271 dtb = output_fdt_info[etype][0]
275 def GetUpdateNodes(node, for_repack=False):
276 """Yield all the nodes that need to be updated in all device trees
278 The property referenced by this node is added to any device trees which
279 have the given node. Due to removable of unwanted notes, SPL and TPL may
283 node: Node object in the main device tree to look up
284 for_repack: True if we want only nodes which need 'repack' properties
285 added to them (e.g. 'orig-offset'), False to return all nodes. We
286 don't add repack properties to SPL/TPL device trees.
289 Node objects in each device tree that is in use (U-Boot proper, which
290 is node, SPL and TPL)
293 for entry_type, (dtb, fname) in output_fdt_info.items():
294 if dtb != node.GetFdt():
295 if for_repack and entry_type != 'u-boot-dtb':
297 other_node = dtb.GetNode(fdt_path_prefix + node.path)
301 def AddZeroProp(node, prop, for_repack=False):
302 """Add a new property to affected device trees with an integer value of 0.
305 prop_name: Name of property
306 for_repack: True is this property is only needed for repacking
308 for n in GetUpdateNodes(node, for_repack):
311 def AddSubnode(node, name):
312 """Add a new subnode to a node in affected device trees
316 name: name of node to add
319 New subnode that was created in main tree
322 for n in GetUpdateNodes(node):
323 subnode = n.AddSubnode(name)
328 def AddString(node, prop, value):
329 """Add a new string property to affected device trees
332 prop_name: Name of property
333 value: String value (which will be \0-terminated in the DT)
335 for n in GetUpdateNodes(node):
336 n.AddString(prop, value)
338 def AddInt(node, prop, value):
339 """Add a new string property to affected device trees
342 prop_name: Name of property
343 val: Integer value of property
345 for n in GetUpdateNodes(node):
346 n.AddInt(prop, value)
348 def SetInt(node, prop, value, for_repack=False):
349 """Update an integer property in affected device trees with an integer value
351 This is not allowed to change the size of the FDT.
354 prop_name: Name of property
355 for_repack: True is this property is only needed for repacking
357 for n in GetUpdateNodes(node, for_repack):
358 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
359 (n.GetFdt().name, n.path, prop, value))
360 n.SetInt(prop, value)
362 def CheckAddHashProp(node):
363 hash_node = node.FindNode('hash')
365 algo = hash_node.props.get('algo')
367 return "Missing 'algo' property for hash node"
368 if algo.value == 'sha256':
371 return "Unknown hash algorithm '%s'" % algo
372 for n in GetUpdateNodes(hash_node):
373 n.AddEmptyProp('value', size)
375 def CheckSetHashValue(node, get_data_func):
376 hash_node = node.FindNode('hash')
378 algo = hash_node.props.get('algo').value
381 m.update(get_data_func())
383 for n in GetUpdateNodes(hash_node):
384 n.SetData('value', data)
386 def SetAllowEntryExpansion(allow):
387 """Set whether post-pack expansion of entries is allowed
390 allow: True to allow expansion, False to raise an exception
392 global allow_entry_expansion
394 allow_entry_expansion = allow
396 def AllowEntryExpansion():
397 """Check whether post-pack expansion of entries is allowed
400 True if expansion should be allowed, False if an exception should be
403 return allow_entry_expansion
405 def SetAllowEntryContraction(allow):
406 """Set whether post-pack contraction of entries is allowed
409 allow: True to allow contraction, False to raise an exception
411 global allow_entry_contraction
413 allow_entry_contraction = allow
415 def AllowEntryContraction():
416 """Check whether post-pack contraction of entries is allowed
419 True if contraction should be allowed, False if an exception should be
422 return allow_entry_contraction