blob: 08e627985de399d832283ac8effb4955b83fedf7 [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 Glassf46621d2018-09-14 04:57:21 -0600229def GetUpdateNodes(node):
230 """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
238
239 Yields:
240 Node objects in each device tree that is in use (U-Boot proper, which
241 is node, SPL and TPL)
242 """
243 yield node
Simon Glass6ca0dcb2019-07-20 12:23:43 -0600244 for dtb, fname, _ in output_fdt_info.values():
Simon Glass6ed45ba2018-09-14 04:57:24 -0600245 if dtb != node.GetFdt():
Simon Glass10f9d002019-07-20 12:23:50 -0600246 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600247 if other_node:
248 yield other_node
Simon Glassf46621d2018-09-14 04:57:21 -0600249
250def AddZeroProp(node, prop):
251 """Add a new property to affected device trees with an integer value of 0.
252
253 Args:
254 prop_name: Name of property
255 """
256 for n in GetUpdateNodes(node):
257 n.AddZeroProp(prop)
258
Simon Glass0a98b282018-09-14 04:57:28 -0600259def AddSubnode(node, name):
260 """Add a new subnode to a node in affected device trees
261
262 Args:
263 node: Node to add to
264 name: name of node to add
265
266 Returns:
267 New subnode that was created in main tree
268 """
269 first = None
270 for n in GetUpdateNodes(node):
271 subnode = n.AddSubnode(name)
272 if not first:
273 first = subnode
274 return first
275
276def AddString(node, prop, value):
277 """Add a new string property to affected device trees
278
279 Args:
280 prop_name: Name of property
281 value: String value (which will be \0-terminated in the DT)
282 """
283 for n in GetUpdateNodes(node):
284 n.AddString(prop, value)
285
Simon Glassf46621d2018-09-14 04:57:21 -0600286def SetInt(node, prop, value):
287 """Update an integer property in affected device trees with an integer value
288
289 This is not allowed to change the size of the FDT.
290
291 Args:
292 prop_name: Name of property
293 """
294 for n in GetUpdateNodes(node):
295 n.SetInt(prop, value)
Simon Glasse0e5df92018-09-14 04:57:31 -0600296
297def CheckAddHashProp(node):
298 hash_node = node.FindNode('hash')
299 if hash_node:
300 algo = hash_node.props.get('algo')
301 if not algo:
302 return "Missing 'algo' property for hash node"
303 if algo.value == 'sha256':
304 size = 32
305 else:
306 return "Unknown hash algorithm '%s'" % algo
307 for n in GetUpdateNodes(hash_node):
308 n.AddEmptyProp('value', size)
309
310def CheckSetHashValue(node, get_data_func):
311 hash_node = node.FindNode('hash')
312 if hash_node:
313 algo = hash_node.props.get('algo').value
314 if algo == 'sha256':
315 m = hashlib.sha256()
316 m.update(get_data_func())
317 data = m.digest()
318 for n in GetUpdateNodes(hash_node):
319 n.SetData('value', data)
Simon Glassbf6906b2019-07-08 14:25:36 -0600320
321def SetAllowEntryExpansion(allow):
322 """Set whether post-pack expansion of entries is allowed
323
324 Args:
325 allow: True to allow expansion, False to raise an exception
326 """
327 global allow_entry_expansion
328
329 allow_entry_expansion = allow
330
331def AllowEntryExpansion():
332 """Check whether post-pack expansion of entries is allowed
333
334 Returns:
335 True if expansion should be allowed, False if an exception should be
336 raised
337 """
338 return allow_entry_expansion