env: Kconfig: cosmetics: update comment for SYS_RELOC_GD_ENV_ADDR
[oweals/u-boot.git] / tools / binman / state.py
1 # 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
8 import hashlib
9 import re
10
11 from dtoc import fdt
12 import os
13 from patman import tools
14 from patman import tout
15
16 # 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
24 #       Entry object, or None if not known
25 output_fdt_info = {}
26
27 # Prefix to add to an fdtmap path to turn it into a path to the /binman node
28 fdt_path_prefix = ''
29
30 # Arguments passed to binman to provide arguments to entries
31 entry_args = {}
32
33 # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
34 # ftest.py)
35 use_fake_dtb = False
36
37 # The DTB which contains the full image information
38 main_dtb = None
39
40 # 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()
43 allow_entry_expansion = True
44
45 # 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.
51 allow_entry_contraction = False
52
53 def GetFdtForEtype(etype):
54     """Get the Fdt object for a particular device-tree entry
55
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
58     entry and returns the associated Fdt object.
59
60     Args:
61         etype: Entry type of device tree (e.g. 'u-boot-dtb')
62
63     Returns:
64         Fdt object associated with the entry type
65     """
66     value = output_fdt_info.get(etype);
67     if not value:
68         return None
69     return value[0]
70
71 def GetFdtPath(etype):
72     """Get the full pathname of a particular Fdt object
73
74     Similar to GetFdtForEtype() but returns the pathname associated with the
75     Fdt.
76
77     Args:
78         etype: Entry type of device tree (e.g. 'u-boot-dtb')
79
80     Returns:
81         Full path name to the associated Fdt
82     """
83     return output_fdt_info[etype][0]._fname
84
85 def GetFdtContents(etype='u-boot-dtb'):
86     """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:
93         etype: Entry type to look up (e.g. 'u-boot.dtb').
94
95     Returns:
96         tuple:
97             pathname to Fdt
98             Fdt data (as bytes)
99     """
100     if etype not in output_fdt_info:
101         return None, None
102     if not use_fake_dtb:
103         pathname = GetFdtPath(etype)
104         data = GetFdtForEtype(etype).GetContents()
105     else:
106         fname = output_fdt_info[etype][1]
107         pathname = tools.GetInputFilename(fname)
108         data = tools.ReadFile(pathname)
109     return pathname, data
110
111 def UpdateFdtContents(etype, data):
112     """Update the contents of a particular device tree
113
114     The device tree is updated and written back to its file. This affects what
115     is returned from future called to GetFdtContents(), etc.
116
117     Args:
118         etype: Entry type (e.g. 'u-boot-dtb')
119         data: Data to replace the DTB with
120     """
121     dtb, fname, entry = output_fdt_info[etype]
122     dtb_fname = dtb.GetFilename()
123     tools.WriteFile(dtb_fname, data)
124     dtb = fdt.FdtScan(dtb_fname)
125     output_fdt_info[etype] = [dtb, fname, entry]
126
127 def SetEntryArgs(args):
128     """Set the value of the entry args
129
130     This sets up the entry_args dict which is used to supply entry arguments to
131     entries.
132
133     Args:
134         args: List of entry arguments, each in the format "name=value"
135     """
136     global entry_args
137
138     entry_args = {}
139     if args:
140         for arg in args:
141             m = re.match('([^=]*)=(.*)', arg)
142             if not m:
143                 raise ValueError("Invalid entry arguemnt '%s'" % arg)
144             entry_args[m.group(1)] = m.group(2)
145
146 def GetEntryArg(name):
147     """Get the value of an entry argument
148
149     Args:
150         name: Name of argument to retrieve
151
152     Returns:
153         String value of argument
154     """
155     return entry_args.get(name)
156
157 def Prepare(images, dtb):
158     """Get device tree files ready for use
159
160     This sets up a set of device tree files that can be retrieved by
161     GetAllFdts(). This includes U-Boot proper and any SPL device trees.
162
163     Args:
164         images: List of images being used
165         dtb: Main dtb
166     """
167     global output_fdt_info, main_dtb, fdt_path_prefix
168     # Import these here in case libfdt.py is not available, in which case
169     # the above help option still works.
170     from dtoc import fdt
171     from dtoc import fdt_util
172
173     # If we are updating the DTBs we need to put these updated versions
174     # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
175     # since it is assumed to be the one passed in with options.dt, and
176     # was handled just above.
177     main_dtb = dtb
178     output_fdt_info.clear()
179     fdt_path_prefix = ''
180     output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb', None]
181     output_fdt_info['u-boot-spl-dtb'] = [dtb, 'spl/u-boot-spl.dtb', None]
182     output_fdt_info['u-boot-tpl-dtb'] = [dtb, 'tpl/u-boot-tpl.dtb', None]
183     if not use_fake_dtb:
184         fdt_set = {}
185         for image in images.values():
186             fdt_set.update(image.GetFdts())
187         for etype, other in fdt_set.items():
188             entry, other_fname = other
189             infile = tools.GetInputFilename(other_fname)
190             other_fname_dtb = fdt_util.EnsureCompiled(infile)
191             out_fname = tools.GetOutputFilename('%s.out' %
192                     os.path.split(other_fname)[1])
193             tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
194             other_dtb = fdt.FdtScan(out_fname)
195             output_fdt_info[etype] = [other_dtb, out_fname, entry]
196
197 def PrepareFromLoadedData(image):
198     """Get device tree files ready for use with a loaded image
199
200     Loaded images are different from images that are being created by binman,
201     since there is generally already an fdtmap and we read the description from
202     that. This provides the position and size of every entry in the image with
203     no calculation required.
204
205     This function uses the same output_fdt_info[] as Prepare(). It finds the
206     device tree files, adds a reference to the fdtmap and sets the FDT path
207     prefix to translate from the fdtmap (where the root node is the image node)
208     to the normal device tree (where the image node is under a /binman node).
209
210     Args:
211         images: List of images being used
212     """
213     global output_fdt_info, main_dtb, fdt_path_prefix
214
215     tout.Info('Preparing device trees')
216     output_fdt_info.clear()
217     fdt_path_prefix = ''
218     output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb', None]
219     main_dtb = None
220     tout.Info("   Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name)
221     for etype, value in image.GetFdts().items():
222         entry, fname = value
223         out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype)
224         tout.Info("   Found device tree type '%s' at '%s' path '%s'" %
225                   (etype, out_fname, entry.GetPath()))
226         entry._filename = entry.GetDefaultFilename()
227         data = entry.ReadData()
228
229         tools.WriteFile(out_fname, data)
230         dtb = fdt.Fdt(out_fname)
231         dtb.Scan()
232         image_node = dtb.GetNode('/binman')
233         if 'multiple-images' in image_node.props:
234             image_node = dtb.GetNode('/binman/%s' % image.image_node)
235         fdt_path_prefix = image_node.path
236         output_fdt_info[etype] = [dtb, None, entry]
237     tout.Info("   FDT path prefix '%s'" % fdt_path_prefix)
238
239
240 def GetAllFdts():
241     """Yield all device tree files being used by binman
242
243     Yields:
244         Device trees being used (U-Boot proper, SPL, TPL)
245     """
246     if main_dtb:
247         yield main_dtb
248     for etype in output_fdt_info:
249         dtb = output_fdt_info[etype][0]
250         if dtb != main_dtb:
251             yield dtb
252
253 def GetUpdateNodes(node, for_repack=False):
254     """Yield all the nodes that need to be updated in all device trees
255
256     The property referenced by this node is added to any device trees which
257     have the given node. Due to removable of unwanted notes, SPL and TPL may
258     not have this node.
259
260     Args:
261         node: Node object in the main device tree to look up
262         for_repack: True if we want only nodes which need 'repack' properties
263             added to them (e.g. 'orig-offset'), False to return all nodes. We
264             don't add repack properties to SPL/TPL device trees.
265
266     Yields:
267         Node objects in each device tree that is in use (U-Boot proper, which
268             is node, SPL and TPL)
269     """
270     yield node
271     for dtb, fname, entry in output_fdt_info.values():
272         if dtb != node.GetFdt():
273             if for_repack and entry.etype != 'u-boot-dtb':
274                 continue
275             other_node = dtb.GetNode(fdt_path_prefix + node.path)
276             #print('   try', fdt_path_prefix + node.path, other_node)
277             if other_node:
278                 yield other_node
279
280 def AddZeroProp(node, prop, for_repack=False):
281     """Add a new property to affected device trees with an integer value of 0.
282
283     Args:
284         prop_name: Name of property
285         for_repack: True is this property is only needed for repacking
286     """
287     for n in GetUpdateNodes(node, for_repack):
288         n.AddZeroProp(prop)
289
290 def AddSubnode(node, name):
291     """Add a new subnode to a node in affected device trees
292
293     Args:
294         node: Node to add to
295         name: name of node to add
296
297     Returns:
298         New subnode that was created in main tree
299     """
300     first = None
301     for n in GetUpdateNodes(node):
302         subnode = n.AddSubnode(name)
303         if not first:
304             first = subnode
305     return first
306
307 def AddString(node, prop, value):
308     """Add a new string property to affected device trees
309
310     Args:
311         prop_name: Name of property
312         value: String value (which will be \0-terminated in the DT)
313     """
314     for n in GetUpdateNodes(node):
315         n.AddString(prop, value)
316
317 def SetInt(node, prop, value, for_repack=False):
318     """Update an integer property in affected device trees with an integer value
319
320     This is not allowed to change the size of the FDT.
321
322     Args:
323         prop_name: Name of property
324         for_repack: True is this property is only needed for repacking
325     """
326     for n in GetUpdateNodes(node, for_repack):
327         tout.Detail("File %s: Update node '%s' prop '%s' to %#x" %
328                     (n.GetFdt().name, n.path, prop, value))
329         n.SetInt(prop, value)
330
331 def CheckAddHashProp(node):
332     hash_node = node.FindNode('hash')
333     if hash_node:
334         algo = hash_node.props.get('algo')
335         if not algo:
336             return "Missing 'algo' property for hash node"
337         if algo.value == 'sha256':
338             size = 32
339         else:
340             return "Unknown hash algorithm '%s'" % algo
341         for n in GetUpdateNodes(hash_node):
342             n.AddEmptyProp('value', size)
343
344 def CheckSetHashValue(node, get_data_func):
345     hash_node = node.FindNode('hash')
346     if hash_node:
347         algo = hash_node.props.get('algo').value
348         if algo == 'sha256':
349             m = hashlib.sha256()
350             m.update(get_data_func())
351             data = m.digest()
352         for n in GetUpdateNodes(hash_node):
353             n.SetData('value', data)
354
355 def SetAllowEntryExpansion(allow):
356     """Set whether post-pack expansion of entries is allowed
357
358     Args:
359        allow: True to allow expansion, False to raise an exception
360     """
361     global allow_entry_expansion
362
363     allow_entry_expansion = allow
364
365 def AllowEntryExpansion():
366     """Check whether post-pack expansion of entries is allowed
367
368     Returns:
369         True if expansion should be allowed, False if an exception should be
370             raised
371     """
372     return allow_entry_expansion
373
374 def SetAllowEntryContraction(allow):
375     """Set whether post-pack contraction of entries is allowed
376
377     Args:
378        allow: True to allow contraction, False to raise an exception
379     """
380     global allow_entry_contraction
381
382     allow_entry_contraction = allow
383
384 def AllowEntryContraction():
385     """Check whether post-pack contraction of entries is allowed
386
387     Returns:
388         True if contraction should be allowed, False if an exception should be
389             raised
390     """
391     return allow_entry_contraction