]>
Commit | Line | Data |
---|---|---|
c55a50f5 SG |
1 | # SPDX-License-Identifier: GPL-2.0+ |
2 | # Copyright 2018 Google, Inc | |
3 | # Written by Simon Glass <[email protected]> | |
4 | # | |
5 | # Holds and modifies the state information held by binman | |
6 | # | |
7 | ||
e0e5df93 | 8 | import hashlib |
c55a50f5 | 9 | import re |
c55a50f5 | 10 | |
16287933 | 11 | from dtoc import fdt |
c55a50f5 | 12 | import os |
bf776679 SG |
13 | from patman import tools |
14 | from patman import tout | |
c55a50f5 | 15 | |
7697170e SG |
16 | # Map an dtb etype to its expected filename |
17 | DTB_TYPE_FNAME = { | |
18 | 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb', | |
19 | 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb', | |
20 | } | |
21 | ||
fb5e8b16 SG |
22 | # 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 | # | |
cb8bebbd | 26 | # key: entry type (e.g. 'u-boot-dtb) |
fb5e8b16 SG |
27 | # value: tuple: |
28 | # Fdt object | |
29 | # Filename | |
6ca0dcba | 30 | output_fdt_info = {} |
c55a50f5 | 31 | |
10f9d006 SG |
32 | # Prefix to add to an fdtmap path to turn it into a path to the /binman node |
33 | fdt_path_prefix = '' | |
34 | ||
c55a50f5 SG |
35 | # Arguments passed to binman to provide arguments to entries |
36 | entry_args = {} | |
37 | ||
539aece5 SG |
38 | # True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in |
39 | # ftest.py) | |
93d17413 | 40 | use_fake_dtb = False |
539aece5 | 41 | |
2a72cc72 SG |
42 | # The DTB which contains the full image information |
43 | main_dtb = None | |
44 | ||
bf6906ba SG |
45 | # 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() | |
48 | allow_entry_expansion = True | |
49 | ||
61ec04f9 SG |
50 | # 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. | |
56 | allow_entry_contraction = False | |
57 | ||
fb5e8b16 SG |
58 | def GetFdtForEtype(etype): |
59 | """Get the Fdt object for a particular device-tree entry | |
c55a50f5 SG |
60 | |
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 | |
fb5e8b16 | 63 | entry and returns the associated Fdt object. |
c55a50f5 SG |
64 | |
65 | Args: | |
fb5e8b16 | 66 | etype: Entry type of device tree (e.g. 'u-boot-dtb') |
c55a50f5 SG |
67 | |
68 | Returns: | |
fb5e8b16 | 69 | Fdt object associated with the entry type |
c55a50f5 | 70 | """ |
6ca0dcba | 71 | value = output_fdt_info.get(etype); |
6a3b5b54 SG |
72 | if not value: |
73 | return None | |
74 | return value[0] | |
c55a50f5 | 75 | |
fb5e8b16 | 76 | def GetFdtPath(etype): |
c55a50f5 SG |
77 | """Get the full pathname of a particular Fdt object |
78 | ||
726e2961 SG |
79 | Similar to GetFdtForEtype() but returns the pathname associated with the |
80 | Fdt. | |
c55a50f5 SG |
81 | |
82 | Args: | |
fb5e8b16 | 83 | etype: Entry type of device tree (e.g. 'u-boot-dtb') |
c55a50f5 SG |
84 | |
85 | Returns: | |
86 | Full path name to the associated Fdt | |
87 | """ | |
6ca0dcba | 88 | return output_fdt_info[etype][0]._fname |
c55a50f5 | 89 | |
fb5e8b16 | 90 | def GetFdtContents(etype='u-boot-dtb'): |
6ed45ba0 SG |
91 | """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: | |
fb5e8b16 | 98 | etype: Entry type to look up (e.g. 'u-boot.dtb'). |
6ed45ba0 SG |
99 | |
100 | Returns: | |
101 | tuple: | |
102 | pathname to Fdt | |
103 | Fdt data (as bytes) | |
104 | """ | |
6ca0dcba | 105 | if etype not in output_fdt_info: |
6a3b5b54 SG |
106 | return None, None |
107 | if not use_fake_dtb: | |
fb5e8b16 SG |
108 | pathname = GetFdtPath(etype) |
109 | data = GetFdtForEtype(etype).GetContents() | |
6ed45ba0 | 110 | else: |
6ca0dcba | 111 | fname = output_fdt_info[etype][1] |
6ed45ba0 SG |
112 | pathname = tools.GetInputFilename(fname) |
113 | data = tools.ReadFile(pathname) | |
114 | return pathname, data | |
115 | ||
f6e02497 SG |
116 | def 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 | """ | |
cb8bebbd | 126 | dtb, fname = output_fdt_info[etype] |
f6e02497 SG |
127 | dtb_fname = dtb.GetFilename() |
128 | tools.WriteFile(dtb_fname, data) | |
129 | dtb = fdt.FdtScan(dtb_fname) | |
cb8bebbd | 130 | output_fdt_info[etype] = [dtb, fname] |
f6e02497 | 131 | |
c55a50f5 SG |
132 | def 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 | ||
151 | def 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) | |
2a72cc72 | 161 | |
539aece5 | 162 | def Prepare(images, dtb): |
2a72cc72 SG |
163 | """Get device tree files ready for use |
164 | ||
4bdd1159 SG |
165 | 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. | |
2a72cc72 SG |
167 | |
168 | Args: | |
539aece5 | 169 | images: List of images being used |
2a72cc72 SG |
170 | dtb: Main dtb |
171 | """ | |
10f9d006 | 172 | global output_fdt_info, main_dtb, fdt_path_prefix |
2a72cc72 SG |
173 | # Import these here in case libfdt.py is not available, in which case |
174 | # the above help option still works. | |
16287933 SG |
175 | from dtoc import fdt |
176 | from dtoc import fdt_util | |
2a72cc72 SG |
177 | |
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 | |
6ca0dcba | 183 | output_fdt_info.clear() |
10f9d006 | 184 | fdt_path_prefix = '' |
cb8bebbd | 185 | output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb'] |
7697170e SG |
186 | if use_fake_dtb: |
187 | for etype, fname in DTB_TYPE_FNAME.items(): | |
cb8bebbd | 188 | output_fdt_info[etype] = [dtb, fname] |
7697170e | 189 | else: |
f49462e5 | 190 | fdt_set = {} |
539aece5 | 191 | for image in images.values(): |
77e4ef1b SG |
192 | fdt_set.update(image.GetFdts()) |
193 | for etype, other in fdt_set.items(): | |
7697170e SG |
194 | entry, fname = other |
195 | infile = tools.GetInputFilename(fname) | |
196 | fname_dtb = fdt_util.EnsureCompiled(infile) | |
539aece5 | 197 | out_fname = tools.GetOutputFilename('%s.out' % |
7697170e SG |
198 | os.path.split(fname)[1]) |
199 | tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) | |
539aece5 | 200 | other_dtb = fdt.FdtScan(out_fname) |
cb8bebbd | 201 | output_fdt_info[etype] = [other_dtb, out_fname] |
2a72cc72 | 202 | |
10f9d006 SG |
203 | def PrepareFromLoadedData(image): |
204 | """Get device tree files ready for use with a loaded image | |
205 | ||
206 | Loaded images are different from images that are being created by binman, | |
207 | since there is generally already an fdtmap and we read the description from | |
208 | that. This provides the position and size of every entry in the image with | |
209 | no calculation required. | |
210 | ||
211 | This function uses the same output_fdt_info[] as Prepare(). It finds the | |
212 | device tree files, adds a reference to the fdtmap and sets the FDT path | |
213 | prefix to translate from the fdtmap (where the root node is the image node) | |
214 | to the normal device tree (where the image node is under a /binman node). | |
215 | ||
216 | Args: | |
217 | images: List of images being used | |
218 | """ | |
219 | global output_fdt_info, main_dtb, fdt_path_prefix | |
220 | ||
221 | tout.Info('Preparing device trees') | |
222 | output_fdt_info.clear() | |
223 | fdt_path_prefix = '' | |
cb8bebbd | 224 | output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb'] |
10f9d006 SG |
225 | main_dtb = None |
226 | tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name) | |
227 | for etype, value in image.GetFdts().items(): | |
228 | entry, fname = value | |
229 | out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype) | |
230 | tout.Info(" Found device tree type '%s' at '%s' path '%s'" % | |
231 | (etype, out_fname, entry.GetPath())) | |
232 | entry._filename = entry.GetDefaultFilename() | |
233 | data = entry.ReadData() | |
234 | ||
235 | tools.WriteFile(out_fname, data) | |
236 | dtb = fdt.Fdt(out_fname) | |
237 | dtb.Scan() | |
238 | image_node = dtb.GetNode('/binman') | |
239 | if 'multiple-images' in image_node.props: | |
240 | image_node = dtb.GetNode('/binman/%s' % image.image_node) | |
241 | fdt_path_prefix = image_node.path | |
cb8bebbd | 242 | output_fdt_info[etype] = [dtb, None] |
10f9d006 SG |
243 | tout.Info(" FDT path prefix '%s'" % fdt_path_prefix) |
244 | ||
245 | ||
4bdd1159 | 246 | def GetAllFdts(): |
2a72cc72 SG |
247 | """Yield all device tree files being used by binman |
248 | ||
249 | Yields: | |
250 | Device trees being used (U-Boot proper, SPL, TPL) | |
251 | """ | |
10f9d006 SG |
252 | if main_dtb: |
253 | yield main_dtb | |
6ca0dcba SG |
254 | for etype in output_fdt_info: |
255 | dtb = output_fdt_info[etype][0] | |
77e4ef1b SG |
256 | if dtb != main_dtb: |
257 | yield dtb | |
2a72cc72 | 258 | |
12bb1a99 | 259 | def GetUpdateNodes(node, for_repack=False): |
f46621d2 SG |
260 | """Yield all the nodes that need to be updated in all device trees |
261 | ||
262 | The property referenced by this node is added to any device trees which | |
263 | have the given node. Due to removable of unwanted notes, SPL and TPL may | |
264 | not have this node. | |
265 | ||
266 | Args: | |
267 | node: Node object in the main device tree to look up | |
12bb1a99 SG |
268 | for_repack: True if we want only nodes which need 'repack' properties |
269 | added to them (e.g. 'orig-offset'), False to return all nodes. We | |
270 | don't add repack properties to SPL/TPL device trees. | |
f46621d2 SG |
271 | |
272 | Yields: | |
273 | Node objects in each device tree that is in use (U-Boot proper, which | |
274 | is node, SPL and TPL) | |
275 | """ | |
276 | yield node | |
cb8bebbd | 277 | for entry_type, (dtb, fname) in output_fdt_info.items(): |
6ed45ba0 | 278 | if dtb != node.GetFdt(): |
cb8bebbd | 279 | if for_repack and entry_type != 'u-boot-dtb': |
12bb1a99 | 280 | continue |
10f9d006 | 281 | other_node = dtb.GetNode(fdt_path_prefix + node.path) |
6ed45ba0 SG |
282 | if other_node: |
283 | yield other_node | |
f46621d2 | 284 | |
12bb1a99 | 285 | def AddZeroProp(node, prop, for_repack=False): |
f46621d2 SG |
286 | """Add a new property to affected device trees with an integer value of 0. |
287 | ||
288 | Args: | |
289 | prop_name: Name of property | |
12bb1a99 | 290 | for_repack: True is this property is only needed for repacking |
f46621d2 | 291 | """ |
12bb1a99 | 292 | for n in GetUpdateNodes(node, for_repack): |
f46621d2 SG |
293 | n.AddZeroProp(prop) |
294 | ||
0a98b28b SG |
295 | def AddSubnode(node, name): |
296 | """Add a new subnode to a node in affected device trees | |
297 | ||
298 | Args: | |
299 | node: Node to add to | |
300 | name: name of node to add | |
301 | ||
302 | Returns: | |
303 | New subnode that was created in main tree | |
304 | """ | |
305 | first = None | |
306 | for n in GetUpdateNodes(node): | |
307 | subnode = n.AddSubnode(name) | |
308 | if not first: | |
309 | first = subnode | |
310 | return first | |
311 | ||
312 | def AddString(node, prop, value): | |
313 | """Add a new string property to affected device trees | |
314 | ||
315 | Args: | |
316 | prop_name: Name of property | |
317 | value: String value (which will be \0-terminated in the DT) | |
318 | """ | |
319 | for n in GetUpdateNodes(node): | |
320 | n.AddString(prop, value) | |
321 | ||
6eb99326 SG |
322 | def AddInt(node, prop, value): |
323 | """Add a new string property to affected device trees | |
324 | ||
325 | Args: | |
326 | prop_name: Name of property | |
327 | val: Integer value of property | |
328 | """ | |
329 | for n in GetUpdateNodes(node): | |
330 | n.AddInt(prop, value) | |
331 | ||
12bb1a99 | 332 | def SetInt(node, prop, value, for_repack=False): |
f46621d2 SG |
333 | """Update an integer property in affected device trees with an integer value |
334 | ||
335 | This is not allowed to change the size of the FDT. | |
336 | ||
337 | Args: | |
338 | prop_name: Name of property | |
12bb1a99 | 339 | for_repack: True is this property is only needed for repacking |
f46621d2 | 340 | """ |
12bb1a99 SG |
341 | for n in GetUpdateNodes(node, for_repack): |
342 | tout.Detail("File %s: Update node '%s' prop '%s' to %#x" % | |
51014aab | 343 | (n.GetFdt().name, n.path, prop, value)) |
f46621d2 | 344 | n.SetInt(prop, value) |
e0e5df93 SG |
345 | |
346 | def CheckAddHashProp(node): | |
347 | hash_node = node.FindNode('hash') | |
348 | if hash_node: | |
349 | algo = hash_node.props.get('algo') | |
350 | if not algo: | |
351 | return "Missing 'algo' property for hash node" | |
352 | if algo.value == 'sha256': | |
353 | size = 32 | |
354 | else: | |
355 | return "Unknown hash algorithm '%s'" % algo | |
356 | for n in GetUpdateNodes(hash_node): | |
357 | n.AddEmptyProp('value', size) | |
358 | ||
359 | def CheckSetHashValue(node, get_data_func): | |
360 | hash_node = node.FindNode('hash') | |
361 | if hash_node: | |
362 | algo = hash_node.props.get('algo').value | |
363 | if algo == 'sha256': | |
364 | m = hashlib.sha256() | |
365 | m.update(get_data_func()) | |
366 | data = m.digest() | |
367 | for n in GetUpdateNodes(hash_node): | |
368 | n.SetData('value', data) | |
bf6906ba SG |
369 | |
370 | def SetAllowEntryExpansion(allow): | |
371 | """Set whether post-pack expansion of entries is allowed | |
372 | ||
373 | Args: | |
374 | allow: True to allow expansion, False to raise an exception | |
375 | """ | |
376 | global allow_entry_expansion | |
377 | ||
378 | allow_entry_expansion = allow | |
379 | ||
380 | def AllowEntryExpansion(): | |
381 | """Check whether post-pack expansion of entries is allowed | |
382 | ||
383 | Returns: | |
384 | True if expansion should be allowed, False if an exception should be | |
385 | raised | |
386 | """ | |
387 | return allow_entry_expansion | |
61ec04f9 SG |
388 | |
389 | def SetAllowEntryContraction(allow): | |
390 | """Set whether post-pack contraction of entries is allowed | |
391 | ||
392 | Args: | |
393 | allow: True to allow contraction, False to raise an exception | |
394 | """ | |
395 | global allow_entry_contraction | |
396 | ||
397 | allow_entry_contraction = allow | |
398 | ||
399 | def AllowEntryContraction(): | |
400 | """Check whether post-pack contraction of entries is allowed | |
401 | ||
402 | Returns: | |
403 | True if contraction should be allowed, False if an exception should be | |
404 | raised | |
405 | """ | |
406 | return allow_entry_contraction |