blob: f22cc82d870cc77ac34164257eeb005927cf3b5c [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 Glassc55a50f2018-09-14 04:57:19 -0600111def SetEntryArgs(args):
112 """Set the value of the entry args
113
114 This sets up the entry_args dict which is used to supply entry arguments to
115 entries.
116
117 Args:
118 args: List of entry arguments, each in the format "name=value"
119 """
120 global entry_args
121
122 entry_args = {}
123 if args:
124 for arg in args:
125 m = re.match('([^=]*)=(.*)', arg)
126 if not m:
127 raise ValueError("Invalid entry arguemnt '%s'" % arg)
128 entry_args[m.group(1)] = m.group(2)
129
130def GetEntryArg(name):
131 """Get the value of an entry argument
132
133 Args:
134 name: Name of argument to retrieve
135
136 Returns:
137 String value of argument
138 """
139 return entry_args.get(name)
Simon Glass2a72cc72018-09-14 04:57:20 -0600140
Simon Glass539aece2018-09-14 04:57:22 -0600141def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600142 """Get device tree files ready for use
143
Simon Glass4bdd1152019-07-20 12:23:29 -0600144 This sets up a set of device tree files that can be retrieved by
145 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
Simon Glass2a72cc72018-09-14 04:57:20 -0600146
147 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600148 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600149 dtb: Main dtb
150 """
Simon Glass10f9d002019-07-20 12:23:50 -0600151 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-09-14 04:57:20 -0600152 # Import these here in case libfdt.py is not available, in which case
153 # the above help option still works.
154 import fdt
155 import fdt_util
156
157 # If we are updating the DTBs we need to put these updated versions
158 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
159 # since it is assumed to be the one passed in with options.dt, and
160 # was handled just above.
161 main_dtb = dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600162 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600163 fdt_path_prefix = ''
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600164 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
165 output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
166 output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
Simon Glass539aece2018-09-14 04:57:22 -0600167 if not use_fake_dtb:
Simon Glassf49462e2019-07-20 12:23:34 -0600168 fdt_set = {}
Simon Glass539aece2018-09-14 04:57:22 -0600169 for image in images.values():
Simon Glass77e4ef12019-07-20 12:23:33 -0600170 fdt_set.update(image.GetFdts())
171 for etype, other in fdt_set.items():
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600172 entry, other_fname = other
Simon Glass539aece2018-09-14 04:57:22 -0600173 infile = tools.GetInputFilename(other_fname)
174 other_fname_dtb = fdt_util.EnsureCompiled(infile)
175 out_fname = tools.GetOutputFilename('%s.out' %
176 os.path.split(other_fname)[1])
177 tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
178 other_dtb = fdt.FdtScan(out_fname)
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600179 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glass2a72cc72018-09-14 04:57:20 -0600180
Simon Glass10f9d002019-07-20 12:23:50 -0600181def PrepareFromLoadedData(image):
182 """Get device tree files ready for use with a loaded image
183
184 Loaded images are different from images that are being created by binman,
185 since there is generally already an fdtmap and we read the description from
186 that. This provides the position and size of every entry in the image with
187 no calculation required.
188
189 This function uses the same output_fdt_info[] as Prepare(). It finds the
190 device tree files, adds a reference to the fdtmap and sets the FDT path
191 prefix to translate from the fdtmap (where the root node is the image node)
192 to the normal device tree (where the image node is under a /binman node).
193
194 Args:
195 images: List of images being used
196 """
197 global output_fdt_info, main_dtb, fdt_path_prefix
198
199 tout.Info('Preparing device trees')
200 output_fdt_info.clear()
201 fdt_path_prefix = ''
202 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
203 main_dtb = None
204 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
205 for etype, value in image.GetFdts().items():
206 entry, fname = value
207 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
208 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
209 (etype, out_fname, entry.GetPath()))
210 entry._filename = entry.GetDefaultFilename()
211 data = entry.ReadData()
212
213 tools.WriteFile(out_fname, data)
214 dtb = fdt.Fdt(out_fname)
215 dtb.Scan()
216 image_node = dtb.GetNode('/binman')
217 if 'multiple-images' in image_node.props:
218 image_node = dtb.GetNode('/binman/%s' % image.image_node)
219 fdt_path_prefix = image_node.path
220 output_fdt_info[etype] = [dtb, None, entry]
221 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
222
223
Simon Glass4bdd1152019-07-20 12:23:29 -0600224def GetAllFdts():
Simon Glass2a72cc72018-09-14 04:57:20 -0600225 """Yield all device tree files being used by binman
226
227 Yields:
228 Device trees being used (U-Boot proper, SPL, TPL)
229 """
Simon Glass10f9d002019-07-20 12:23:50 -0600230 if main_dtb:
231 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600232 for etype in output_fdt_info:
233 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600234 if dtb != main_dtb:
235 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600236
Simon Glass12bb1a92019-07-20 12:23:51 -0600237def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600238 """Yield all the nodes that need to be updated in all device trees
239
240 The property referenced by this node is added to any device trees which
241 have the given node. Due to removable of unwanted notes, SPL and TPL may
242 not have this node.
243
244 Args:
245 node: Node object in the main device tree to look up
Simon Glass12bb1a92019-07-20 12:23:51 -0600246 for_repack: True if we want only nodes which need 'repack' properties
247 added to them (e.g. 'orig-offset'), False to return all nodes. We
248 don't add repack properties to SPL/TPL device trees.
Simon Glassf46621d2018-09-14 04:57:21 -0600249
250 Yields:
251 Node objects in each device tree that is in use (U-Boot proper, which
252 is node, SPL and TPL)
253 """
254 yield node
Simon Glass12bb1a92019-07-20 12:23:51 -0600255 for dtb, fname, entry in output_fdt_info.values():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600256 if dtb != node.GetFdt():
Simon Glass12bb1a92019-07-20 12:23:51 -0600257 if for_repack and entry.etype != 'u-boot-dtb':
258 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600259 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass51014aa2019-07-20 12:23:56 -0600260 #print(' try', fdt_path_prefix + node.path, other_node)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600261 if other_node:
262 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600263
Simon Glass12bb1a92019-07-20 12:23:51 -0600264def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600265 """Add a new property to affected device trees with an integer value of 0.
266
267 Args:
268 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600269 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600270 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600271 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600272 n.AddZeroProp(prop)
273
Simon Glass0a98b282018-09-14 04:57:28 -0600274def AddSubnode(node, name):
275 """Add a new subnode to a node in affected device trees
276
277 Args:
278 node: Node to add to
279 name: name of node to add
280
281 Returns:
282 New subnode that was created in main tree
283 """
284 first = None
285 for n in GetUpdateNodes(node):
286 subnode = n.AddSubnode(name)
287 if not first:
288 first = subnode
289 return first
290
291def AddString(node, prop, value):
292 """Add a new string property to affected device trees
293
294 Args:
295 prop_name: Name of property
296 value: String value (which will be \0-terminated in the DT)
297 """
298 for n in GetUpdateNodes(node):
299 n.AddString(prop, value)
300
Simon Glass12bb1a92019-07-20 12:23:51 -0600301def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-09-14 04:57:21 -0600302 """Update an integer property in affected device trees with an integer value
303
304 This is not allowed to change the size of the FDT.
305
306 Args:
307 prop_name: Name of property
Simon Glass12bb1a92019-07-20 12:23:51 -0600308 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600309 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600310 for n in GetUpdateNodes(node, for_repack):
311 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glass51014aa2019-07-20 12:23:56 -0600312 (n.GetFdt().name, n.path, prop, value))
Simon Glassf46621d2018-09-14 04:57:21 -0600313 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600314
315def CheckAddHashProp(node):
316 hash_node = node.FindNode('hash')
317 if hash_node:
318 algo = hash_node.props.get('algo')
319 if not algo:
320 return "Missing 'algo' property for hash node"
321 if algo.value == 'sha256':
322 size = 32
323 else:
324 return "Unknown hash algorithm '%s'" % algo
325 for n in GetUpdateNodes(hash_node):
326 n.AddEmptyProp('value', size)
327
328def CheckSetHashValue(node, get_data_func):
329 hash_node = node.FindNode('hash')
330 if hash_node:
331 algo = hash_node.props.get('algo').value
332 if algo == 'sha256':
333 m = hashlib.sha256()
334 m.update(get_data_func())
335 data = m.digest()
336 for n in GetUpdateNodes(hash_node):
337 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600338
339def SetAllowEntryExpansion(allow):
340 """Set whether post-pack expansion of entries is allowed
341
342 Args:
343 allow: True to allow expansion, False to raise an exception
344 """
345 global allow_entry_expansion
346
347 allow_entry_expansion = allow
348
349def AllowEntryExpansion():
350 """Check whether post-pack expansion of entries is allowed
351
352 Returns:
353 True if expansion should be allowed, False if an exception should be
354 raised
355 """
356 return allow_entry_expansion
Simon Glass61ec04f2019-07-20 12:23:58 -0600357
358def SetAllowEntryContraction(allow):
359 """Set whether post-pack contraction of entries is allowed
360
361 Args:
362 allow: True to allow contraction, False to raise an exception
363 """
364 global allow_entry_contraction
365
366 allow_entry_contraction = allow
367
368def AllowEntryContraction():
369 """Check whether post-pack contraction of entries is allowed
370
371 Returns:
372 True if contraction should be allowed, False if an exception should be
373 raised
374 """
375 return allow_entry_contraction