blob: d704ed2c7cd1a589f2cca0596e43aaaba1d5e4ab [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 Glass10f9d002019-07-20 12:23:50 -060011import fdt
Simon Glassc55a50f2018-09-14 04:57:19 -060012import os
13import tools
Simon Glass10f9d002019-07-20 12:23:50 -060014import tout
Simon Glassc55a50f2018-09-14 04:57:19 -060015
Simon Glassfb5e8b12019-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 Glass6ca0dcb2019-07-20 12:23:43 -060024# Entry object, or None if not known
25output_fdt_info = {}
Simon Glassc55a50f2018-09-14 04:57:19 -060026
Simon Glass10f9d002019-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 Glassc55a50f2018-09-14 04:57:19 -060030# Arguments passed to binman to provide arguments to entries
31entry_args = {}
32
Simon Glass539aece2018-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 Glass93d17412018-09-14 04:57:23 -060035use_fake_dtb = False
Simon Glass539aece2018-09-14 04:57:22 -060036
Simon Glass2a72cc72018-09-14 04:57:20 -060037# The DTB which contains the full image information
38main_dtb = None
39
Simon Glassbf6906b2019-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 Glass61ec04f2019-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 Glassfb5e8b12019-07-20 12:23:32 -060053def GetFdtForEtype(etype):
54 """Get the Fdt object for a particular device-tree entry
Simon Glassc55a50f2018-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 Glassfb5e8b12019-07-20 12:23:32 -060058 entry and returns the associated Fdt object.
Simon Glassc55a50f2018-09-14 04:57:19 -060059
60 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060061 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060062
63 Returns:
Simon Glassfb5e8b12019-07-20 12:23:32 -060064 Fdt object associated with the entry type
Simon Glassc55a50f2018-09-14 04:57:19 -060065 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060066 value = output_fdt_info.get(etype);
Simon Glass6a3b5b52019-07-20 12:23:42 -060067 if not value:
68 return None
69 return value[0]
Simon Glassc55a50f2018-09-14 04:57:19 -060070
Simon Glassfb5e8b12019-07-20 12:23:32 -060071def GetFdtPath(etype):
Simon Glassc55a50f2018-09-14 04:57:19 -060072 """Get the full pathname of a particular Fdt object
73
Simon Glass726e2962019-07-20 12:23:30 -060074 Similar to GetFdtForEtype() but returns the pathname associated with the
75 Fdt.
Simon Glassc55a50f2018-09-14 04:57:19 -060076
77 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060078 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060079
80 Returns:
81 Full path name to the associated Fdt
82 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060083 return output_fdt_info[etype][0]._fname
Simon Glassc55a50f2018-09-14 04:57:19 -060084
Simon Glassfb5e8b12019-07-20 12:23:32 -060085def GetFdtContents(etype='u-boot-dtb'):
Simon Glass6ed45ba2018-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 Glassfb5e8b12019-07-20 12:23:32 -060093 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glass6ed45ba2018-09-14 04:57:24 -060094
95 Returns:
96 tuple:
97 pathname to Fdt
98 Fdt data (as bytes)
99 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600100 if etype not in output_fdt_info:
Simon Glass6a3b5b52019-07-20 12:23:42 -0600101 return None, None
102 if not use_fake_dtb:
Simon Glassfb5e8b12019-07-20 12:23:32 -0600103 pathname = GetFdtPath(etype)
104 data = GetFdtForEtype(etype).GetContents()
Simon Glass6ed45ba2018-09-14 04:57:24 -0600105 else:
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600106 fname = output_fdt_info[etype][1]
Simon Glass6ed45ba2018-09-14 04:57:24 -0600107 pathname = tools.GetInputFilename(fname)
108 data = tools.ReadFile(pathname)
109 return pathname, data
110
Simon Glassf6e02492019-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 Glassc55a50f2018-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 Glass2a72cc72018-09-14 04:57:20 -0600156
Simon Glass539aece2018-09-14 04:57:22 -0600157def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600158 """Get device tree files ready for use
159
Simon Glass4bdd1152019-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 Glass2a72cc72018-09-14 04:57:20 -0600162
163 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600164 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600165 dtb: Main dtb
166 """
Simon Glass10f9d002019-07-20 12:23:50 -0600167 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-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.
170 import fdt
171 import fdt_util
172
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 Glass6ca0dcb2019-07-20 12:23:43 -0600178 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600179 fdt_path_prefix = ''
Simon Glass6ca0dcb2019-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 Glass539aece2018-09-14 04:57:22 -0600183 if not use_fake_dtb:
Simon Glassf49462e2019-07-20 12:23:34 -0600184 fdt_set = {}
Simon Glass539aece2018-09-14 04:57:22 -0600185 for image in images.values():
Simon Glass77e4ef12019-07-20 12:23:33 -0600186 fdt_set.update(image.GetFdts())
187 for etype, other in fdt_set.items():
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600188 entry, other_fname = other
Simon Glass539aece2018-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 Glass6ca0dcb2019-07-20 12:23:43 -0600195 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glass2a72cc72018-09-14 04:57:20 -0600196
Simon Glass10f9d002019-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 Glass4bdd1152019-07-20 12:23:29 -0600240def GetAllFdts():
Simon Glass2a72cc72018-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 Glass10f9d002019-07-20 12:23:50 -0600246 if main_dtb:
247 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600248 for etype in output_fdt_info:
249 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600250 if dtb != main_dtb:
251 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600252
Simon Glass12bb1a92019-07-20 12:23:51 -0600253def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-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 Glass12bb1a92019-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 Glassf46621d2018-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 Glass12bb1a92019-07-20 12:23:51 -0600271 for dtb, fname, entry in output_fdt_info.values():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600272 if dtb != node.GetFdt():
Simon Glass12bb1a92019-07-20 12:23:51 -0600273 if for_repack and entry.etype != 'u-boot-dtb':
274 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600275 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass51014aa2019-07-20 12:23:56 -0600276 #print(' try', fdt_path_prefix + node.path, other_node)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600277 if other_node:
278 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600279
Simon Glass12bb1a92019-07-20 12:23:51 -0600280def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-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 Glass12bb1a92019-07-20 12:23:51 -0600285 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600286 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600287 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600288 n.AddZeroProp(prop)
289
Simon Glass0a98b282018-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 Glass12bb1a92019-07-20 12:23:51 -0600317def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600318 """Update an integer property in affected device trees with an integer value
319
320 This is not allowed to change the size of the FDT.
321
322 Args:
323 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600324 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600325 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600326 for n in GetUpdateNodes(node, for_repack):
327 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glass51014aa2019-07-20 12:23:56 -0600328 (n.GetFdt().name, n.path, prop, value))
Simon Glassf46621d2018-09-14 04:57:21 -0600329 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600330
331def CheckAddHashProp(node):
332 hash_node = node.FindNode('hash')
333 if hash_node:
334 algo = hash_node.props.get('algo')
335 if not algo:
336 return "Missing 'algo' property for hash node"
337 if algo.value == 'sha256':
338 size = 32
339 else:
340 return "Unknown hash algorithm '%s'" % algo
341 for n in GetUpdateNodes(hash_node):
342 n.AddEmptyProp('value', size)
343
344def CheckSetHashValue(node, get_data_func):
345 hash_node = node.FindNode('hash')
346 if hash_node:
347 algo = hash_node.props.get('algo').value
348 if algo == 'sha256':
349 m = hashlib.sha256()
350 m.update(get_data_func())
351 data = m.digest()
352 for n in GetUpdateNodes(hash_node):
353 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600354
355def SetAllowEntryExpansion(allow):
356 """Set whether post-pack expansion of entries is allowed
357
358 Args:
359 allow: True to allow expansion, False to raise an exception
360 """
361 global allow_entry_expansion
362
363 allow_entry_expansion = allow
364
365def AllowEntryExpansion():
366 """Check whether post-pack expansion of entries is allowed
367
368 Returns:
369 True if expansion should be allowed, False if an exception should be
370 raised
371 """
372 return allow_entry_expansion
Simon Glass61ec04f2019-07-20 12:23:58 -0600373
374def SetAllowEntryContraction(allow):
375 """Set whether post-pack contraction of entries is allowed
376
377 Args:
378 allow: True to allow contraction, False to raise an exception
379 """
380 global allow_entry_contraction
381
382 allow_entry_contraction = allow
383
384def AllowEntryContraction():
385 """Check whether post-pack contraction of entries is allowed
386
387 Returns:
388 True if contraction should be allowed, False if an exception should be
389 raised
390 """
391 return allow_entry_contraction