1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright 2018 Google, Inc
5 # Holds and modifies the state information held by binman
13 from patman import tools
14 from patman import tout
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 # Don't allow entries to contract after they have been packed. Instead just
46 # leave some wasted space. If allowed, this is detected and forces a re-pack,
47 # but may result in entries that oscillate in size, thus causing a pack error.
48 # An example is a compressed device tree where the original offset values
49 # result in a larger compressed size than the new ones, but then after updating
50 # to the new ones, the compressed size increases, etc.
51 allow_entry_contraction = False
53 def GetFdtForEtype(etype):
54 """Get the Fdt object for a particular device-tree entry
56 Binman keeps track of at least one device-tree file called u-boot.dtb but
57 can also have others (e.g. for SPL). This function looks up the given
58 entry and returns the associated Fdt object.
61 etype: Entry type of device tree (e.g. 'u-boot-dtb')
64 Fdt object associated with the entry type
66 value = output_fdt_info.get(etype);
71 def GetFdtPath(etype):
72 """Get the full pathname of a particular Fdt object
74 Similar to GetFdtForEtype() but returns the pathname associated with the
78 etype: Entry type of device tree (e.g. 'u-boot-dtb')
81 Full path name to the associated Fdt
83 return output_fdt_info[etype][0]._fname
85 def GetFdtContents(etype='u-boot-dtb'):
86 """Looks up the FDT pathname and contents
88 This is used to obtain the Fdt pathname and contents when needed by an
89 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
93 etype: Entry type to look up (e.g. 'u-boot.dtb').
100 if etype not in output_fdt_info:
103 pathname = GetFdtPath(etype)
104 data = GetFdtForEtype(etype).GetContents()
106 fname = output_fdt_info[etype][1]
107 pathname = tools.GetInputFilename(fname)
108 data = tools.ReadFile(pathname)
109 return pathname, data
111 def UpdateFdtContents(etype, data):
112 """Update the contents of a particular device tree
114 The device tree is updated and written back to its file. This affects what
115 is returned from future called to GetFdtContents(), etc.
118 etype: Entry type (e.g. 'u-boot-dtb')
119 data: Data to replace the DTB with
121 dtb, fname, entry = output_fdt_info[etype]
122 dtb_fname = dtb.GetFilename()
123 tools.WriteFile(dtb_fname, data)
124 dtb = fdt.FdtScan(dtb_fname)
125 output_fdt_info[etype] = [dtb, fname, entry]
127 def SetEntryArgs(args):
128 """Set the value of the entry args
130 This sets up the entry_args dict which is used to supply entry arguments to
134 args: List of entry arguments, each in the format "name=value"
141 m = re.match('([^=]*)=(.*)', arg)
143 raise ValueError("Invalid entry arguemnt '%s'" % arg)
144 entry_args[m.group(1)] = m.group(2)
146 def GetEntryArg(name):
147 """Get the value of an entry argument
150 name: Name of argument to retrieve
153 String value of argument
155 return entry_args.get(name)
157 def Prepare(images, dtb):
158 """Get device tree files ready for use
160 This sets up a set of device tree files that can be retrieved by
161 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
164 images: List of images being used
167 global output_fdt_info, main_dtb, fdt_path_prefix
168 # Import these here in case libfdt.py is not available, in which case
169 # the above help option still works.
171 from dtoc import fdt_util
173 # If we are updating the DTBs we need to put these updated versions
174 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
175 # since it is assumed to be the one passed in with options.dt, and
176 # was handled just above.
178 output_fdt_info.clear()
180 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
181 output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
182 output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
185 for image in images.values():
186 fdt_set.update(image.GetFdts())
187 for etype, other in fdt_set.items():
188 entry, other_fname = other
189 infile = tools.GetInputFilename(other_fname)
190 other_fname_dtb = fdt_util.EnsureCompiled(infile)
191 out_fname = tools.GetOutputFilename('%s.out' %
192 os.path.split(other_fname)[1])
193 tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
194 other_dtb = fdt.FdtScan(out_fname)
195 output_fdt_info[etype] = [other_dtb, out_fname, entry]
197 def PrepareFromLoadedData(image):
198 """Get device tree files ready for use with a loaded image
200 Loaded images are different from images that are being created by binman,
201 since there is generally already an fdtmap and we read the description from
202 that. This provides the position and size of every entry in the image with
203 no calculation required.
205 This function uses the same output_fdt_info[] as Prepare(). It finds the
206 device tree files, adds a reference to the fdtmap and sets the FDT path
207 prefix to translate from the fdtmap (where the root node is the image node)
208 to the normal device tree (where the image node is under a /binman node).
211 images: List of images being used
213 global output_fdt_info, main_dtb, fdt_path_prefix
215 tout.Info('Preparing device trees')
216 output_fdt_info.clear()
218 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
220 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
221 for etype, value in image.GetFdts().items():
223 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
224 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
225 (etype, out_fname, entry.GetPath()))
226 entry._filename = entry.GetDefaultFilename()
227 data = entry.ReadData()
229 tools.WriteFile(out_fname, data)
230 dtb = fdt.Fdt(out_fname)
232 image_node = dtb.GetNode('/binman')
233 if 'multiple-images' in image_node.props:
234 image_node = dtb.GetNode('/binman/%s' % image.image_node)
235 fdt_path_prefix = image_node.path
236 output_fdt_info[etype] = [dtb, None, entry]
237 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
241 """Yield all device tree files being used by binman
244 Device trees being used (U-Boot proper, SPL, TPL)
248 for etype in output_fdt_info:
249 dtb = output_fdt_info[etype][0]
253 def GetUpdateNodes(node, for_repack=False):
254 """Yield all the nodes that need to be updated in all device trees
256 The property referenced by this node is added to any device trees which
257 have the given node. Due to removable of unwanted notes, SPL and TPL may
261 node: Node object in the main device tree to look up
262 for_repack: True if we want only nodes which need 'repack' properties
263 added to them (e.g. 'orig-offset'), False to return all nodes. We
264 don't add repack properties to SPL/TPL device trees.
267 Node objects in each device tree that is in use (U-Boot proper, which
268 is node, SPL and TPL)
271 for dtb, fname, entry in output_fdt_info.values():
272 if dtb != node.GetFdt():
273 if for_repack and entry.etype != 'u-boot-dtb':
275 other_node = dtb.GetNode(fdt_path_prefix + node.path)
276 #print(' try', fdt_path_prefix + node.path, other_node)
280 def AddZeroProp(node, prop, for_repack=False):
281 """Add a new property to affected device trees with an integer value of 0.
284 prop_name: Name of property
285 for_repack: True is this property is only needed for repacking
287 for n in GetUpdateNodes(node, for_repack):
290 def AddSubnode(node, name):
291 """Add a new subnode to a node in affected device trees
295 name: name of node to add
298 New subnode that was created in main tree
301 for n in GetUpdateNodes(node):
302 subnode = n.AddSubnode(name)
307 def AddString(node, prop, value):
308 """Add a new string property to affected device trees
311 prop_name: Name of property
312 value: String value (which will be \0-terminated in the DT)
314 for n in GetUpdateNodes(node):
315 n.AddString(prop, value)
317 def SetInt(node, prop, value, for_repack=False):
318 """Update an integer property in affected device trees with an integer value
320 This is not allowed to change the size of the FDT.
323 prop_name: Name of property
324 for_repack: True is this property is only needed for repacking
326 for n in GetUpdateNodes(node, for_repack):
327 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
328 (n.GetFdt().name, n.path, prop, value))
329 n.SetInt(prop, value)
331 def CheckAddHashProp(node):
332 hash_node = node.FindNode('hash')
334 algo = hash_node.props.get('algo')
336 return "Missing 'algo' property for hash node"
337 if algo.value == 'sha256':
340 return "Unknown hash algorithm '%s'" % algo
341 for n in GetUpdateNodes(hash_node):
342 n.AddEmptyProp('value', size)
344 def CheckSetHashValue(node, get_data_func):
345 hash_node = node.FindNode('hash')
347 algo = hash_node.props.get('algo').value
350 m.update(get_data_func())
352 for n in GetUpdateNodes(hash_node):
353 n.SetData('value', data)
355 def SetAllowEntryExpansion(allow):
356 """Set whether post-pack expansion of entries is allowed
359 allow: True to allow expansion, False to raise an exception
361 global allow_entry_expansion
363 allow_entry_expansion = allow
365 def AllowEntryExpansion():
366 """Check whether post-pack expansion of entries is allowed
369 True if expansion should be allowed, False if an exception should be
372 return allow_entry_expansion
374 def SetAllowEntryContraction(allow):
375 """Set whether post-pack contraction of entries is allowed
378 allow: True to allow contraction, False to raise an exception
380 global allow_entry_contraction
382 allow_entry_contraction = allow
384 def AllowEntryContraction():
385 """Check whether post-pack contraction of entries is allowed
388 True if contraction should be allowed, False if an exception should be
391 return allow_entry_contraction