blob: 84f606b188516422ae1948f65ce0648161a643e3 [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 Glassc585dd42020-04-17 18:09:03 -060011from dtoc import fdt
Simon Glass29aa7362018-09-14 04:57:19 -060012import os
Simon Glassa997ea52020-04-17 18:09:04 -060013from patman import tools
14from patman import tout
Simon Glass29aa7362018-09-14 04:57:19 -060015
Simon Glassf184d952021-03-18 20:25:00 +130016# Map an dtb etype to its expected filename
17DTB_TYPE_FNAME = {
18 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb',
19 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb',
20 }
21
Simon Glass2be381d2019-07-20 12:23:32 -060022# Records the device-tree files known to binman, keyed by entry type (e.g.
23# 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by
24# binman. They have been copied to <xxx>.out files.
25#
Simon Glassa1ba6182021-03-18 20:25:01 +130026# key: entry type (e.g. 'u-boot-dtb)
Simon Glass2be381d2019-07-20 12:23:32 -060027# value: tuple:
28# Fdt object
29# Filename
Simon Glass95f1a942019-07-20 12:23:43 -060030output_fdt_info = {}
Simon Glass29aa7362018-09-14 04:57:19 -060031
Simon Glass072959a2019-07-20 12:23:50 -060032# Prefix to add to an fdtmap path to turn it into a path to the /binman node
33fdt_path_prefix = ''
34
Simon Glass29aa7362018-09-14 04:57:19 -060035# Arguments passed to binman to provide arguments to entries
36entry_args = {}
37
Simon Glass0c9d5b52018-09-14 04:57:22 -060038# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
39# ftest.py)
Simon Glass31402012018-09-14 04:57:23 -060040use_fake_dtb = False
Simon Glass0c9d5b52018-09-14 04:57:22 -060041
Simon Glassbdb40312018-09-14 04:57:20 -060042# The DTB which contains the full image information
43main_dtb = None
44
Simon Glasscbc80e82019-07-08 14:25:36 -060045# Allow entries to expand after they have been packed. This is detected and
46# forces a re-pack. If not allowed, any attempted expansion causes an error in
47# Entry.ProcessContentsUpdate()
48allow_entry_expansion = True
49
Simon Glass9d8ee322019-07-20 12:23:58 -060050# Don't allow entries to contract after they have been packed. Instead just
51# leave some wasted space. If allowed, this is detected and forces a re-pack,
52# but may result in entries that oscillate in size, thus causing a pack error.
53# An example is a compressed device tree where the original offset values
54# result in a larger compressed size than the new ones, but then after updating
55# to the new ones, the compressed size increases, etc.
56allow_entry_contraction = False
57
Simon Glass2be381d2019-07-20 12:23:32 -060058def GetFdtForEtype(etype):
59 """Get the Fdt object for a particular device-tree entry
Simon Glass29aa7362018-09-14 04:57:19 -060060
61 Binman keeps track of at least one device-tree file called u-boot.dtb but
62 can also have others (e.g. for SPL). This function looks up the given
Simon Glass2be381d2019-07-20 12:23:32 -060063 entry and returns the associated Fdt object.
Simon Glass29aa7362018-09-14 04:57:19 -060064
65 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060066 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060067
68 Returns:
Simon Glass2be381d2019-07-20 12:23:32 -060069 Fdt object associated with the entry type
Simon Glass29aa7362018-09-14 04:57:19 -060070 """
Simon Glass95f1a942019-07-20 12:23:43 -060071 value = output_fdt_info.get(etype);
Simon Glass385138a2019-07-20 12:23:42 -060072 if not value:
73 return None
74 return value[0]
Simon Glass29aa7362018-09-14 04:57:19 -060075
Simon Glass2be381d2019-07-20 12:23:32 -060076def GetFdtPath(etype):
Simon Glass29aa7362018-09-14 04:57:19 -060077 """Get the full pathname of a particular Fdt object
78
Simon Glassa3030a52019-07-20 12:23:30 -060079 Similar to GetFdtForEtype() but returns the pathname associated with the
80 Fdt.
Simon Glass29aa7362018-09-14 04:57:19 -060081
82 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060083 etype: Entry type of device tree (e.g. 'u-boot-dtb')
Simon Glass29aa7362018-09-14 04:57:19 -060084
85 Returns:
86 Full path name to the associated Fdt
87 """
Simon Glass95f1a942019-07-20 12:23:43 -060088 return output_fdt_info[etype][0]._fname
Simon Glass29aa7362018-09-14 04:57:19 -060089
Simon Glass2be381d2019-07-20 12:23:32 -060090def GetFdtContents(etype='u-boot-dtb'):
Simon Glasse219aa42018-09-14 04:57:24 -060091 """Looks up the FDT pathname and contents
92
93 This is used to obtain the Fdt pathname and contents when needed by an
94 entry. It supports a 'fake' dtb, allowing tests to substitute test data for
95 the real dtb.
96
97 Args:
Simon Glass2be381d2019-07-20 12:23:32 -060098 etype: Entry type to look up (e.g. 'u-boot.dtb').
Simon Glasse219aa42018-09-14 04:57:24 -060099
100 Returns:
101 tuple:
102 pathname to Fdt
103 Fdt data (as bytes)
104 """
Simon Glass95f1a942019-07-20 12:23:43 -0600105 if etype not in output_fdt_info:
Simon Glass385138a2019-07-20 12:23:42 -0600106 return None, None
107 if not use_fake_dtb:
Simon Glass2be381d2019-07-20 12:23:32 -0600108 pathname = GetFdtPath(etype)
109 data = GetFdtForEtype(etype).GetContents()
Simon Glasse219aa42018-09-14 04:57:24 -0600110 else:
Simon Glass95f1a942019-07-20 12:23:43 -0600111 fname = output_fdt_info[etype][1]
Simon Glasse219aa42018-09-14 04:57:24 -0600112 pathname = tools.GetInputFilename(fname)
113 data = tools.ReadFile(pathname)
114 return pathname, data
115
Simon Glass74f5feb2019-07-20 12:24:08 -0600116def UpdateFdtContents(etype, data):
117 """Update the contents of a particular device tree
118
119 The device tree is updated and written back to its file. This affects what
120 is returned from future called to GetFdtContents(), etc.
121
122 Args:
123 etype: Entry type (e.g. 'u-boot-dtb')
124 data: Data to replace the DTB with
125 """
Simon Glassa1ba6182021-03-18 20:25:01 +1300126 dtb, fname = output_fdt_info[etype]
Simon Glass74f5feb2019-07-20 12:24:08 -0600127 dtb_fname = dtb.GetFilename()
128 tools.WriteFile(dtb_fname, data)
129 dtb = fdt.FdtScan(dtb_fname)
Simon Glassa1ba6182021-03-18 20:25:01 +1300130 output_fdt_info[etype] = [dtb, fname]
Simon Glass74f5feb2019-07-20 12:24:08 -0600131
Simon Glass29aa7362018-09-14 04:57:19 -0600132def SetEntryArgs(args):
133 """Set the value of the entry args
134
135 This sets up the entry_args dict which is used to supply entry arguments to
136 entries.
137
138 Args:
139 args: List of entry arguments, each in the format "name=value"
140 """
141 global entry_args
142
143 entry_args = {}
144 if args:
145 for arg in args:
146 m = re.match('([^=]*)=(.*)', arg)
147 if not m:
148 raise ValueError("Invalid entry arguemnt '%s'" % arg)
149 entry_args[m.group(1)] = m.group(2)
150
151def GetEntryArg(name):
152 """Get the value of an entry argument
153
154 Args:
155 name: Name of argument to retrieve
156
157 Returns:
158 String value of argument
159 """
160 return entry_args.get(name)
Simon Glassbdb40312018-09-14 04:57:20 -0600161
Simon Glass0c9d5b52018-09-14 04:57:22 -0600162def Prepare(images, dtb):
Simon Glassbdb40312018-09-14 04:57:20 -0600163 """Get device tree files ready for use
164
Simon Glass5a300602019-07-20 12:23:29 -0600165 This sets up a set of device tree files that can be retrieved by
166 GetAllFdts(). This includes U-Boot proper and any SPL device trees.
Simon Glassbdb40312018-09-14 04:57:20 -0600167
168 Args:
Simon Glass0c9d5b52018-09-14 04:57:22 -0600169 images: List of images being used
Simon Glassbdb40312018-09-14 04:57:20 -0600170 dtb: Main dtb
171 """
Simon Glass072959a2019-07-20 12:23:50 -0600172 global output_fdt_info, main_dtb, fdt_path_prefix
Simon Glassbdb40312018-09-14 04:57:20 -0600173 # Import these here in case libfdt.py is not available, in which case
174 # the above help option still works.
Simon Glassc585dd42020-04-17 18:09:03 -0600175 from dtoc import fdt
176 from dtoc import fdt_util
Simon Glassbdb40312018-09-14 04:57:20 -0600177
178 # If we are updating the DTBs we need to put these updated versions
179 # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
180 # since it is assumed to be the one passed in with options.dt, and
181 # was handled just above.
182 main_dtb = dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600183 output_fdt_info.clear()
Simon Glass072959a2019-07-20 12:23:50 -0600184 fdt_path_prefix = ''
Simon Glassa1ba6182021-03-18 20:25:01 +1300185 output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb']
Simon Glassf184d952021-03-18 20:25:00 +1300186 if use_fake_dtb:
187 for etype, fname in DTB_TYPE_FNAME.items():
Simon Glassa1ba6182021-03-18 20:25:01 +1300188 output_fdt_info[etype] = [dtb, fname]
Simon Glassf184d952021-03-18 20:25:00 +1300189 else:
Simon Glassf3aba912019-07-20 12:23:34 -0600190 fdt_set = {}
Simon Glasseb751fd2021-03-18 20:25:03 +1300191 for etype, fname in DTB_TYPE_FNAME.items():
192 infile = tools.GetInputFilename(fname, allow_missing=True)
193 if infile and os.path.exists(infile):
194 fname_dtb = fdt_util.EnsureCompiled(infile)
195 out_fname = tools.GetOutputFilename('%s.out' %
196 os.path.split(fname)[1])
197 tools.WriteFile(out_fname, tools.ReadFile(fname_dtb))
198 other_dtb = fdt.FdtScan(out_fname)
199 output_fdt_info[etype] = [other_dtb, out_fname]
200
Simon Glassbdb40312018-09-14 04:57:20 -0600201
Simon Glass072959a2019-07-20 12:23:50 -0600202def PrepareFromLoadedData(image):
203 """Get device tree files ready for use with a loaded image
204
205 Loaded images are different from images that are being created by binman,
206 since there is generally already an fdtmap and we read the description from
207 that. This provides the position and size of every entry in the image with
208 no calculation required.
209
210 This function uses the same output_fdt_info[] as Prepare(). It finds the
211 device tree files, adds a reference to the fdtmap and sets the FDT path
212 prefix to translate from the fdtmap (where the root node is the image node)
213 to the normal device tree (where the image node is under a /binman node).
214
215 Args:
216 images: List of images being used
217 """
218 global output_fdt_info, main_dtb, fdt_path_prefix
219
220 tout.Info('Preparing device trees')
221 output_fdt_info.clear()
222 fdt_path_prefix = ''
Simon Glassa1ba6182021-03-18 20:25:01 +1300223 output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb']
Simon Glass072959a2019-07-20 12:23:50 -0600224 main_dtb = None
225 tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
226 for etype, value in image.GetFdts().items():
227 entry, fname = value
228 out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
229 tout.Info(" Found device tree type '%s' at '%s' path '%s'" %
230 (etype, out_fname, entry.GetPath()))
231 entry._filename = entry.GetDefaultFilename()
232 data = entry.ReadData()
233
234 tools.WriteFile(out_fname, data)
235 dtb = fdt.Fdt(out_fname)
236 dtb.Scan()
237 image_node = dtb.GetNode('/binman')
238 if 'multiple-images' in image_node.props:
239 image_node = dtb.GetNode('/binman/%s' % image.image_node)
240 fdt_path_prefix = image_node.path
Simon Glassa1ba6182021-03-18 20:25:01 +1300241 output_fdt_info[etype] = [dtb, None]
Simon Glass072959a2019-07-20 12:23:50 -0600242 tout.Info(" FDT path prefix '%s'" % fdt_path_prefix)
243
244
Simon Glass5a300602019-07-20 12:23:29 -0600245def GetAllFdts():
Simon Glassbdb40312018-09-14 04:57:20 -0600246 """Yield all device tree files being used by binman
247
248 Yields:
249 Device trees being used (U-Boot proper, SPL, TPL)
250 """
Simon Glass072959a2019-07-20 12:23:50 -0600251 if main_dtb:
252 yield main_dtb
Simon Glass95f1a942019-07-20 12:23:43 -0600253 for etype in output_fdt_info:
254 dtb = output_fdt_info[etype][0]
Simon Glass132fa722019-07-20 12:23:33 -0600255 if dtb != main_dtb:
256 yield dtb
Simon Glassbdb40312018-09-14 04:57:20 -0600257
Simon Glassfb30e292019-07-20 12:23:51 -0600258def GetUpdateNodes(node, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600259 """Yield all the nodes that need to be updated in all device trees
260
261 The property referenced by this node is added to any device trees which
262 have the given node. Due to removable of unwanted notes, SPL and TPL may
263 not have this node.
264
265 Args:
266 node: Node object in the main device tree to look up
Simon Glassfb30e292019-07-20 12:23:51 -0600267 for_repack: True if we want only nodes which need 'repack' properties
268 added to them (e.g. 'orig-offset'), False to return all nodes. We
269 don't add repack properties to SPL/TPL device trees.
Simon Glassc8135dc2018-09-14 04:57:21 -0600270
271 Yields:
272 Node objects in each device tree that is in use (U-Boot proper, which
273 is node, SPL and TPL)
274 """
275 yield node
Simon Glassa1ba6182021-03-18 20:25:01 +1300276 for entry_type, (dtb, fname) in output_fdt_info.items():
Simon Glasse219aa42018-09-14 04:57:24 -0600277 if dtb != node.GetFdt():
Simon Glassa1ba6182021-03-18 20:25:01 +1300278 if for_repack and entry_type != 'u-boot-dtb':
Simon Glassfb30e292019-07-20 12:23:51 -0600279 continue
Simon Glass072959a2019-07-20 12:23:50 -0600280 other_node = dtb.GetNode(fdt_path_prefix + node.path)
Simon Glasse219aa42018-09-14 04:57:24 -0600281 if other_node:
282 yield other_node
Simon Glassc8135dc2018-09-14 04:57:21 -0600283
Simon Glassfb30e292019-07-20 12:23:51 -0600284def AddZeroProp(node, prop, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600285 """Add a new property to affected device trees with an integer value of 0.
286
287 Args:
288 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600289 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600290 """
Simon Glassfb30e292019-07-20 12:23:51 -0600291 for n in GetUpdateNodes(node, for_repack):
Simon Glassc8135dc2018-09-14 04:57:21 -0600292 n.AddZeroProp(prop)
293
Simon Glassac6328c2018-09-14 04:57:28 -0600294def AddSubnode(node, name):
295 """Add a new subnode to a node in affected device trees
296
297 Args:
298 node: Node to add to
299 name: name of node to add
300
301 Returns:
302 New subnode that was created in main tree
303 """
304 first = None
305 for n in GetUpdateNodes(node):
306 subnode = n.AddSubnode(name)
307 if not first:
308 first = subnode
309 return first
310
311def AddString(node, prop, value):
312 """Add a new string property to affected device trees
313
314 Args:
315 prop_name: Name of property
316 value: String value (which will be \0-terminated in the DT)
317 """
318 for n in GetUpdateNodes(node):
319 n.AddString(prop, value)
320
Simon Glassa2af7302021-01-06 21:35:18 -0700321def AddInt(node, prop, value):
322 """Add a new string property to affected device trees
323
324 Args:
325 prop_name: Name of property
326 val: Integer value of property
327 """
328 for n in GetUpdateNodes(node):
329 n.AddInt(prop, value)
330
Simon Glassfb30e292019-07-20 12:23:51 -0600331def SetInt(node, prop, value, for_repack=False):
Simon Glassc8135dc2018-09-14 04:57:21 -0600332 """Update an integer property in affected device trees with an integer value
333
334 This is not allowed to change the size of the FDT.
335
336 Args:
337 prop_name: Name of property
Simon Glassfb30e292019-07-20 12:23:51 -0600338 for_repack: True is this property is only needed for repacking
Simon Glassc8135dc2018-09-14 04:57:21 -0600339 """
Simon Glassfb30e292019-07-20 12:23:51 -0600340 for n in GetUpdateNodes(node, for_repack):
341 tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
Simon Glassf8a54bc2019-07-20 12:23:56 -0600342 (n.GetFdt().name, n.path, prop, value))
Simon Glassc8135dc2018-09-14 04:57:21 -0600343 n.SetInt(prop, value)
Simon Glassae7cf032018-09-14 04:57:31 -0600344
345def CheckAddHashProp(node):
346 hash_node = node.FindNode('hash')
347 if hash_node:
348 algo = hash_node.props.get('algo')
349 if not algo:
350 return "Missing 'algo' property for hash node"
351 if algo.value == 'sha256':
352 size = 32
353 else:
354 return "Unknown hash algorithm '%s'" % algo
355 for n in GetUpdateNodes(hash_node):
356 n.AddEmptyProp('value', size)
357
358def CheckSetHashValue(node, get_data_func):
359 hash_node = node.FindNode('hash')
360 if hash_node:
361 algo = hash_node.props.get('algo').value
362 if algo == 'sha256':
363 m = hashlib.sha256()
364 m.update(get_data_func())
365 data = m.digest()
366 for n in GetUpdateNodes(hash_node):
367 n.SetData('value', data)
Simon Glasscbc80e82019-07-08 14:25:36 -0600368
369def SetAllowEntryExpansion(allow):
370 """Set whether post-pack expansion of entries is allowed
371
372 Args:
373 allow: True to allow expansion, False to raise an exception
374 """
375 global allow_entry_expansion
376
377 allow_entry_expansion = allow
378
379def AllowEntryExpansion():
380 """Check whether post-pack expansion of entries is allowed
381
382 Returns:
383 True if expansion should be allowed, False if an exception should be
384 raised
385 """
386 return allow_entry_expansion
Simon Glass9d8ee322019-07-20 12:23:58 -0600387
388def SetAllowEntryContraction(allow):
389 """Set whether post-pack contraction of entries is allowed
390
391 Args:
392 allow: True to allow contraction, False to raise an exception
393 """
394 global allow_entry_contraction
395
396 allow_entry_contraction = allow
397
398def AllowEntryContraction():
399 """Check whether post-pack contraction of entries is allowed
400
401 Returns:
402 True if contraction should be allowed, False if an exception should be
403 raised
404 """
405 return allow_entry_contraction