blob: 2379e24ef6dff79af57c89b4c95e6be44f8cb56f [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 Glass072959a2019-07-20 12:23:50 -060011import fdt
Simon Glass29aa7362018-09-14 04:57:19 -060012import os
13import tools
Simon Glass072959a2019-07-20 12:23:50 -060014import 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 Glass2be381d2019-07-20 12:23:32 -060045def GetFdtForEtype(etype):
46 """Get the Fdt object for a particular device-tree entry
Simon Glass29aa7362018-09-14 04:57:19 -060047
48 Binman keeps track of at least one device-tree file called u-boot.dtb but
49 can also have others (e.g. for SPL). This function looks up the given
Simon Glass2be381d2019-07-20 12:23:32 -060050 entry and returns the associated Fdt object.
Simon Glass29aa7362018-09-14 04:57:19 -060051
52 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060053 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060054
55 Returns:
Simon Glass2be381d2019-07-20 12:23:32 -060056 Fdt object associated with the entry type
Simon Glass29aa7362018-09-14 04:57:19 -060057 """
Simon Glass95f1a942019-07-20 12:23:43 -060058 value = output_fdt_info.get(etype);
Simon Glass385138a2019-07-20 12:23:42 -060059 if not value:
60 return None
61 return value[0]
Simon Glass29aa7362018-09-14 04:57:19 -060062
Simon Glass2be381d2019-07-20 12:23:32 -060063def GetFdtPath(etype):
Simon Glass29aa7362018-09-14 04:57:19 -060064 """Get the full pathname of a particular Fdt object
65
Simon Glassa3030a52019-07-20 12:23:30 -060066 Similar to GetFdtForEtype() but returns the pathname associated with the
67 Fdt.
Simon Glass29aa7362018-09-14 04:57:19 -060068
69 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060070 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060071
72 Returns:
73 Full path name to the associated Fdt
74 """
Simon Glass95f1a942019-07-20 12:23:43 -060075 return output_fdt_info[etype][0]._fname
Simon Glass29aa7362018-09-14 04:57:19 -060076
Simon Glass2be381d2019-07-20 12:23:32 -060077def GetFdtContents(etype='u-boot-dtb'):
Simon Glasse219aa42018-09-14 04:57:24 -060078 """Looks up the FDT pathname and contents
79
80 This is used to obtain the Fdt pathname and contents when needed by an
81 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
82 the real dtb.
83
84 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060085 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glasse219aa42018-09-14 04:57:24 -060086
87 Returns:
88 tuple:
89 pathname to Fdt
90 Fdt data (as bytes)
91 """
Simon Glass95f1a942019-07-20 12:23:43 -060092 if etype not in output_fdt_info:
Simon Glass385138a2019-07-20 12:23:42 -060093 return None, None
94 if not use_fake_dtb:
Simon Glass2be381d2019-07-20 12:23:32 -060095 pathname = GetFdtPath(etype)
96 data = GetFdtForEtype(etype).GetContents()
Simon Glasse219aa42018-09-14 04:57:24 -060097 else:
Simon Glass95f1a942019-07-20 12:23:43 -060098 fname = output_fdt_info[etype][1]
Simon Glasse219aa42018-09-14 04:57:24 -060099 pathname = tools.GetInputFilename(fname)
100 data = tools.ReadFile(pathname)
101 return pathname, data
102
Simon Glass29aa7362018-09-14 04:57:19 -0600103def SetEntryArgs(args):
104 """Set the value of the entry args
105
106 This sets up the entry_args dict which is used to supply entry arguments to
107 entries.
108
109 Args:
110 args: List of entry arguments, each in the format "name=value"
111 """
112 global entry_args
113
114 entry_args = {}
115 if args:
116 for arg in args:
117 m = re.match('([^=]*)=(.*)', arg)
118 if not m:
119 raise ValueError("Invalid entry arguemnt '%s'" % arg)
120 entry_args[m.group(1)] = m.group(2)
121
122def GetEntryArg(name):
123 """Get the value of an entry argument
124
125 Args:
126 name: Name of argument to retrieve
127
128 Returns:
129 String value of argument
130 """
131 return entry_args.get(name)
Simon Glassbdb40312018-09-14 04:57:20 -0600132
Simon Glass0c9d5b52018-09-14 04:57:22 -0600133def Prepare(images, dtb):
Simon Glassbdb40312018-09-14 04:57:20 -0600134 """Get device tree files ready for use
135
Simon Glass5a300602019-07-20 12:23:29 -0600136 This sets up a set of device tree files that can be retrieved by
137 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
Simon Glassbdb40312018-09-14 04:57:20 -0600138
139 Args:
Simon Glass0c9d5b52018-09-14 04:57:22 -0600140 images: List of images being used
Simon Glassbdb40312018-09-14 04:57:20 -0600141 dtb: Main dtb
142 """
Simon Glass072959a2019-07-20 12:23:50 -0600143 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glassbdb40312018-09-14 04:57:20 -0600144 # Import these here in case libfdt.py is not available, in which case
145 # the above help option still works.
146 import fdt
147 import fdt_util
148
149 # If we are updating the DTBs we need to put these updated versions
150 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
151 # since it is assumed to be the one passed in with options.dt, and
152 # was handled just above.
153 main_dtb = dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600154 output_fdt_info.clear()
Simon Glass072959a2019-07-20 12:23:50 -0600155 fdt_path_prefix = ''
Simon Glass95f1a942019-07-20 12:23:43 -0600156 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
157 output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
158 output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
Simon Glass0c9d5b52018-09-14 04:57:22 -0600159 if not use_fake_dtb:
Simon Glassf3aba912019-07-20 12:23:34 -0600160 fdt_set = {}
Simon Glass0c9d5b52018-09-14 04:57:22 -0600161 for image in images.values():
Simon Glass132fa722019-07-20 12:23:33 -0600162 fdt_set.update(image.GetFdts())
163 for etype, other in fdt_set.items():
Simon Glass95f1a942019-07-20 12:23:43 -0600164 entry, other_fname = other
Simon Glass0c9d5b52018-09-14 04:57:22 -0600165 infile = tools.GetInputFilename(other_fname)
166 other_fname_dtb = fdt_util.EnsureCompiled(infile)
167 out_fname = tools.GetOutputFilename('%s.out' %
168 os.path.split(other_fname)[1])
169 tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
170 other_dtb = fdt.FdtScan(out_fname)
Simon Glass95f1a942019-07-20 12:23:43 -0600171 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glassbdb40312018-09-14 04:57:20 -0600172
Simon Glass072959a2019-07-20 12:23:50 -0600173def PrepareFromLoadedData(image):
174 """Get device tree files ready for use with a loaded image
175
176 Loaded images are different from images that are being created by binman,
177 since there is generally already an fdtmap and we read the description from
178 that. This provides the position and size of every entry in the image with
179 no calculation required.
180
181 This function uses the same output_fdt_info[] as Prepare(). It finds the
182 device tree files, adds a reference to the fdtmap and sets the FDT path
183 prefix to translate from the fdtmap (where the root node is the image node)
184 to the normal device tree (where the image node is under a /binman node).
185
186 Args:
187 images: List of images being used
188 """
189 global output_fdt_info, main_dtb, fdt_path_prefix
190
191 tout.Info('Preparing device trees')
192 output_fdt_info.clear()
193 fdt_path_prefix = ''
194 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
195 main_dtb = None
196 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
197 for etype, value in image.GetFdts().items():
198 entry, fname = value
199 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
200 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
201 (etype, out_fname, entry.GetPath()))
202 entry._filename = entry.GetDefaultFilename()
203 data = entry.ReadData()
204
205 tools.WriteFile(out_fname, data)
206 dtb = fdt.Fdt(out_fname)
207 dtb.Scan()
208 image_node = dtb.GetNode('/binman')
209 if 'multiple-images' in image_node.props:
210 image_node = dtb.GetNode('/binman/%s' % image.image_node)
211 fdt_path_prefix = image_node.path
212 output_fdt_info[etype] = [dtb, None, entry]
213 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
214
215
Simon Glass5a300602019-07-20 12:23:29 -0600216def GetAllFdts():
Simon Glassbdb40312018-09-14 04:57:20 -0600217 """Yield all device tree files being used by binman
218
219 Yields:
220 Device trees being used (U-Boot proper, SPL, TPL)
221 """
Simon Glass072959a2019-07-20 12:23:50 -0600222 if main_dtb:
223 yield main_dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600224 for etype in output_fdt_info:
225 dtb = output_fdt_info[etype][0]
Simon Glass132fa722019-07-20 12:23:33 -0600226 if dtb != main_dtb:
227 yield dtb
Simon Glassbdb40312018-09-14 04:57:20 -0600228
Simon Glassfb30e292019-07-20 12:23:51 -0600229def GetUpdateNodes(node, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600230 """Yield all the nodes that need to be updated in all device trees
231
232 The property referenced by this node is added to any device trees which
233 have the given node. Due to removable of unwanted notes, SPL and TPL may
234 not have this node.
235
236 Args:
237 node: Node object in the main device tree to look up
Simon Glassfb30e292019-07-20 12:23:51 -0600238 for_repack: True if we want only nodes which need 'repack' properties
239 added to them (e.g. 'orig-offset'), False to return all nodes. We
240 don't add repack properties to SPL/TPL device trees.
Simon Glassc8135dc2018-09-14 04:57:21 -0600241
242 Yields:
243 Node objects in each device tree that is in use (U-Boot proper, which
244 is node, SPL and TPL)
245 """
246 yield node
Simon Glassfb30e292019-07-20 12:23:51 -0600247 for dtb, fname, entry in output_fdt_info.values():
Simon Glasse219aa42018-09-14 04:57:24 -0600248 if dtb != node.GetFdt():
Simon Glassfb30e292019-07-20 12:23:51 -0600249 if for_repack and entry.etype != 'u-boot-dtb':
250 continue
Simon Glass072959a2019-07-20 12:23:50 -0600251 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glasse219aa42018-09-14 04:57:24 -0600252 if other_node:
253 yield other_node
Simon Glassc8135dc2018-09-14 04:57:21 -0600254
Simon Glassfb30e292019-07-20 12:23:51 -0600255def AddZeroProp(node, prop, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600256 """Add a new property to affected device trees with an integer value of 0.
257
258 Args:
259 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600260 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600261 """
Simon Glassfb30e292019-07-20 12:23:51 -0600262 for n in GetUpdateNodes(node, for_repack):
Simon Glassc8135dc2018-09-14 04:57:21 -0600263 n.AddZeroProp(prop)
264
Simon Glassac6328c2018-09-14 04:57:28 -0600265def AddSubnode(node, name):
266 """Add a new subnode to a node in affected device trees
267
268 Args:
269 node: Node to add to
270 name: name of node to add
271
272 Returns:
273 New subnode that was created in main tree
274 """
275 first = None
276 for n in GetUpdateNodes(node):
277 subnode = n.AddSubnode(name)
278 if not first:
279 first = subnode
280 return first
281
282def AddString(node, prop, value):
283 """Add a new string property to affected device trees
284
285 Args:
286 prop_name: Name of property
287 value: String value (which will be \0-terminated in the DT)
288 """
289 for n in GetUpdateNodes(node):
290 n.AddString(prop, value)
291
Simon Glassfb30e292019-07-20 12:23:51 -0600292def SetInt(node, prop, value, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600293 """Update an integer property in affected device trees with an integer value
294
295 This is not allowed to change the size of the FDT.
296
297 Args:
298 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600299 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600300 """
Simon Glassfb30e292019-07-20 12:23:51 -0600301 for n in GetUpdateNodes(node, for_repack):
302 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
303 (node.GetFdt().name, node.path, prop, value))
Simon Glassc8135dc2018-09-14 04:57:21 -0600304 n.SetInt(prop, value)
Simon Glassae7cf032018-09-14 04:57:31 -0600305
306def CheckAddHashProp(node):
307 hash_node = node.FindNode('hash')
308 if hash_node:
309 algo = hash_node.props.get('algo')
310 if not algo:
311 return "Missing 'algo' property for hash node"
312 if algo.value == 'sha256':
313 size = 32
314 else:
315 return "Unknown hash algorithm '%s'" % algo
316 for n in GetUpdateNodes(hash_node):
317 n.AddEmptyProp('value', size)
318
319def CheckSetHashValue(node, get_data_func):
320 hash_node = node.FindNode('hash')
321 if hash_node:
322 algo = hash_node.props.get('algo').value
323 if algo == 'sha256':
324 m = hashlib.sha256()
325 m.update(get_data_func())
326 data = m.digest()
327 for n in GetUpdateNodes(hash_node):
328 n.SetData('value', data)
Simon Glasscbc80e82019-07-08 14:25:36 -0600329
330def SetAllowEntryExpansion(allow):
331 """Set whether post-pack expansion of entries is allowed
332
333 Args:
334 allow: True to allow expansion, False to raise an exception
335 """
336 global allow_entry_expansion
337
338 allow_entry_expansion = allow
339
340def AllowEntryExpansion():
341 """Check whether post-pack expansion of entries is allowed
342
343 Returns:
344 True if expansion should be allowed, False if an exception should be
345 raised
346 """
347 return allow_entry_expansion