blob: 2f567587382bb420ca43bc1365b4e98bf28fa7ef [file] [log] [blame]
Simon Glassc55a50f2018-09-14 04:57:19 -06001# 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
Simon Glasse0e5df92018-09-14 04:57:31 -06008import hashlib
Simon Glassc55a50f2018-09-14 04:57:19 -06009import re
Simon Glassc69d19c2021-07-06 10:36:37 -060010import threading
Simon Glassc55a50f2018-09-14 04:57:19 -060011
Simon Glass16287932020-04-17 18:09:03 -060012from dtoc import fdt
Simon Glassc55a50f2018-09-14 04:57:19 -060013import os
Simon Glassbf776672020-04-17 18:09:04 -060014from patman import tools
15from patman import tout
Simon Glassc55a50f2018-09-14 04:57:19 -060016
Simon Glass76971702021-03-18 20:25:00 +130017# Map an dtb etype to its expected filename
18DTB_TYPE_FNAME = {
19 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb',
20 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb',
21 }
22
Simon Glassfb5e8b12019-07-20 12:23:32 -060023# Records the device-tree files known to binman, keyed by entry type (e.g.
24# 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by
25# binman. They have been copied to <xxx>.out files.
26#
Simon Glasscb8bebb2021-03-18 20:25:01 +130027# key: entry type (e.g. 'u-boot-dtb)
Simon Glassfb5e8b12019-07-20 12:23:32 -060028# value: tuple:
29# Fdt object
30# Filename
Simon Glass6ca0dcb2019-07-20 12:23:43 -060031output_fdt_info = {}
Simon Glassc55a50f2018-09-14 04:57:19 -060032
Simon Glass10f9d002019-07-20 12:23:50 -060033# Prefix to add to an fdtmap path to turn it into a path to the /binman node
34fdt_path_prefix = ''
35
Simon Glassc55a50f2018-09-14 04:57:19 -060036# Arguments passed to binman to provide arguments to entries
37entry_args = {}
38
Simon Glass539aece2018-09-14 04:57:22 -060039# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
40# ftest.py)
Simon Glass93d17412018-09-14 04:57:23 -060041use_fake_dtb = False
Simon Glass539aece2018-09-14 04:57:22 -060042
Simon Glass2a72cc72018-09-14 04:57:20 -060043# The DTB which contains the full image information
44main_dtb = None
45
Simon Glassbf6906b2019-07-08 14:25:36 -060046# Allow entries to expand after they have been packed. This is detected and
47# forces a re-pack. If not allowed, any attempted expansion causes an error in
48# Entry.ProcessContentsUpdate()
49allow_entry_expansion = True
50
Simon Glass61ec04f2019-07-20 12:23:58 -060051# Don't allow entries to contract after they have been packed. Instead just
52# leave some wasted space. If allowed, this is detected and forces a re-pack,
53# but may result in entries that oscillate in size, thus causing a pack error.
54# An example is a compressed device tree where the original offset values
55# result in a larger compressed size than the new ones, but then after updating
56# to the new ones, the compressed size increases, etc.
57allow_entry_contraction = False
58
Simon Glassc69d19c2021-07-06 10:36:37 -060059# Number of threads to use for binman (None means machine-dependent)
60num_threads = None
61
Simon Glassfb5e8b12019-07-20 12:23:32 -060062def GetFdtForEtype(etype):
63 """Get the Fdt object for a particular device-tree entry
Simon Glassc55a50f2018-09-14 04:57:19 -060064
65 Binman keeps track of at least one device-tree file called u-boot.dtb but
66 can also have others (e.g. for SPL). This function looks up the given
Simon Glassfb5e8b12019-07-20 12:23:32 -060067 entry and returns the associated Fdt object.
Simon Glassc55a50f2018-09-14 04:57:19 -060068
69 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060070 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060071
72 Returns:
Simon Glassfb5e8b12019-07-20 12:23:32 -060073 Fdt object associated with the entry type
Simon Glassc55a50f2018-09-14 04:57:19 -060074 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060075 value = output_fdt_info.get(etype);
Simon Glass6a3b5b52019-07-20 12:23:42 -060076 if not value:
77 return None
78 return value[0]
Simon Glassc55a50f2018-09-14 04:57:19 -060079
Simon Glassfb5e8b12019-07-20 12:23:32 -060080def GetFdtPath(etype):
Simon Glassc55a50f2018-09-14 04:57:19 -060081 """Get the full pathname of a particular Fdt object
82
Simon Glass726e2962019-07-20 12:23:30 -060083 Similar to GetFdtForEtype() but returns the pathname associated with the
84 Fdt.
Simon Glassc55a50f2018-09-14 04:57:19 -060085
86 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060087 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060088
89 Returns:
90 Full path name to the associated Fdt
91 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060092 return output_fdt_info[etype][0]._fname
Simon Glassc55a50f2018-09-14 04:57:19 -060093
Simon Glassfb5e8b12019-07-20 12:23:32 -060094def GetFdtContents(etype='u-boot-dtb'):
Simon Glass6ed45ba2018-09-14 04:57:24 -060095 """Looks up the FDT pathname and contents
96
97 This is used to obtain the Fdt pathname and contents when needed by an
98 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
99 the real dtb.
100
101 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -0600102 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glass6ed45ba2018-09-14 04:57:24 -0600103
104 Returns:
105 tuple:
106 pathname to Fdt
107 Fdt data (as bytes)
108 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600109 if etype not in output_fdt_info:
Simon Glass6a3b5b52019-07-20 12:23:42 -0600110 return None, None
111 if not use_fake_dtb:
Simon Glassfb5e8b12019-07-20 12:23:32 -0600112 pathname = GetFdtPath(etype)
113 data = GetFdtForEtype(etype).GetContents()
Simon Glass6ed45ba2018-09-14 04:57:24 -0600114 else:
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600115 fname = output_fdt_info[etype][1]
Simon Glass6ed45ba2018-09-14 04:57:24 -0600116 pathname = tools.GetInputFilename(fname)
117 data = tools.ReadFile(pathname)
118 return pathname, data
119
Simon Glassf6e02492019-07-20 12:24:08 -0600120def UpdateFdtContents(etype, data):
121 """Update the contents of a particular device tree
122
123 The device tree is updated and written back to its file. This affects what
124 is returned from future called to GetFdtContents(), etc.
125
126 Args:
127 etype: Entry type (e.g. 'u-boot-dtb')
128 data: Data to replace the DTB with
129 """
Simon Glasscb8bebb2021-03-18 20:25:01 +1300130 dtb, fname = output_fdt_info[etype]
Simon Glassf6e02492019-07-20 12:24:08 -0600131 dtb_fname = dtb.GetFilename()
132 tools.WriteFile(dtb_fname, data)
133 dtb = fdt.FdtScan(dtb_fname)
Simon Glasscb8bebb2021-03-18 20:25:01 +1300134 output_fdt_info[etype] = [dtb, fname]
Simon Glassf6e02492019-07-20 12:24:08 -0600135
Simon Glassc55a50f2018-09-14 04:57:19 -0600136def SetEntryArgs(args):
137 """Set the value of the entry args
138
139 This sets up the entry_args dict which is used to supply entry arguments to
140 entries.
141
142 Args:
143 args: List of entry arguments, each in the format "name=value"
144 """
145 global entry_args
146
147 entry_args = {}
Simon Glass06684922021-03-18 20:25:07 +1300148 tout.Debug('Processing entry args:')
Simon Glassc55a50f2018-09-14 04:57:19 -0600149 if args:
150 for arg in args:
151 m = re.match('([^=]*)=(.*)', arg)
152 if not m:
153 raise ValueError("Invalid entry arguemnt '%s'" % arg)
Simon Glass06684922021-03-18 20:25:07 +1300154 name, value = m.groups()
155 tout.Debug(' %20s = %s' % (name, value))
156 entry_args[name] = value
157 tout.Debug('Processing entry args done')
Simon Glassc55a50f2018-09-14 04:57:19 -0600158
159def GetEntryArg(name):
160 """Get the value of an entry argument
161
162 Args:
163 name: Name of argument to retrieve
164
165 Returns:
166 String value of argument
167 """
168 return entry_args.get(name)
Simon Glass2a72cc72018-09-14 04:57:20 -0600169
Simon Glass06684922021-03-18 20:25:07 +1300170def GetEntryArgBool(name):
171 """Get the value of an entry argument as a boolean
172
173 Args:
174 name: Name of argument to retrieve
175
176 Returns:
177 False if the entry argument is consider False (empty, '0' or 'n'), else
178 True
179 """
180 val = GetEntryArg(name)
181 return val and val not in ['n', '0']
182
Simon Glass539aece2018-09-14 04:57:22 -0600183def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600184 """Get device tree files ready for use
185
Simon Glass4bdd1152019-07-20 12:23:29 -0600186 This sets up a set of device tree files that can be retrieved by
187 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
Simon Glass2a72cc72018-09-14 04:57:20 -0600188
189 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600190 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600191 dtb: Main dtb
192 """
Simon Glass10f9d002019-07-20 12:23:50 -0600193 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-09-14 04:57:20 -0600194 # Import these here in case libfdt.py is not available, in which case
195 # the above help option still works.
Simon Glass16287932020-04-17 18:09:03 -0600196 from dtoc import fdt
197 from dtoc import fdt_util
Simon Glass2a72cc72018-09-14 04:57:20 -0600198
199 # If we are updating the DTBs we need to put these updated versions
200 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
201 # since it is assumed to be the one passed in with options.dt, and
202 # was handled just above.
203 main_dtb = dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600204 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600205 fdt_path_prefix = ''
Simon Glasscb8bebb2021-03-18 20:25:01 +1300206 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb']
Simon Glass76971702021-03-18 20:25:00 +1300207 if use_fake_dtb:
208 for etype, fname in DTB_TYPE_FNAME.items():
Simon Glasscb8bebb2021-03-18 20:25:01 +1300209 output_fdt_info[etype] = [dtb, fname]
Simon Glass76971702021-03-18 20:25:00 +1300210 else:
Simon Glassf49462e2019-07-20 12:23:34 -0600211 fdt_set = {}
Simon Glass5187b802021-03-18 20:25:03 +1300212 for etype, fname in DTB_TYPE_FNAME.items():
213 infile = tools.GetInputFilename(fname, allow_missing=True)
214 if infile and os.path.exists(infile):
215 fname_dtb = fdt_util.EnsureCompiled(infile)
216 out_fname = tools.GetOutputFilename('%s.out' %
217 os.path.split(fname)[1])
218 tools.WriteFile(out_fname, tools.ReadFile(fname_dtb))
219 other_dtb = fdt.FdtScan(out_fname)
220 output_fdt_info[etype] = [other_dtb, out_fname]
221
Simon Glass2a72cc72018-09-14 04:57:20 -0600222
Simon Glass10f9d002019-07-20 12:23:50 -0600223def PrepareFromLoadedData(image):
224 """Get device tree files ready for use with a loaded image
225
226 Loaded images are different from images that are being created by binman,
227 since there is generally already an fdtmap and we read the description from
228 that. This provides the position and size of every entry in the image with
229 no calculation required.
230
231 This function uses the same output_fdt_info[] as Prepare(). It finds the
232 device tree files, adds a reference to the fdtmap and sets the FDT path
233 prefix to translate from the fdtmap (where the root node is the image node)
234 to the normal device tree (where the image node is under a /binman node).
235
236 Args:
237 images: List of images being used
238 """
239 global output_fdt_info, main_dtb, fdt_path_prefix
240
241 tout.Info('Preparing device trees')
242 output_fdt_info.clear()
243 fdt_path_prefix = ''
Simon Glasscb8bebb2021-03-18 20:25:01 +1300244 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb']
Simon Glass10f9d002019-07-20 12:23:50 -0600245 main_dtb = None
246 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
247 for etype, value in image.GetFdts().items():
248 entry, fname = value
249 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
250 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
251 (etype, out_fname, entry.GetPath()))
252 entry._filename = entry.GetDefaultFilename()
253 data = entry.ReadData()
254
255 tools.WriteFile(out_fname, data)
256 dtb = fdt.Fdt(out_fname)
257 dtb.Scan()
258 image_node = dtb.GetNode('/binman')
259 if 'multiple-images' in image_node.props:
260 image_node = dtb.GetNode('/binman/%s' % image.image_node)
261 fdt_path_prefix = image_node.path
Simon Glasscb8bebb2021-03-18 20:25:01 +1300262 output_fdt_info[etype] = [dtb, None]
Simon Glass10f9d002019-07-20 12:23:50 -0600263 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
264
265
Simon Glass4bdd1152019-07-20 12:23:29 -0600266def GetAllFdts():
Simon Glass2a72cc72018-09-14 04:57:20 -0600267 """Yield all device tree files being used by binman
268
269 Yields:
270 Device trees being used (U-Boot proper, SPL, TPL)
271 """
Simon Glass10f9d002019-07-20 12:23:50 -0600272 if main_dtb:
273 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600274 for etype in output_fdt_info:
275 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600276 if dtb != main_dtb:
277 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600278
Simon Glass12bb1a92019-07-20 12:23:51 -0600279def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600280 """Yield all the nodes that need to be updated in all device trees
281
282 The property referenced by this node is added to any device trees which
283 have the given node. Due to removable of unwanted notes, SPL and TPL may
284 not have this node.
285
286 Args:
287 node: Node object in the main device tree to look up
Simon Glass12bb1a92019-07-20 12:23:51 -0600288 for_repack: True if we want only nodes which need 'repack' properties
289 added to them (e.g. 'orig-offset'), False to return all nodes. We
290 don't add repack properties to SPL/TPL device trees.
Simon Glassf46621d2018-09-14 04:57:21 -0600291
292 Yields:
293 Node objects in each device tree that is in use (U-Boot proper, which
294 is node, SPL and TPL)
295 """
296 yield node
Simon Glasscb8bebb2021-03-18 20:25:01 +1300297 for entry_type, (dtb, fname) in output_fdt_info.items():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600298 if dtb != node.GetFdt():
Simon Glasscb8bebb2021-03-18 20:25:01 +1300299 if for_repack and entry_type != 'u-boot-dtb':
Simon Glass12bb1a92019-07-20 12:23:51 -0600300 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600301 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600302 if other_node:
303 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600304
Simon Glass12bb1a92019-07-20 12:23:51 -0600305def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600306 """Add a new property to affected device trees with an integer value of 0.
307
308 Args:
309 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600310 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600311 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600312 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600313 n.AddZeroProp(prop)
314
Simon Glass0a98b282018-09-14 04:57:28 -0600315def AddSubnode(node, name):
316 """Add a new subnode to a node in affected device trees
317
318 Args:
319 node: Node to add to
320 name: name of node to add
321
322 Returns:
323 New subnode that was created in main tree
324 """
325 first = None
326 for n in GetUpdateNodes(node):
327 subnode = n.AddSubnode(name)
328 if not first:
329 first = subnode
330 return first
331
332def AddString(node, prop, value):
333 """Add a new string property to affected device trees
334
335 Args:
336 prop_name: Name of property
337 value: String value (which will be \0-terminated in the DT)
338 """
339 for n in GetUpdateNodes(node):
340 n.AddString(prop, value)
341
Simon Glass6eb99322021-01-06 21:35:18 -0700342def AddInt(node, prop, value):
343 """Add a new string property to affected device trees
344
345 Args:
346 prop_name: Name of property
347 val: Integer value of property
348 """
349 for n in GetUpdateNodes(node):
350 n.AddInt(prop, value)
351
Simon Glass12bb1a92019-07-20 12:23:51 -0600352def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600353 """Update an integer property in affected device trees with an integer value
354
355 This is not allowed to change the size of the FDT.
356
357 Args:
358 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600359 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600360 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600361 for n in GetUpdateNodes(node, for_repack):
362 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glass51014aa2019-07-20 12:23:56 -0600363 (n.GetFdt().name, n.path, prop, value))
Simon Glassf46621d2018-09-14 04:57:21 -0600364 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600365
366def CheckAddHashProp(node):
367 hash_node = node.FindNode('hash')
368 if hash_node:
369 algo = hash_node.props.get('algo')
370 if not algo:
371 return "Missing 'algo' property for hash node"
372 if algo.value == 'sha256':
373 size = 32
374 else:
375 return "Unknown hash algorithm '%s'" % algo
376 for n in GetUpdateNodes(hash_node):
377 n.AddEmptyProp('value', size)
378
379def CheckSetHashValue(node, get_data_func):
380 hash_node = node.FindNode('hash')
381 if hash_node:
382 algo = hash_node.props.get('algo').value
383 if algo == 'sha256':
384 m = hashlib.sha256()
385 m.update(get_data_func())
386 data = m.digest()
387 for n in GetUpdateNodes(hash_node):
388 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600389
390def SetAllowEntryExpansion(allow):
391 """Set whether post-pack expansion of entries is allowed
392
393 Args:
394 allow: True to allow expansion, False to raise an exception
395 """
396 global allow_entry_expansion
397
398 allow_entry_expansion = allow
399
400def AllowEntryExpansion():
401 """Check whether post-pack expansion of entries is allowed
402
403 Returns:
404 True if expansion should be allowed, False if an exception should be
405 raised
406 """
407 return allow_entry_expansion
Simon Glass61ec04f2019-07-20 12:23:58 -0600408
409def SetAllowEntryContraction(allow):
410 """Set whether post-pack contraction of entries is allowed
411
412 Args:
413 allow: True to allow contraction, False to raise an exception
414 """
415 global allow_entry_contraction
416
417 allow_entry_contraction = allow
418
419def AllowEntryContraction():
420 """Check whether post-pack contraction of entries is allowed
421
422 Returns:
423 True if contraction should be allowed, False if an exception should be
424 raised
425 """
426 return allow_entry_contraction
Simon Glassc69d19c2021-07-06 10:36:37 -0600427
428def SetThreads(threads):
429 """Set the number of threads to use when building sections
430
431 Args:
432 threads: Number of threads to use (None for default, 0 for
433 single-threaded)
434 """
435 global num_threads
436
437 num_threads = threads
438
439def GetThreads():
440 """Get the number of threads to use when building sections
441
442 Returns:
443 Number of threads to use (None for default, 0 for single-threaded)
444 """
445 return num_threads