blob: dfb176045548d707e96edc517bf355ee39abcbab [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 Glassc55a50f2018-09-14 04:57:19 -060010
Simon Glass16287932020-04-17 18:09:03 -060011from dtoc import fdt
Simon Glassc55a50f2018-09-14 04:57:19 -060012import os
Simon Glassbf776672020-04-17 18:09:04 -060013from patman import tools
14from patman import tout
Simon Glassc55a50f2018-09-14 04:57:19 -060015
Simon Glass76971702021-03-18 20:25:00 +130016# Map an dtb etype to its expected filename
17DTB_TYPE_FNAME = {
18 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb',
19 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb',
20 }
21
Simon Glassfb5e8b12019-07-20 12:23:32 -060022# 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.
25#
Simon Glasscb8bebb2021-03-18 20:25:01 +130026# key: entry type (e.g. 'u-boot-dtb)
Simon Glassfb5e8b12019-07-20 12:23:32 -060027# value: tuple:
28# Fdt object
29# Filename
Simon Glass6ca0dcb2019-07-20 12:23:43 -060030output_fdt_info = {}
Simon Glassc55a50f2018-09-14 04:57:19 -060031
Simon Glass10f9d002019-07-20 12:23:50 -060032# Prefix to add to an fdtmap path to turn it into a path to the /binman node
33fdt_path_prefix = ''
34
Simon Glassc55a50f2018-09-14 04:57:19 -060035# Arguments passed to binman to provide arguments to entries
36entry_args = {}
37
Simon Glass539aece2018-09-14 04:57:22 -060038# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
39# ftest.py)
Simon Glass93d17412018-09-14 04:57:23 -060040use_fake_dtb = False
Simon Glass539aece2018-09-14 04:57:22 -060041
Simon Glass2a72cc72018-09-14 04:57:20 -060042# The DTB which contains the full image information
43main_dtb = None
44
Simon Glassbf6906b2019-07-08 14:25:36 -060045# 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()
48allow_entry_expansion = True
49
Simon Glass61ec04f2019-07-20 12:23:58 -060050# 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.
56allow_entry_contraction = False
57
Simon Glassfb5e8b12019-07-20 12:23:32 -060058def GetFdtForEtype(etype):
59 """Get the Fdt object for a particular device-tree entry
Simon Glassc55a50f2018-09-14 04:57:19 -060060
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
Simon Glassfb5e8b12019-07-20 12:23:32 -060063 entry and returns the associated Fdt object.
Simon Glassc55a50f2018-09-14 04:57:19 -060064
65 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060066 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060067
68 Returns:
Simon Glassfb5e8b12019-07-20 12:23:32 -060069 Fdt object associated with the entry type
Simon Glassc55a50f2018-09-14 04:57:19 -060070 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060071 value = output_fdt_info.get(etype);
Simon Glass6a3b5b52019-07-20 12:23:42 -060072 if not value:
73 return None
74 return value[0]
Simon Glassc55a50f2018-09-14 04:57:19 -060075
Simon Glassfb5e8b12019-07-20 12:23:32 -060076def GetFdtPath(etype):
Simon Glassc55a50f2018-09-14 04:57:19 -060077 """Get the full pathname of a particular Fdt object
78
Simon Glass726e2962019-07-20 12:23:30 -060079 Similar to GetFdtForEtype() but returns the pathname associated with the
80 Fdt.
Simon Glassc55a50f2018-09-14 04:57:19 -060081
82 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060083 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060084
85 Returns:
86 Full path name to the associated Fdt
87 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060088 return output_fdt_info[etype][0]._fname
Simon Glassc55a50f2018-09-14 04:57:19 -060089
Simon Glassfb5e8b12019-07-20 12:23:32 -060090def GetFdtContents(etype='u-boot-dtb'):
Simon Glass6ed45ba2018-09-14 04:57:24 -060091 """Looks up the FDT pathname and contents
92
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
95 the real dtb.
96
97 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060098 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glass6ed45ba2018-09-14 04:57:24 -060099
100 Returns:
101 tuple:
102 pathname to Fdt
103 Fdt data (as bytes)
104 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600105 if etype not in output_fdt_info:
Simon Glass6a3b5b52019-07-20 12:23:42 -0600106 return None, None
107 if not use_fake_dtb:
Simon Glassfb5e8b12019-07-20 12:23:32 -0600108 pathname = GetFdtPath(etype)
109 data = GetFdtForEtype(etype).GetContents()
Simon Glass6ed45ba2018-09-14 04:57:24 -0600110 else:
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600111 fname = output_fdt_info[etype][1]
Simon Glass6ed45ba2018-09-14 04:57:24 -0600112 pathname = tools.GetInputFilename(fname)
113 data = tools.ReadFile(pathname)
114 return pathname, data
115
Simon Glassf6e02492019-07-20 12:24:08 -0600116def UpdateFdtContents(etype, data):
117 """Update the contents of a particular device tree
118
119 The device tree is updated and written back to its file. This affects what
120 is returned from future called to GetFdtContents(), etc.
121
122 Args:
123 etype: Entry type (e.g. 'u-boot-dtb')
124 data: Data to replace the DTB with
125 """
Simon Glasscb8bebb2021-03-18 20:25:01 +1300126 dtb, fname = output_fdt_info[etype]
Simon Glassf6e02492019-07-20 12:24:08 -0600127 dtb_fname = dtb.GetFilename()
128 tools.WriteFile(dtb_fname, data)
129 dtb = fdt.FdtScan(dtb_fname)
Simon Glasscb8bebb2021-03-18 20:25:01 +1300130 output_fdt_info[etype] = [dtb, fname]
Simon Glassf6e02492019-07-20 12:24:08 -0600131
Simon Glassc55a50f2018-09-14 04:57:19 -0600132def SetEntryArgs(args):
133 """Set the value of the entry args
134
135 This sets up the entry_args dict which is used to supply entry arguments to
136 entries.
137
138 Args:
139 args: List of entry arguments, each in the format "name=value"
140 """
141 global entry_args
142
143 entry_args = {}
Simon Glass06684922021-03-18 20:25:07 +1300144 tout.Debug('Processing entry args:')
Simon Glassc55a50f2018-09-14 04:57:19 -0600145 if args:
146 for arg in args:
147 m = re.match('([^=]*)=(.*)', arg)
148 if not m:
149 raise ValueError("Invalid entry arguemnt '%s'" % arg)
Simon Glass06684922021-03-18 20:25:07 +1300150 name, value = m.groups()
151 tout.Debug(' %20s = %s' % (name, value))
152 entry_args[name] = value
153 tout.Debug('Processing entry args done')
Simon Glassc55a50f2018-09-14 04:57:19 -0600154
155def GetEntryArg(name):
156 """Get the value of an entry argument
157
158 Args:
159 name: Name of argument to retrieve
160
161 Returns:
162 String value of argument
163 """
164 return entry_args.get(name)
Simon Glass2a72cc72018-09-14 04:57:20 -0600165
Simon Glass06684922021-03-18 20:25:07 +1300166def GetEntryArgBool(name):
167 """Get the value of an entry argument as a boolean
168
169 Args:
170 name: Name of argument to retrieve
171
172 Returns:
173 False if the entry argument is consider False (empty, '0' or 'n'), else
174 True
175 """
176 val = GetEntryArg(name)
177 return val and val not in ['n', '0']
178
Simon Glass539aece2018-09-14 04:57:22 -0600179def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600180 """Get device tree files ready for use
181
Simon Glass4bdd1152019-07-20 12:23:29 -0600182 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.
Simon Glass2a72cc72018-09-14 04:57:20 -0600184
185 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600186 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600187 dtb: Main dtb
188 """
Simon Glass10f9d002019-07-20 12:23:50 -0600189 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-09-14 04:57:20 -0600190 # Import these here in case libfdt.py is not available, in which case
191 # the above help option still works.
Simon Glass16287932020-04-17 18:09:03 -0600192 from dtoc import fdt
193 from dtoc import fdt_util
Simon Glass2a72cc72018-09-14 04:57:20 -0600194
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.
199 main_dtb = dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600200 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600201 fdt_path_prefix = ''
Simon Glasscb8bebb2021-03-18 20:25:01 +1300202 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb']
Simon Glass76971702021-03-18 20:25:00 +1300203 if use_fake_dtb:
204 for etype, fname in DTB_TYPE_FNAME.items():
Simon Glasscb8bebb2021-03-18 20:25:01 +1300205 output_fdt_info[etype] = [dtb, fname]
Simon Glass76971702021-03-18 20:25:00 +1300206 else:
Simon Glassf49462e2019-07-20 12:23:34 -0600207 fdt_set = {}
Simon Glass5187b802021-03-18 20:25:03 +1300208 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]
217
Simon Glass2a72cc72018-09-14 04:57:20 -0600218
Simon Glass10f9d002019-07-20 12:23:50 -0600219def PrepareFromLoadedData(image):
220 """Get device tree files ready for use with a loaded image
221
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.
226
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).
231
232 Args:
233 images: List of images being used
234 """
235 global output_fdt_info, main_dtb, fdt_path_prefix
236
237 tout.Info('Preparing device trees')
238 output_fdt_info.clear()
239 fdt_path_prefix = ''
Simon Glasscb8bebb2021-03-18 20:25:01 +1300240 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb']
Simon Glass10f9d002019-07-20 12:23:50 -0600241 main_dtb = None
242 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
243 for etype, value in image.GetFdts().items():
244 entry, fname = value
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()
250
251 tools.WriteFile(out_fname, data)
252 dtb = fdt.Fdt(out_fname)
253 dtb.Scan()
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
Simon Glasscb8bebb2021-03-18 20:25:01 +1300258 output_fdt_info[etype] = [dtb, None]
Simon Glass10f9d002019-07-20 12:23:50 -0600259 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
260
261
Simon Glass4bdd1152019-07-20 12:23:29 -0600262def GetAllFdts():
Simon Glass2a72cc72018-09-14 04:57:20 -0600263 """Yield all device tree files being used by binman
264
265 Yields:
266 Device trees being used (U-Boot proper, SPL, TPL)
267 """
Simon Glass10f9d002019-07-20 12:23:50 -0600268 if main_dtb:
269 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600270 for etype in output_fdt_info:
271 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600272 if dtb != main_dtb:
273 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600274
Simon Glass12bb1a92019-07-20 12:23:51 -0600275def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600276 """Yield all the nodes that need to be updated in all device trees
277
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
280 not have this node.
281
282 Args:
283 node: Node object in the main device tree to look up
Simon Glass12bb1a92019-07-20 12:23:51 -0600284 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.
Simon Glassf46621d2018-09-14 04:57:21 -0600287
288 Yields:
289 Node objects in each device tree that is in use (U-Boot proper, which
290 is node, SPL and TPL)
291 """
292 yield node
Simon Glasscb8bebb2021-03-18 20:25:01 +1300293 for entry_type, (dtb, fname) in output_fdt_info.items():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600294 if dtb != node.GetFdt():
Simon Glasscb8bebb2021-03-18 20:25:01 +1300295 if for_repack and entry_type != 'u-boot-dtb':
Simon Glass12bb1a92019-07-20 12:23:51 -0600296 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600297 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600298 if other_node:
299 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600300
Simon Glass12bb1a92019-07-20 12:23:51 -0600301def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600302 """Add a new property to affected device trees with an integer value of 0.
303
304 Args:
305 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600306 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600307 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600308 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600309 n.AddZeroProp(prop)
310
Simon Glass0a98b282018-09-14 04:57:28 -0600311def AddSubnode(node, name):
312 """Add a new subnode to a node in affected device trees
313
314 Args:
315 node: Node to add to
316 name: name of node to add
317
318 Returns:
319 New subnode that was created in main tree
320 """
321 first = None
322 for n in GetUpdateNodes(node):
323 subnode = n.AddSubnode(name)
324 if not first:
325 first = subnode
326 return first
327
328def AddString(node, prop, value):
329 """Add a new string property to affected device trees
330
331 Args:
332 prop_name: Name of property
333 value: String value (which will be \0-terminated in the DT)
334 """
335 for n in GetUpdateNodes(node):
336 n.AddString(prop, value)
337
Simon Glass6eb99322021-01-06 21:35:18 -0700338def AddInt(node, prop, value):
339 """Add a new string property to affected device trees
340
341 Args:
342 prop_name: Name of property
343 val: Integer value of property
344 """
345 for n in GetUpdateNodes(node):
346 n.AddInt(prop, value)
347
Simon Glass12bb1a92019-07-20 12:23:51 -0600348def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600349 """Update an integer property in affected device trees with an integer value
350
351 This is not allowed to change the size of the FDT.
352
353 Args:
354 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600355 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600356 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600357 for n in GetUpdateNodes(node, for_repack):
358 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glass51014aa2019-07-20 12:23:56 -0600359 (n.GetFdt().name, n.path, prop, value))
Simon Glassf46621d2018-09-14 04:57:21 -0600360 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600361
362def CheckAddHashProp(node):
363 hash_node = node.FindNode('hash')
364 if hash_node:
365 algo = hash_node.props.get('algo')
366 if not algo:
367 return "Missing 'algo' property for hash node"
368 if algo.value == 'sha256':
369 size = 32
370 else:
371 return "Unknown hash algorithm '%s'" % algo
372 for n in GetUpdateNodes(hash_node):
373 n.AddEmptyProp('value', size)
374
375def CheckSetHashValue(node, get_data_func):
376 hash_node = node.FindNode('hash')
377 if hash_node:
378 algo = hash_node.props.get('algo').value
379 if algo == 'sha256':
380 m = hashlib.sha256()
381 m.update(get_data_func())
382 data = m.digest()
383 for n in GetUpdateNodes(hash_node):
384 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600385
386def SetAllowEntryExpansion(allow):
387 """Set whether post-pack expansion of entries is allowed
388
389 Args:
390 allow: True to allow expansion, False to raise an exception
391 """
392 global allow_entry_expansion
393
394 allow_entry_expansion = allow
395
396def AllowEntryExpansion():
397 """Check whether post-pack expansion of entries is allowed
398
399 Returns:
400 True if expansion should be allowed, False if an exception should be
401 raised
402 """
403 return allow_entry_expansion
Simon Glass61ec04f2019-07-20 12:23:58 -0600404
405def SetAllowEntryContraction(allow):
406 """Set whether post-pack contraction of entries is allowed
407
408 Args:
409 allow: True to allow contraction, False to raise an exception
410 """
411 global allow_entry_contraction
412
413 allow_entry_contraction = allow
414
415def AllowEntryContraction():
416 """Check whether post-pack contraction of entries is allowed
417
418 Returns:
419 True if contraction should be allowed, False if an exception should be
420 raised
421 """
422 return allow_entry_contraction