blob: 2379e24ef6dff79af57c89b4c95e6be44f8cb56f [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 Glassfb5e8b12019-07-20 12:23:32 -060045def GetFdtForEtype(etype):
46 """Get the Fdt object for a particular device-tree entry
Simon Glassc55a50f2018-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 Glassfb5e8b12019-07-20 12:23:32 -060050 entry and returns the associated Fdt object.
Simon Glassc55a50f2018-09-14 04:57:19 -060051
52 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060053 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060054
55 Returns:
Simon Glassfb5e8b12019-07-20 12:23:32 -060056 Fdt object associated with the entry type
Simon Glassc55a50f2018-09-14 04:57:19 -060057 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060058 value = output_fdt_info.get(etype);
Simon Glass6a3b5b52019-07-20 12:23:42 -060059 if not value:
60 return None
61 return value[0]
Simon Glassc55a50f2018-09-14 04:57:19 -060062
Simon Glassfb5e8b12019-07-20 12:23:32 -060063def GetFdtPath(etype):
Simon Glassc55a50f2018-09-14 04:57:19 -060064 """Get the full pathname of a particular Fdt object
65
Simon Glass726e2962019-07-20 12:23:30 -060066 Similar to GetFdtForEtype() but returns the pathname associated with the
67 Fdt.
Simon Glassc55a50f2018-09-14 04:57:19 -060068
69 Args:
Simon Glassfb5e8b12019-07-20 12:23:32 -060070 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glassc55a50f2018-09-14 04:57:19 -060071
72 Returns:
73 Full path name to the associated Fdt
74 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060075 return output_fdt_info[etype][0]._fname
Simon Glassc55a50f2018-09-14 04:57:19 -060076
Simon Glassfb5e8b12019-07-20 12:23:32 -060077def GetFdtContents(etype='u-boot-dtb'):
Simon Glass6ed45ba2018-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 Glassfb5e8b12019-07-20 12:23:32 -060085 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glass6ed45ba2018-09-14 04:57:24 -060086
87 Returns:
88 tuple:
89 pathname to Fdt
90 Fdt data (as bytes)
91 """
Simon Glass6ca0dcb2019-07-20 12:23:43 -060092 if etype not in output_fdt_info:
Simon Glass6a3b5b52019-07-20 12:23:42 -060093 return None, None
94 if not use_fake_dtb:
Simon Glassfb5e8b12019-07-20 12:23:32 -060095 pathname = GetFdtPath(etype)
96 data = GetFdtForEtype(etype).GetContents()
Simon Glass6ed45ba2018-09-14 04:57:24 -060097 else:
Simon Glass6ca0dcb2019-07-20 12:23:43 -060098 fname = output_fdt_info[etype][1]
Simon Glass6ed45ba2018-09-14 04:57:24 -060099 pathname = tools.GetInputFilename(fname)
100 data = tools.ReadFile(pathname)
101 return pathname, data
102
Simon Glassc55a50f2018-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 Glass2a72cc72018-09-14 04:57:20 -0600132
Simon Glass539aece2018-09-14 04:57:22 -0600133def Prepare(images, dtb):
Simon Glass2a72cc72018-09-14 04:57:20 -0600134 """Get device tree files ready for use
135
Simon Glass4bdd1152019-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 Glass2a72cc72018-09-14 04:57:20 -0600138
139 Args:
Simon Glass539aece2018-09-14 04:57:22 -0600140 images: List of images being used
Simon Glass2a72cc72018-09-14 04:57:20 -0600141 dtb: Main dtb
142 """
Simon Glass10f9d002019-07-20 12:23:50 -0600143 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glass2a72cc72018-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 Glass6ca0dcb2019-07-20 12:23:43 -0600154 output_fdt_info.clear()
Simon Glass10f9d002019-07-20 12:23:50 -0600155 fdt_path_prefix = ''
Simon Glass6ca0dcb2019-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 Glass539aece2018-09-14 04:57:22 -0600159 if not use_fake_dtb:
Simon Glassf49462e2019-07-20 12:23:34 -0600160 fdt_set = {}
Simon Glass539aece2018-09-14 04:57:22 -0600161 for image in images.values():
Simon Glass77e4ef12019-07-20 12:23:33 -0600162 fdt_set.update(image.GetFdts())
163 for etype, other in fdt_set.items():
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600164 entry, other_fname = other
Simon Glass539aece2018-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 Glass6ca0dcb2019-07-20 12:23:43 -0600171 output_fdt_info[etype] = [other_dtb, out_fname, entry]
Simon Glass2a72cc72018-09-14 04:57:20 -0600172
Simon Glass10f9d002019-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 Glass4bdd1152019-07-20 12:23:29 -0600216def GetAllFdts():
Simon Glass2a72cc72018-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 Glass10f9d002019-07-20 12:23:50 -0600222 if main_dtb:
223 yield main_dtb
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600224 for etype in output_fdt_info:
225 dtb = output_fdt_info[etype][0]
Simon Glass77e4ef12019-07-20 12:23:33 -0600226 if dtb != main_dtb:
227 yield dtb
Simon Glass2a72cc72018-09-14 04:57:20 -0600228
Simon Glass12bb1a92019-07-20 12:23:51 -0600229def GetUpdateNodes(node, for_repack=False):
Simon Glassf46621d2018-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 Glass12bb1a92019-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 Glassf46621d2018-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 Glass12bb1a92019-07-20 12:23:51 -0600247 for dtb, fname, entry in output_fdt_info.values():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600248 if dtb != node.GetFdt():
Simon Glass12bb1a92019-07-20 12:23:51 -0600249 if for_repack and entry.etype != 'u-boot-dtb':
250 continue
Simon Glass10f9d002019-07-20 12:23:50 -0600251 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600252 if other_node:
253 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600254
Simon Glass12bb1a92019-07-20 12:23:51 -0600255def AddZeroProp(node, prop, for_repack=False):
Simon Glassf46621d2018-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 Glass12bb1a92019-07-20 12:23:51 -0600260 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600261 """
Simon Glass12bb1a92019-07-20 12:23:51 -0600262 for n in GetUpdateNodes(node, for_repack):
Simon Glassf46621d2018-09-14 04:57:21 -0600263 n.AddZeroProp(prop)
264
Simon Glass0a98b282018-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 Glass12bb1a92019-07-20 12:23:51 -0600292def SetInt(node, prop, value, for_repack=False):
Simon Glassf46621d2018-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 Glass12bb1a92019-07-20 12:23:51 -0600299 for_repack: True is this property is only needed for repacking
Simon Glassf46621d2018-09-14 04:57:21 -0600300 """
Simon Glass12bb1a92019-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 Glassf46621d2018-09-14 04:57:21 -0600304 n.SetInt(prop, value)
Simon Glasse0e5df92018-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 Glassbf6906b2019-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