blob: bb3e36ea7afb84218ebb8245fb731a2836ac13df [file] [log] [blame]
Simon Glass29aa7362018-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 Glassae7cf032018-09-14 04:57:31 -06008import hashlib
Simon Glass29aa7362018-09-14 04:57:19 -06009import re
Simon Glass29aa7362018-09-14 04:57:19 -060010
Simon Glassc585dd42020-04-17 18:09:03 -060011from dtoc import fdt
Simon Glass29aa7362018-09-14 04:57:19 -060012import os
Simon Glassa997ea52020-04-17 18:09:04 -060013from patman import tools
14from patman import tout
Simon Glass29aa7362018-09-14 04:57:19 -060015
Simon Glass2be381d2019-07-20 12:23:32 -060016# 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.
19#
20# key: entry type
21# value: tuple:
22# Fdt object
23# Filename
Simon Glass95f1a942019-07-20 12:23:43 -060024# Entry object, or None if not known
25output_fdt_info = {}
Simon Glass29aa7362018-09-14 04:57:19 -060026
Simon Glass072959a2019-07-20 12:23:50 -060027# Prefix to add to an fdtmap path to turn it into a path to the /binman node
28fdt_path_prefix = ''
29
Simon Glass29aa7362018-09-14 04:57:19 -060030# Arguments passed to binman to provide arguments to entries
31entry_args = {}
32
Simon Glass0c9d5b52018-09-14 04:57:22 -060033# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
34# ftest.py)
Simon Glass31402012018-09-14 04:57:23 -060035use_fake_dtb = False
Simon Glass0c9d5b52018-09-14 04:57:22 -060036
Simon Glassbdb40312018-09-14 04:57:20 -060037# The DTB which contains the full image information
38main_dtb = None
39
Simon Glasscbc80e82019-07-08 14:25:36 -060040# 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()
43allow_entry_expansion = True
44
Simon Glass9d8ee322019-07-20 12:23:58 -060045# 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.
51allow_entry_contraction = False
52
Simon Glass2be381d2019-07-20 12:23:32 -060053def GetFdtForEtype(etype):
54 """Get the Fdt object for a particular device-tree entry
Simon Glass29aa7362018-09-14 04:57:19 -060055
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
Simon Glass2be381d2019-07-20 12:23:32 -060058 entry and returns the associated Fdt object.
Simon Glass29aa7362018-09-14 04:57:19 -060059
60 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060061 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060062
63 Returns:
Simon Glass2be381d2019-07-20 12:23:32 -060064 Fdt object associated with the entry type
Simon Glass29aa7362018-09-14 04:57:19 -060065 """
Simon Glass95f1a942019-07-20 12:23:43 -060066 value = output_fdt_info.get(etype);
Simon Glass385138a2019-07-20 12:23:42 -060067 if not value:
68 return None
69 return value[0]
Simon Glass29aa7362018-09-14 04:57:19 -060070
Simon Glass2be381d2019-07-20 12:23:32 -060071def GetFdtPath(etype):
Simon Glass29aa7362018-09-14 04:57:19 -060072 """Get the full pathname of a particular Fdt object
73
Simon Glassa3030a52019-07-20 12:23:30 -060074 Similar to GetFdtForEtype() but returns the pathname associated with the
75 Fdt.
Simon Glass29aa7362018-09-14 04:57:19 -060076
77 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060078 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060079
80 Returns:
81 Full path name to the associated Fdt
82 """
Simon Glass95f1a942019-07-20 12:23:43 -060083 return output_fdt_info[etype][0]._fname
Simon Glass29aa7362018-09-14 04:57:19 -060084
Simon Glass2be381d2019-07-20 12:23:32 -060085def GetFdtContents(etype='u-boot-dtb'):
Simon Glasse219aa42018-09-14 04:57:24 -060086 """Looks up the FDT pathname and contents
87
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
90 the real dtb.
91
92 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060093 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glasse219aa42018-09-14 04:57:24 -060094
95 Returns:
96 tuple:
97 pathname to Fdt
98 Fdt data (as bytes)
99 """
Simon Glass95f1a942019-07-20 12:23:43 -0600100 if etype not in output_fdt_info:
Simon Glass385138a2019-07-20 12:23:42 -0600101 return None, None
102 if not use_fake_dtb:
Simon Glass2be381d2019-07-20 12:23:32 -0600103 pathname = GetFdtPath(etype)
104 data = GetFdtForEtype(etype).GetContents()
Simon Glasse219aa42018-09-14 04:57:24 -0600105 else:
Simon Glass95f1a942019-07-20 12:23:43 -0600106 fname = output_fdt_info[etype][1]
Simon Glasse219aa42018-09-14 04:57:24 -0600107 pathname = tools.GetInputFilename(fname)
108 data = tools.ReadFile(pathname)
109 return pathname, data
110
Simon Glass74f5feb2019-07-20 12:24:08 -0600111def UpdateFdtContents(etype, data):
112 """Update the contents of a particular device tree
113
114 The device tree is updated and written back to its file. This affects what
115 is returned from future called to GetFdtContents(), etc.
116
117 Args:
118 etype: Entry type (e.g. 'u-boot-dtb')
119 data: Data to replace the DTB with
120 """
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]
126
Simon Glass29aa7362018-09-14 04:57:19 -0600127def SetEntryArgs(args):
128 """Set the value of the entry args
129
130 This sets up the entry_args dict which is used to supply entry arguments to
131 entries.
132
133 Args:
134 args: List of entry arguments, each in the format "name=value"
135 """
136 global entry_args
137
138 entry_args = {}
139 if args:
140 for arg in args:
141 m = re.match('([^=]*)=(.*)', arg)
142 if not m:
143 raise ValueError("Invalid entry arguemnt '%s'" % arg)
144 entry_args[m.group(1)] = m.group(2)
145
146def GetEntryArg(name):
147 """Get the value of an entry argument
148
149 Args:
150 name: Name of argument to retrieve
151
152 Returns:
153 String value of argument
154 """
155 return entry_args.get(name)
Simon Glassbdb40312018-09-14 04:57:20 -0600156
Simon Glass0c9d5b52018-09-14 04:57:22 -0600157def Prepare(images, dtb):
Simon Glassbdb40312018-09-14 04:57:20 -0600158 """Get device tree files ready for use
159
Simon Glass5a300602019-07-20 12:23:29 -0600160 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.
Simon Glassbdb40312018-09-14 04:57:20 -0600162
163 Args:
Simon Glass0c9d5b52018-09-14 04:57:22 -0600164 images: List of images being used
Simon Glassbdb40312018-09-14 04:57:20 -0600165 dtb: Main dtb
166 """
Simon Glass072959a2019-07-20 12:23:50 -0600167 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glassbdb40312018-09-14 04:57:20 -0600168 # Import these here in case libfdt.py is not available, in which case
169 # the above help option still works.
Simon Glassc585dd42020-04-17 18:09:03 -0600170 from dtoc import fdt
171 from dtoc import fdt_util
Simon Glassbdb40312018-09-14 04:57:20 -0600172
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.
177 main_dtb = dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600178 output_fdt_info.clear()
Simon Glass072959a2019-07-20 12:23:50 -0600179 fdt_path_prefix = ''
Simon Glass95f1a942019-07-20 12:23:43 -0600180 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]
Simon Glass0c9d5b52018-09-14 04:57:22 -0600183 if not use_fake_dtb:
Simon Glassf3aba912019-07-20 12:23:34 -0600184 fdt_set = {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600185 for image in images.values():
Simon Glass132fa722019-07-20 12:23:33 -0600186 fdt_set.update(image.GetFdts())
187 for etype, other in fdt_set.items():
Simon Glass95f1a942019-07-20 12:23:43 -0600188 entry, other_fname = other
Simon Glass0c9d5b52018-09-14 04:57:22 -0600189 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)
Simon Glass95f1a942019-07-20 12:23:43 -0600195 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glassbdb40312018-09-14 04:57:20 -0600196
Simon Glass072959a2019-07-20 12:23:50 -0600197def PrepareFromLoadedData(image):
198 """Get device tree files ready for use with a loaded image
199
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.
204
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).
209
210 Args:
211 images: List of images being used
212 """
213 global output_fdt_info, main_dtb, fdt_path_prefix
214
215 tout.Info('Preparing device trees')
216 output_fdt_info.clear()
217 fdt_path_prefix = ''
218 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
219 main_dtb = None
220 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
221 for etype, value in image.GetFdts().items():
222 entry, fname = value
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()
228
229 tools.WriteFile(out_fname, data)
230 dtb = fdt.Fdt(out_fname)
231 dtb.Scan()
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)
238
239
Simon Glass5a300602019-07-20 12:23:29 -0600240def GetAllFdts():
Simon Glassbdb40312018-09-14 04:57:20 -0600241 """Yield all device tree files being used by binman
242
243 Yields:
244 Device trees being used (U-Boot proper, SPL, TPL)
245 """
Simon Glass072959a2019-07-20 12:23:50 -0600246 if main_dtb:
247 yield main_dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600248 for etype in output_fdt_info:
249 dtb = output_fdt_info[etype][0]
Simon Glass132fa722019-07-20 12:23:33 -0600250 if dtb != main_dtb:
251 yield dtb
Simon Glassbdb40312018-09-14 04:57:20 -0600252
Simon Glassfb30e292019-07-20 12:23:51 -0600253def GetUpdateNodes(node, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600254 """Yield all the nodes that need to be updated in all device trees
255
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
258 not have this node.
259
260 Args:
261 node: Node object in the main device tree to look up
Simon Glassfb30e292019-07-20 12:23:51 -0600262 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.
Simon Glassc8135dc2018-09-14 04:57:21 -0600265
266 Yields:
267 Node objects in each device tree that is in use (U-Boot proper, which
268 is node, SPL and TPL)
269 """
270 yield node
Simon Glassfb30e292019-07-20 12:23:51 -0600271 for dtb, fname, entry in output_fdt_info.values():
Simon Glasse219aa42018-09-14 04:57:24 -0600272 if dtb != node.GetFdt():
Simon Glassfb30e292019-07-20 12:23:51 -0600273 if for_repack and entry.etype != 'u-boot-dtb':
274 continue
Simon Glass072959a2019-07-20 12:23:50 -0600275 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glassf8a54bc2019-07-20 12:23:56 -0600276 #print(' try', fdt_path_prefix + node.path, other_node)
Simon Glasse219aa42018-09-14 04:57:24 -0600277 if other_node:
278 yield other_node
Simon Glassc8135dc2018-09-14 04:57:21 -0600279
Simon Glassfb30e292019-07-20 12:23:51 -0600280def AddZeroProp(node, prop, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600281 """Add a new property to affected device trees with an integer value of 0.
282
283 Args:
284 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600285 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600286 """
Simon Glassfb30e292019-07-20 12:23:51 -0600287 for n in GetUpdateNodes(node, for_repack):
Simon Glassc8135dc2018-09-14 04:57:21 -0600288 n.AddZeroProp(prop)
289
Simon Glassac6328c2018-09-14 04:57:28 -0600290def AddSubnode(node, name):
291 """Add a new subnode to a node in affected device trees
292
293 Args:
294 node: Node to add to
295 name: name of node to add
296
297 Returns:
298 New subnode that was created in main tree
299 """
300 first = None
301 for n in GetUpdateNodes(node):
302 subnode = n.AddSubnode(name)
303 if not first:
304 first = subnode
305 return first
306
307def AddString(node, prop, value):
308 """Add a new string property to affected device trees
309
310 Args:
311 prop_name: Name of property
312 value: String value (which will be \0-terminated in the DT)
313 """
314 for n in GetUpdateNodes(node):
315 n.AddString(prop, value)
316
Simon Glassa2af7302021-01-06 21:35:18 -0700317def AddInt(node, prop, value):
318 """Add a new string property to affected device trees
319
320 Args:
321 prop_name: Name of property
322 val: Integer value of property
323 """
324 for n in GetUpdateNodes(node):
325 n.AddInt(prop, value)
326
Simon Glassfb30e292019-07-20 12:23:51 -0600327def SetInt(node, prop, value, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600328 """Update an integer property in affected device trees with an integer value
329
330 This is not allowed to change the size of the FDT.
331
332 Args:
333 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600334 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600335 """
Simon Glassfb30e292019-07-20 12:23:51 -0600336 for n in GetUpdateNodes(node, for_repack):
337 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glassf8a54bc2019-07-20 12:23:56 -0600338 (n.GetFdt().name, n.path, prop, value))
Simon Glassc8135dc2018-09-14 04:57:21 -0600339 n.SetInt(prop, value)
Simon Glassae7cf032018-09-14 04:57:31 -0600340
341def CheckAddHashProp(node):
342 hash_node = node.FindNode('hash')
343 if hash_node:
344 algo = hash_node.props.get('algo')
345 if not algo:
346 return "Missing 'algo' property for hash node"
347 if algo.value == 'sha256':
348 size = 32
349 else:
350 return "Unknown hash algorithm '%s'" % algo
351 for n in GetUpdateNodes(hash_node):
352 n.AddEmptyProp('value', size)
353
354def CheckSetHashValue(node, get_data_func):
355 hash_node = node.FindNode('hash')
356 if hash_node:
357 algo = hash_node.props.get('algo').value
358 if algo == 'sha256':
359 m = hashlib.sha256()
360 m.update(get_data_func())
361 data = m.digest()
362 for n in GetUpdateNodes(hash_node):
363 n.SetData('value', data)
Simon Glasscbc80e82019-07-08 14:25:36 -0600364
365def SetAllowEntryExpansion(allow):
366 """Set whether post-pack expansion of entries is allowed
367
368 Args:
369 allow: True to allow expansion, False to raise an exception
370 """
371 global allow_entry_expansion
372
373 allow_entry_expansion = allow
374
375def AllowEntryExpansion():
376 """Check whether post-pack expansion of entries is allowed
377
378 Returns:
379 True if expansion should be allowed, False if an exception should be
380 raised
381 """
382 return allow_entry_expansion
Simon Glass9d8ee322019-07-20 12:23:58 -0600383
384def SetAllowEntryContraction(allow):
385 """Set whether post-pack contraction of entries is allowed
386
387 Args:
388 allow: True to allow contraction, False to raise an exception
389 """
390 global allow_entry_contraction
391
392 allow_entry_contraction = allow
393
394def AllowEntryContraction():
395 """Check whether post-pack contraction of entries is allowed
396
397 Returns:
398 True if contraction should be allowed, False if an exception should be
399 raised
400 """
401 return allow_entry_contraction