blob: cef5f660bbacfa5967c750428b41da10bdb8c8b3 [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#
26# key: entry type
27# value: tuple:
28# Fdt object
29# Filename
Simon Glass6ca0dcb2019-07-20 12:23:43 -060030# Entry object, or None if not known
31output_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 Glassfb5e8b12019-07-20 12:23:32 -060059def GetFdtForEtype(etype):
60 """Get the Fdt object for a particular device-tree entry
Simon Glassc55a50f2018-09-14 04:57:19 -060061
62 Binman keeps track of at least one device-tree file called u-boot.dtb but
63 can also have others (e.g. for SPL). This function looks up the given
Simon Glassfb5e8b12019-07-20 12:23:32 -060064 entry and returns the associated Fdt object.
Simon Glassc55a50f2018-09-14 04:57:19 -060065
66 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060067 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060068
69 Returns:
Simon Glassfb5e8b12019-07-20 12:23:32 -060070 Fdt object associated with the entry type
Simon Glassc55a50f2018-09-14 04:57:19 -060071 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060072 value = output_fdt_info.get(etype);
Simon Glass6a3b5b52019-07-20 12:23:42 -060073 if not value:
74 return None
75 return value[0]
Simon Glassc55a50f2018-09-14 04:57:19 -060076
Simon Glassfb5e8b12019-07-20 12:23:32 -060077def GetFdtPath(etype):
Simon Glassc55a50f2018-09-14 04:57:19 -060078 """Get the full pathname of a particular Fdt object
79
Simon Glass726e2962019-07-20 12:23:30 -060080 Similar to GetFdtForEtype() but returns the pathname associated with the
81 Fdt.
Simon Glassc55a50f2018-09-14 04:57:19 -060082
83 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060084 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060085
86 Returns:
87 Full path name to the associated Fdt
88 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060089 return output_fdt_info[etype][0]._fname
Simon Glassc55a50f2018-09-14 04:57:19 -060090
Simon Glassfb5e8b12019-07-20 12:23:32 -060091def GetFdtContents(etype='u-boot-dtb'):
Simon Glass6ed45ba2018-09-14 04:57:24 -060092 """Looks up the FDT pathname and contents
93
94 This is used to obtain the Fdt pathname and contents when needed by an
95 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
96 the real dtb.
97
98 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060099 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glass6ed45ba2018-09-14 04:57:24 -0600100
101 Returns:
102 tuple:
103 pathname to Fdt
104 Fdt data (as bytes)
105 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600106 if etype not in output_fdt_info:
Simon Glass6a3b5b52019-07-20 12:23:42 -0600107 return None, None
108 if not use_fake_dtb:
Simon Glassfb5e8b12019-07-20 12:23:32 -0600109 pathname = GetFdtPath(etype)
110 data = GetFdtForEtype(etype).GetContents()
Simon Glass6ed45ba2018-09-14 04:57:24 -0600111 else:
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600112 fname = output_fdt_info[etype][1]
Simon Glass6ed45ba2018-09-14 04:57:24 -0600113 pathname = tools.GetInputFilename(fname)
114 data = tools.ReadFile(pathname)
115 return pathname, data
116
Simon Glassf6e02492019-07-20 12:24:08 -0600117def UpdateFdtContents(etype, data):
118 """Update the contents of a particular device tree
119
120 The device tree is updated and written back to its file. This affects what
121 is returned from future called to GetFdtContents(), etc.
122
123 Args:
124 etype: Entry type (e.g. 'u-boot-dtb')
125 data: Data to replace the DTB with
126 """
127 dtb, fname, entry = output_fdt_info[etype]
128 dtb_fname = dtb.GetFilename()
129 tools.WriteFile(dtb_fname, data)
130 dtb = fdt.FdtScan(dtb_fname)
131 output_fdt_info[etype] = [dtb, fname, entry]
132
Simon Glassc55a50f2018-09-14 04:57:19 -0600133def SetEntryArgs(args):
134 """Set the value of the entry args
135
136 This sets up the entry_args dict which is used to supply entry arguments to
137 entries.
138
139 Args:
140 args: List of entry arguments, each in the format "name=value"
141 """
142 global entry_args
143
144 entry_args = {}
145 if args:
146 for arg in args:
147 m = re.match('([^=]*)=(.*)', arg)
148 if not m:
149 raise ValueError("Invalid entry arguemnt '%s'" % arg)
150 entry_args[m.group(1)] = m.group(2)
151
152def GetEntryArg(name):
153 """Get the value of an entry argument
154
155 Args:
156 name: Name of argument to retrieve
157
158 Returns:
159 String value of argument
160 """
161 return entry_args.get(name)
Simon Glass2a72cc72018-09-14 04:57:20 -0600162
Simon Glass539aece2018-09-14 04:57:22 -0600163def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600164 """Get device tree files ready for use
165
Simon Glass4bdd1152019-07-20 12:23:29 -0600166 This sets up a set of device tree files that can be retrieved by
167 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
Simon Glass2a72cc72018-09-14 04:57:20 -0600168
169 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600170 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600171 dtb: Main dtb
172 """
Simon Glass10f9d002019-07-20 12:23:50 -0600173 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-09-14 04:57:20 -0600174 # Import these here in case libfdt.py is not available, in which case
175 # the above help option still works.
Simon Glass16287932020-04-17 18:09:03 -0600176 from dtoc import fdt
177 from dtoc import fdt_util
Simon Glass2a72cc72018-09-14 04:57:20 -0600178
179 # If we are updating the DTBs we need to put these updated versions
180 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
181 # since it is assumed to be the one passed in with options.dt, and
182 # was handled just above.
183 main_dtb = dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600184 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600185 fdt_path_prefix = ''
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600186 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
Simon Glass76971702021-03-18 20:25:00 +1300187 if use_fake_dtb:
188 for etype, fname in DTB_TYPE_FNAME.items():
189 output_fdt_info[etype] = [dtb, fname, None]
190 else:
Simon Glassf49462e2019-07-20 12:23:34 -0600191 fdt_set = {}
Simon Glass539aece2018-09-14 04:57:22 -0600192 for image in images.values():
Simon Glass77e4ef12019-07-20 12:23:33 -0600193 fdt_set.update(image.GetFdts())
194 for etype, other in fdt_set.items():
Simon Glass76971702021-03-18 20:25:00 +1300195 entry, fname = other
196 infile = tools.GetInputFilename(fname)
197 fname_dtb = fdt_util.EnsureCompiled(infile)
Simon Glass539aece2018-09-14 04:57:22 -0600198 out_fname = tools.GetOutputFilename('%s.out' %
Simon Glass76971702021-03-18 20:25:00 +1300199 os.path.split(fname)[1])
200 tools.WriteFile(out_fname, tools.ReadFile(fname_dtb))
Simon Glass539aece2018-09-14 04:57:22 -0600201 other_dtb = fdt.FdtScan(out_fname)
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600202 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glass2a72cc72018-09-14 04:57:20 -0600203
Simon Glass10f9d002019-07-20 12:23:50 -0600204def PrepareFromLoadedData(image):
205 """Get device tree files ready for use with a loaded image
206
207 Loaded images are different from images that are being created by binman,
208 since there is generally already an fdtmap and we read the description from
209 that. This provides the position and size of every entry in the image with
210 no calculation required.
211
212 This function uses the same output_fdt_info[] as Prepare(). It finds the
213 device tree files, adds a reference to the fdtmap and sets the FDT path
214 prefix to translate from the fdtmap (where the root node is the image node)
215 to the normal device tree (where the image node is under a /binman node).
216
217 Args:
218 images: List of images being used
219 """
220 global output_fdt_info, main_dtb, fdt_path_prefix
221
222 tout.Info('Preparing device trees')
223 output_fdt_info.clear()
224 fdt_path_prefix = ''
225 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
226 main_dtb = None
227 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
228 for etype, value in image.GetFdts().items():
229 entry, fname = value
230 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
231 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
232 (etype, out_fname, entry.GetPath()))
233 entry._filename = entry.GetDefaultFilename()
234 data = entry.ReadData()
235
236 tools.WriteFile(out_fname, data)
237 dtb = fdt.Fdt(out_fname)
238 dtb.Scan()
239 image_node = dtb.GetNode('/binman')
240 if 'multiple-images' in image_node.props:
241 image_node = dtb.GetNode('/binman/%s' % image.image_node)
242 fdt_path_prefix = image_node.path
243 output_fdt_info[etype] = [dtb, None, entry]
244 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
245
246
Simon Glass4bdd1152019-07-20 12:23:29 -0600247def GetAllFdts():
Simon Glass2a72cc72018-09-14 04:57:20 -0600248 """Yield all device tree files being used by binman
249
250 Yields:
251 Device trees being used (U-Boot proper, SPL, TPL)
252 """
Simon Glass10f9d002019-07-20 12:23:50 -0600253 if main_dtb:
254 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600255 for etype in output_fdt_info:
256 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600257 if dtb != main_dtb:
258 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600259
Simon Glass12bb1a92019-07-20 12:23:51 -0600260def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600261 """Yield all the nodes that need to be updated in all device trees
262
263 The property referenced by this node is added to any device trees which
264 have the given node. Due to removable of unwanted notes, SPL and TPL may
265 not have this node.
266
267 Args:
268 node: Node object in the main device tree to look up
Simon Glass12bb1a92019-07-20 12:23:51 -0600269 for_repack: True if we want only nodes which need 'repack' properties
270 added to them (e.g. 'orig-offset'), False to return all nodes. We
271 don't add repack properties to SPL/TPL device trees.
Simon Glassf46621d2018-09-14 04:57:21 -0600272
273 Yields:
274 Node objects in each device tree that is in use (U-Boot proper, which
275 is node, SPL and TPL)
276 """
277 yield node
Simon Glass12bb1a92019-07-20 12:23:51 -0600278 for dtb, fname, entry in output_fdt_info.values():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600279 if dtb != node.GetFdt():
Simon Glass12bb1a92019-07-20 12:23:51 -0600280 if for_repack and entry.etype != 'u-boot-dtb':
281 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600282 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass51014aa2019-07-20 12:23:56 -0600283 #print(' try', fdt_path_prefix + node.path, other_node)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600284 if other_node:
285 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600286
Simon Glass12bb1a92019-07-20 12:23:51 -0600287def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600288 """Add a new property to affected device trees with an integer value of 0.
289
290 Args:
291 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600292 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600293 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600294 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600295 n.AddZeroProp(prop)
296
Simon Glass0a98b282018-09-14 04:57:28 -0600297def AddSubnode(node, name):
298 """Add a new subnode to a node in affected device trees
299
300 Args:
301 node: Node to add to
302 name: name of node to add
303
304 Returns:
305 New subnode that was created in main tree
306 """
307 first = None
308 for n in GetUpdateNodes(node):
309 subnode = n.AddSubnode(name)
310 if not first:
311 first = subnode
312 return first
313
314def AddString(node, prop, value):
315 """Add a new string property to affected device trees
316
317 Args:
318 prop_name: Name of property
319 value: String value (which will be \0-terminated in the DT)
320 """
321 for n in GetUpdateNodes(node):
322 n.AddString(prop, value)
323
Simon Glass6eb99322021-01-06 21:35:18 -0700324def AddInt(node, prop, value):
325 """Add a new string property to affected device trees
326
327 Args:
328 prop_name: Name of property
329 val: Integer value of property
330 """
331 for n in GetUpdateNodes(node):
332 n.AddInt(prop, value)
333
Simon Glass12bb1a92019-07-20 12:23:51 -0600334def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600335 """Update an integer property in affected device trees with an integer value
336
337 This is not allowed to change the size of the FDT.
338
339 Args:
340 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600341 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600342 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600343 for n in GetUpdateNodes(node, for_repack):
344 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glass51014aa2019-07-20 12:23:56 -0600345 (n.GetFdt().name, n.path, prop, value))
Simon Glassf46621d2018-09-14 04:57:21 -0600346 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600347
348def CheckAddHashProp(node):
349 hash_node = node.FindNode('hash')
350 if hash_node:
351 algo = hash_node.props.get('algo')
352 if not algo:
353 return "Missing 'algo' property for hash node"
354 if algo.value == 'sha256':
355 size = 32
356 else:
357 return "Unknown hash algorithm '%s'" % algo
358 for n in GetUpdateNodes(hash_node):
359 n.AddEmptyProp('value', size)
360
361def CheckSetHashValue(node, get_data_func):
362 hash_node = node.FindNode('hash')
363 if hash_node:
364 algo = hash_node.props.get('algo').value
365 if algo == 'sha256':
366 m = hashlib.sha256()
367 m.update(get_data_func())
368 data = m.digest()
369 for n in GetUpdateNodes(hash_node):
370 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600371
372def SetAllowEntryExpansion(allow):
373 """Set whether post-pack expansion of entries is allowed
374
375 Args:
376 allow: True to allow expansion, False to raise an exception
377 """
378 global allow_entry_expansion
379
380 allow_entry_expansion = allow
381
382def AllowEntryExpansion():
383 """Check whether post-pack expansion of entries is allowed
384
385 Returns:
386 True if expansion should be allowed, False if an exception should be
387 raised
388 """
389 return allow_entry_expansion
Simon Glass61ec04f2019-07-20 12:23:58 -0600390
391def SetAllowEntryContraction(allow):
392 """Set whether post-pack contraction of entries is allowed
393
394 Args:
395 allow: True to allow contraction, False to raise an exception
396 """
397 global allow_entry_contraction
398
399 allow_entry_contraction = allow
400
401def AllowEntryContraction():
402 """Check whether post-pack contraction of entries is allowed
403
404 Returns:
405 True if contraction should be allowed, False if an exception should be
406 raised
407 """
408 return allow_entry_contraction