blob: 5b10f19236015117f74aac81c3faa0122d0b9e5a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassbf7fd502016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glassbf7fd502016-11-25 20:15:51 -07005# Creates binary images from input files controlled by a description
6#
7
8from collections import OrderedDict
Simon Glass87d43322020-08-05 13:27:46 -06009import glob
Simon Glassbf7fd502016-11-25 20:15:51 -070010import os
Simon Glass9fbfaba2020-08-29 11:36:14 -060011import pkg_resources
Simon Glassb2381432020-09-06 10:39:09 -060012import re
Simon Glass9fbfaba2020-08-29 11:36:14 -060013
Simon Glassbf7fd502016-11-25 20:15:51 -070014import sys
Simon Glassbf776672020-04-17 18:09:04 -060015from patman import tools
Simon Glassbf7fd502016-11-25 20:15:51 -070016
Simon Glass386c63c2022-01-09 20:13:50 -070017from binman import bintool
Simon Glass16287932020-04-17 18:09:03 -060018from binman import cbfs_util
19from binman import elf
Simon Glassbf776672020-04-17 18:09:04 -060020from patman import command
21from patman import tout
Simon Glassbf7fd502016-11-25 20:15:51 -070022
23# List of images we plan to create
24# Make this global so that it can be referenced from tests
25images = OrderedDict()
26
Simon Glassb2381432020-09-06 10:39:09 -060027# Help text for each type of missing blob, dict:
28# key: Value of the entry's 'missing-msg' or entry name
29# value: Text for the help
30missing_blob_help = {}
31
Simon Glass0b6023e2021-03-18 20:25:06 +130032def _ReadImageDesc(binman_node, use_expanded):
Simon Glassbf7fd502016-11-25 20:15:51 -070033 """Read the image descriptions from the /binman node
34
35 This normally produces a single Image object called 'image'. But if
36 multiple images are present, they will all be returned.
37
38 Args:
39 binman_node: Node object of the /binman node
Simon Glass0b6023e2021-03-18 20:25:06 +130040 use_expanded: True if the FDT will be updated with the entry information
Simon Glassbf7fd502016-11-25 20:15:51 -070041 Returns:
42 OrderedDict of Image objects, each of which describes an image
43 """
44 images = OrderedDict()
45 if 'multiple-images' in binman_node.props:
46 for node in binman_node.subnodes:
Simon Glass0b6023e2021-03-18 20:25:06 +130047 images[node.name] = Image(node.name, node,
48 use_expanded=use_expanded)
Simon Glassbf7fd502016-11-25 20:15:51 -070049 else:
Simon Glass0b6023e2021-03-18 20:25:06 +130050 images['image'] = Image('image', binman_node, use_expanded=use_expanded)
Simon Glassbf7fd502016-11-25 20:15:51 -070051 return images
52
Simon Glassec3f3782017-05-27 07:38:29 -060053def _FindBinmanNode(dtb):
Simon Glassbf7fd502016-11-25 20:15:51 -070054 """Find the 'binman' node in the device tree
55
56 Args:
Simon Glassec3f3782017-05-27 07:38:29 -060057 dtb: Fdt object to scan
Simon Glassbf7fd502016-11-25 20:15:51 -070058 Returns:
59 Node object of /binman node, or None if not found
60 """
Simon Glassec3f3782017-05-27 07:38:29 -060061 for node in dtb.GetRoot().subnodes:
Simon Glassbf7fd502016-11-25 20:15:51 -070062 if node.name == 'binman':
63 return node
64 return None
65
Simon Glassb2381432020-09-06 10:39:09 -060066def _ReadMissingBlobHelp():
67 """Read the missing-blob-help file
68
69 This file containins help messages explaining what to do when external blobs
70 are missing.
71
72 Returns:
73 Dict:
74 key: Message tag (str)
75 value: Message text (str)
76 """
77
78 def _FinishTag(tag, msg, result):
79 if tag:
80 result[tag] = msg.rstrip()
81 tag = None
82 msg = ''
83 return tag, msg
84
85 my_data = pkg_resources.resource_string(__name__, 'missing-blob-help')
86 re_tag = re.compile('^([-a-z0-9]+):$')
87 result = {}
88 tag = None
89 msg = ''
90 for line in my_data.decode('utf-8').splitlines():
91 if not line.startswith('#'):
92 m_tag = re_tag.match(line)
93 if m_tag:
94 _, msg = _FinishTag(tag, msg, result)
95 tag = m_tag.group(1)
96 elif tag:
97 msg += line + '\n'
98 _FinishTag(tag, msg, result)
99 return result
100
101def _ShowBlobHelp(path, text):
102 tout.Warning('\n%s:' % path)
103 for line in text.splitlines():
104 tout.Warning(' %s' % line)
105
106def _ShowHelpForMissingBlobs(missing_list):
107 """Show help for each missing blob to help the user take action
108
109 Args:
110 missing_list: List of Entry objects to show help for
111 """
112 global missing_blob_help
113
114 if not missing_blob_help:
115 missing_blob_help = _ReadMissingBlobHelp()
116
117 for entry in missing_list:
118 tags = entry.GetHelpTags()
119
120 # Show the first match help message
121 for tag in tags:
122 if tag in missing_blob_help:
123 _ShowBlobHelp(entry._node.path, missing_blob_help[tag])
124 break
125
Simon Glass87d43322020-08-05 13:27:46 -0600126def GetEntryModules(include_testing=True):
127 """Get a set of entry class implementations
128
129 Returns:
130 Set of paths to entry class filenames
131 """
Simon Glass9fbfaba2020-08-29 11:36:14 -0600132 glob_list = pkg_resources.resource_listdir(__name__, 'etype')
133 glob_list = [fname for fname in glob_list if fname.endswith('.py')]
Simon Glass87d43322020-08-05 13:27:46 -0600134 return set([os.path.splitext(os.path.basename(item))[0]
135 for item in glob_list
136 if include_testing or '_testing' not in item])
137
Simon Glassc55a50f2018-09-14 04:57:19 -0600138def WriteEntryDocs(modules, test_missing=None):
139 """Write out documentation for all entries
Simon Glassecab8972018-07-06 10:27:40 -0600140
141 Args:
Simon Glassc55a50f2018-09-14 04:57:19 -0600142 modules: List of Module objects to get docs for
143 test_missing: Used for testing only, to force an entry's documeentation
144 to show as missing even if it is present. Should be set to None in
145 normal use.
Simon Glassecab8972018-07-06 10:27:40 -0600146 """
Simon Glass16287932020-04-17 18:09:03 -0600147 from binman.entry import Entry
Simon Glassfd8d1f72018-07-17 13:25:36 -0600148 Entry.WriteDocs(modules, test_missing)
149
Simon Glass61f564d2019-07-08 14:25:48 -0600150
151def ListEntries(image_fname, entry_paths):
152 """List the entries in an image
153
154 This decodes the supplied image and displays a table of entries from that
155 image, preceded by a header.
156
157 Args:
158 image_fname: Image filename to process
159 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
160 'section/u-boot'])
161 """
162 image = Image.FromFile(image_fname)
163
164 entries, lines, widths = image.GetListEntries(entry_paths)
165
166 num_columns = len(widths)
167 for linenum, line in enumerate(lines):
168 if linenum == 1:
169 # Print header line
170 print('-' * (sum(widths) + num_columns * 2))
171 out = ''
172 for i, item in enumerate(line):
173 width = -widths[i]
174 if item.startswith('>'):
175 width = -width
176 item = item[1:]
177 txt = '%*s ' % (width, item)
178 out += txt
179 print(out.rstrip())
180
Simon Glassf667e452019-07-08 14:25:50 -0600181
182def ReadEntry(image_fname, entry_path, decomp=True):
183 """Extract an entry from an image
184
185 This extracts the data from a particular entry in an image
186
187 Args:
188 image_fname: Image filename to process
189 entry_path: Path to entry to extract
190 decomp: True to return uncompressed data, if the data is compress
191 False to return the raw data
192
193 Returns:
194 data extracted from the entry
195 """
Simon Glass8dbb7442019-08-24 07:22:44 -0600196 global Image
Simon Glass07237982020-08-05 13:27:47 -0600197 from binman.image import Image
Simon Glass8dbb7442019-08-24 07:22:44 -0600198
Simon Glassf667e452019-07-08 14:25:50 -0600199 image = Image.FromFile(image_fname)
200 entry = image.FindEntryPath(entry_path)
201 return entry.ReadData(decomp)
202
203
Simon Glass943bf782021-11-23 21:09:50 -0700204def ShowAltFormats(image):
205 """Show alternative formats available for entries in the image
206
207 This shows a list of formats available.
208
209 Args:
210 image (Image): Image to check
211 """
212 alt_formats = {}
213 image.CheckAltFormats(alt_formats)
214 print('%-10s %-20s %s' % ('Flag (-F)', 'Entry type', 'Description'))
215 for name, val in alt_formats.items():
216 entry, helptext = val
217 print('%-10s %-20s %s' % (name, entry.etype, helptext))
218
219
Simon Glass71ce0ba2019-07-08 14:25:52 -0600220def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
Simon Glass943bf782021-11-23 21:09:50 -0700221 decomp=True, alt_format=None):
Simon Glass71ce0ba2019-07-08 14:25:52 -0600222 """Extract the data from one or more entries and write it to files
223
224 Args:
225 image_fname: Image filename to process
226 output_fname: Single output filename to use if extracting one file, None
227 otherwise
228 outdir: Output directory to use (for any number of files), else None
229 entry_paths: List of entry paths to extract
Simon Glass3ad804e2019-07-20 12:24:12 -0600230 decomp: True to decompress the entry data
Simon Glass71ce0ba2019-07-08 14:25:52 -0600231
232 Returns:
233 List of EntryInfo records that were written
234 """
235 image = Image.FromFile(image_fname)
236
Simon Glass943bf782021-11-23 21:09:50 -0700237 if alt_format == 'list':
238 ShowAltFormats(image)
239 return
240
Simon Glass71ce0ba2019-07-08 14:25:52 -0600241 # Output an entry to a single file, as a special case
242 if output_fname:
243 if not entry_paths:
Simon Glassbb5edc12019-07-20 12:24:14 -0600244 raise ValueError('Must specify an entry path to write with -f')
Simon Glass71ce0ba2019-07-08 14:25:52 -0600245 if len(entry_paths) != 1:
Simon Glassbb5edc12019-07-20 12:24:14 -0600246 raise ValueError('Must specify exactly one entry path to write with -f')
Simon Glass71ce0ba2019-07-08 14:25:52 -0600247 entry = image.FindEntryPath(entry_paths[0])
Simon Glass943bf782021-11-23 21:09:50 -0700248 data = entry.ReadData(decomp, alt_format)
Simon Glass71ce0ba2019-07-08 14:25:52 -0600249 tools.WriteFile(output_fname, data)
250 tout.Notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname))
251 return
252
253 # Otherwise we will output to a path given by the entry path of each entry.
254 # This means that entries will appear in subdirectories if they are part of
255 # a sub-section.
256 einfos = image.GetListEntries(entry_paths)[0]
257 tout.Notice('%d entries match and will be written' % len(einfos))
258 for einfo in einfos:
259 entry = einfo.entry
Simon Glass943bf782021-11-23 21:09:50 -0700260 data = entry.ReadData(decomp, alt_format)
Simon Glass71ce0ba2019-07-08 14:25:52 -0600261 path = entry.GetPath()[1:]
262 fname = os.path.join(outdir, path)
263
264 # If this entry has children, create a directory for it and put its
265 # data in a file called 'root' in that directory
266 if entry.GetEntries():
Simon Glass862ddf92021-03-18 20:24:51 +1300267 if fname and not os.path.exists(fname):
Simon Glass71ce0ba2019-07-08 14:25:52 -0600268 os.makedirs(fname)
269 fname = os.path.join(fname, 'root')
Simon Glass5b378e42021-01-06 21:35:13 -0700270 tout.Notice("Write entry '%s' size %x to '%s'" %
271 (entry.GetPath(), len(data), fname))
Simon Glass71ce0ba2019-07-08 14:25:52 -0600272 tools.WriteFile(fname, data)
273 return einfos
274
275
Simon Glassd7fa4e42019-07-20 12:24:13 -0600276def BeforeReplace(image, allow_resize):
277 """Handle getting an image ready for replacing entries in it
278
279 Args:
280 image: Image to prepare
281 """
282 state.PrepareFromLoadedData(image)
283 image.LoadData()
284
285 # If repacking, drop the old offset/size values except for the original
286 # ones, so we are only left with the constraints.
287 if allow_resize:
288 image.ResetForPack()
289
290
291def ReplaceOneEntry(image, entry, data, do_compress, allow_resize):
292 """Handle replacing a single entry an an image
293
294 Args:
295 image: Image to update
296 entry: Entry to write
297 data: Data to replace with
298 do_compress: True to compress the data if needed, False if data is
299 already compressed so should be used as is
300 allow_resize: True to allow entries to change size (this does a re-pack
301 of the entries), False to raise an exception
302 """
303 if not entry.WriteData(data, do_compress):
304 if not image.allow_repack:
305 entry.Raise('Entry data size does not match, but allow-repack is not present for this image')
306 if not allow_resize:
307 entry.Raise('Entry data size does not match, but resize is disabled')
308
309
310def AfterReplace(image, allow_resize, write_map):
311 """Handle write out an image after replacing entries in it
312
313 Args:
314 image: Image to write
315 allow_resize: True to allow entries to change size (this does a re-pack
316 of the entries), False to raise an exception
317 write_map: True to write a map file
318 """
319 tout.Info('Processing image')
320 ProcessImage(image, update_fdt=True, write_map=write_map,
321 get_contents=False, allow_resize=allow_resize)
322
323
324def WriteEntryToImage(image, entry, data, do_compress=True, allow_resize=True,
325 write_map=False):
326 BeforeReplace(image, allow_resize)
327 tout.Info('Writing data to %s' % entry.GetPath())
328 ReplaceOneEntry(image, entry, data, do_compress, allow_resize)
329 AfterReplace(image, allow_resize=allow_resize, write_map=write_map)
330
331
Simon Glass3ad804e2019-07-20 12:24:12 -0600332def WriteEntry(image_fname, entry_path, data, do_compress=True,
333 allow_resize=True, write_map=False):
Simon Glass22a76b72019-07-20 12:24:11 -0600334 """Replace an entry in an image
335
336 This replaces the data in a particular entry in an image. This size of the
337 new data must match the size of the old data unless allow_resize is True.
338
339 Args:
340 image_fname: Image filename to process
341 entry_path: Path to entry to extract
342 data: Data to replace with
Simon Glass3ad804e2019-07-20 12:24:12 -0600343 do_compress: True to compress the data if needed, False if data is
Simon Glass22a76b72019-07-20 12:24:11 -0600344 already compressed so should be used as is
345 allow_resize: True to allow entries to change size (this does a re-pack
346 of the entries), False to raise an exception
Simon Glass3ad804e2019-07-20 12:24:12 -0600347 write_map: True to write a map file
Simon Glass22a76b72019-07-20 12:24:11 -0600348
349 Returns:
350 Image object that was updated
351 """
Simon Glassd7fa4e42019-07-20 12:24:13 -0600352 tout.Info("Write entry '%s', file '%s'" % (entry_path, image_fname))
Simon Glass22a76b72019-07-20 12:24:11 -0600353 image = Image.FromFile(image_fname)
354 entry = image.FindEntryPath(entry_path)
Simon Glassd7fa4e42019-07-20 12:24:13 -0600355 WriteEntryToImage(image, entry, data, do_compress=do_compress,
356 allow_resize=allow_resize, write_map=write_map)
Simon Glass22a76b72019-07-20 12:24:11 -0600357
Simon Glass22a76b72019-07-20 12:24:11 -0600358 return image
359
Simon Glassa6cb9952019-07-20 12:24:15 -0600360
361def ReplaceEntries(image_fname, input_fname, indir, entry_paths,
362 do_compress=True, allow_resize=True, write_map=False):
363 """Replace the data from one or more entries from input files
364
365 Args:
366 image_fname: Image filename to process
Jan Kiszka89cc0522021-11-11 08:13:30 +0100367 input_fname: Single input filename to use if replacing one file, None
Simon Glassa6cb9952019-07-20 12:24:15 -0600368 otherwise
369 indir: Input directory to use (for any number of files), else None
Jan Kiszka89cc0522021-11-11 08:13:30 +0100370 entry_paths: List of entry paths to replace
Simon Glassa6cb9952019-07-20 12:24:15 -0600371 do_compress: True if the input data is uncompressed and may need to be
372 compressed if the entry requires it, False if the data is already
373 compressed.
374 write_map: True to write a map file
375
376 Returns:
377 List of EntryInfo records that were written
378 """
Jan Kiszkac700f102021-11-11 08:14:18 +0100379 image_fname = os.path.abspath(image_fname)
Simon Glassa6cb9952019-07-20 12:24:15 -0600380 image = Image.FromFile(image_fname)
381
382 # Replace an entry from a single file, as a special case
383 if input_fname:
384 if not entry_paths:
385 raise ValueError('Must specify an entry path to read with -f')
386 if len(entry_paths) != 1:
387 raise ValueError('Must specify exactly one entry path to write with -f')
388 entry = image.FindEntryPath(entry_paths[0])
389 data = tools.ReadFile(input_fname)
390 tout.Notice("Read %#x bytes from file '%s'" % (len(data), input_fname))
391 WriteEntryToImage(image, entry, data, do_compress=do_compress,
392 allow_resize=allow_resize, write_map=write_map)
393 return
394
395 # Otherwise we will input from a path given by the entry path of each entry.
396 # This means that files must appear in subdirectories if they are part of
397 # a sub-section.
398 einfos = image.GetListEntries(entry_paths)[0]
399 tout.Notice("Replacing %d matching entries in image '%s'" %
400 (len(einfos), image_fname))
401
402 BeforeReplace(image, allow_resize)
403
404 for einfo in einfos:
405 entry = einfo.entry
406 if entry.GetEntries():
407 tout.Info("Skipping section entry '%s'" % entry.GetPath())
408 continue
409
410 path = entry.GetPath()[1:]
411 fname = os.path.join(indir, path)
412
413 if os.path.exists(fname):
414 tout.Notice("Write entry '%s' from file '%s'" %
415 (entry.GetPath(), fname))
416 data = tools.ReadFile(fname)
417 ReplaceOneEntry(image, entry, data, do_compress, allow_resize)
418 else:
419 tout.Warning("Skipping entry '%s' from missing file '%s'" %
420 (entry.GetPath(), fname))
421
422 AfterReplace(image, allow_resize=allow_resize, write_map=write_map)
423 return image
424
425
Simon Glass0b6023e2021-03-18 20:25:06 +1300426def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded):
Simon Glassa8573c42019-07-20 12:23:27 -0600427 """Prepare the images to be processed and select the device tree
428
429 This function:
430 - reads in the device tree
431 - finds and scans the binman node to create all entries
432 - selects which images to build
433 - Updates the device tress with placeholder properties for offset,
434 image-pos, etc.
435
436 Args:
437 dtb_fname: Filename of the device tree file to use (.dts or .dtb)
438 selected_images: List of images to output, or None for all
439 update_fdt: True to update the FDT wth entry offsets, etc.
Simon Glass0b6023e2021-03-18 20:25:06 +1300440 use_expanded: True to use expanded versions of entries, if available.
441 So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This
442 is needed if update_fdt is True (although tests may disable it)
Simon Glasse9d336d2020-09-01 05:13:55 -0600443
444 Returns:
445 OrderedDict of images:
446 key: Image name (str)
447 value: Image object
Simon Glassa8573c42019-07-20 12:23:27 -0600448 """
449 # Import these here in case libfdt.py is not available, in which case
450 # the above help option still works.
Simon Glass16287932020-04-17 18:09:03 -0600451 from dtoc import fdt
452 from dtoc import fdt_util
Simon Glassa8573c42019-07-20 12:23:27 -0600453 global images
454
455 # Get the device tree ready by compiling it and copying the compiled
456 # output into a file in our output directly. Then scan it for use
457 # in binman.
458 dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
459 fname = tools.GetOutputFilename('u-boot.dtb.out')
460 tools.WriteFile(fname, tools.ReadFile(dtb_fname))
461 dtb = fdt.FdtScan(fname)
462
463 node = _FindBinmanNode(dtb)
464 if not node:
465 raise ValueError("Device tree '%s' does not have a 'binman' "
466 "node" % dtb_fname)
467
Simon Glass0b6023e2021-03-18 20:25:06 +1300468 images = _ReadImageDesc(node, use_expanded)
Simon Glassa8573c42019-07-20 12:23:27 -0600469
470 if select_images:
471 skip = []
472 new_images = OrderedDict()
473 for name, image in images.items():
474 if name in select_images:
475 new_images[name] = image
476 else:
477 skip.append(name)
478 images = new_images
479 tout.Notice('Skipping images: %s' % ', '.join(skip))
480
481 state.Prepare(images, dtb)
482
483 # Prepare the device tree by making sure that any missing
484 # properties are added (e.g. 'pos' and 'size'). The values of these
485 # may not be correct yet, but we add placeholders so that the
486 # size of the device tree is correct. Later, in
487 # SetCalculatedProperties() we will insert the correct values
488 # without changing the device-tree size, thus ensuring that our
489 # entry offsets remain the same.
490 for image in images.values():
Simon Glass386c63c2022-01-09 20:13:50 -0700491 image.CollectBintools()
Simon Glassa8573c42019-07-20 12:23:27 -0600492 image.ExpandEntries()
493 if update_fdt:
Simon Glassa9fad072020-10-26 17:40:17 -0600494 image.AddMissingProperties(True)
Simon Glassa8573c42019-07-20 12:23:27 -0600495 image.ProcessFdt(dtb)
496
Simon Glass4bdd1152019-07-20 12:23:29 -0600497 for dtb_item in state.GetAllFdts():
Simon Glassa8573c42019-07-20 12:23:27 -0600498 dtb_item.Sync(auto_resize=True)
499 dtb_item.Pack()
500 dtb_item.Flush()
501 return images
502
503
Simon Glass51014aa2019-07-20 12:23:56 -0600504def ProcessImage(image, update_fdt, write_map, get_contents=True,
Heiko Thierya89c8f22022-01-06 11:49:41 +0100505 allow_resize=True, allow_missing=False,
506 allow_fake_blobs=False):
Simon Glassb88e81c2019-07-20 12:23:24 -0600507 """Perform all steps for this image, including checking and # writing it.
508
509 This means that errors found with a later image will be reported after
510 earlier images are already completed and written, but that does not seem
511 important.
512
513 Args:
514 image: Image to process
515 update_fdt: True to update the FDT wth entry offsets, etc.
516 write_map: True to write a map file
Simon Glass10f9d002019-07-20 12:23:50 -0600517 get_contents: True to get the image contents from files, etc., False if
518 the contents is already present
Simon Glass51014aa2019-07-20 12:23:56 -0600519 allow_resize: True to allow entries to change size (this does a re-pack
520 of the entries), False to raise an exception
Simon Glass4f9f1052020-07-09 18:39:38 -0600521 allow_missing: Allow blob_ext objects to be missing
Heiko Thierya89c8f22022-01-06 11:49:41 +0100522 allow_fake_blobs: Allow blob_ext objects to be faked with dummy files
Simon Glassb1cca952020-07-09 18:39:40 -0600523
524 Returns:
Heiko Thierya89c8f22022-01-06 11:49:41 +0100525 True if one or more external blobs are missing or faked,
526 False if all are present
Simon Glassb88e81c2019-07-20 12:23:24 -0600527 """
Simon Glass10f9d002019-07-20 12:23:50 -0600528 if get_contents:
Simon Glass4f9f1052020-07-09 18:39:38 -0600529 image.SetAllowMissing(allow_missing)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100530 image.SetAllowFakeBlob(allow_fake_blobs)
Simon Glass10f9d002019-07-20 12:23:50 -0600531 image.GetEntryContents()
Simon Glassb88e81c2019-07-20 12:23:24 -0600532 image.GetEntryOffsets()
533
534 # We need to pack the entries to figure out where everything
535 # should be placed. This sets the offset/size of each entry.
536 # However, after packing we call ProcessEntryContents() which
537 # may result in an entry changing size. In that case we need to
538 # do another pass. Since the device tree often contains the
539 # final offset/size information we try to make space for this in
540 # AddMissingProperties() above. However, if the device is
541 # compressed we cannot know this compressed size in advance,
542 # since changing an offset from 0x100 to 0x104 (for example) can
543 # alter the compressed size of the device tree. So we need a
544 # third pass for this.
Simon Glasseb0f4a42019-07-20 12:24:06 -0600545 passes = 5
Simon Glassb88e81c2019-07-20 12:23:24 -0600546 for pack_pass in range(passes):
547 try:
548 image.PackEntries()
Simon Glassb88e81c2019-07-20 12:23:24 -0600549 except Exception as e:
550 if write_map:
551 fname = image.WriteMap()
552 print("Wrote map file '%s' to show errors" % fname)
553 raise
554 image.SetImagePos()
555 if update_fdt:
556 image.SetCalculatedProperties()
Simon Glass4bdd1152019-07-20 12:23:29 -0600557 for dtb_item in state.GetAllFdts():
Simon Glassb88e81c2019-07-20 12:23:24 -0600558 dtb_item.Sync()
Simon Glass51014aa2019-07-20 12:23:56 -0600559 dtb_item.Flush()
Simon Glass261cbe02019-08-24 07:23:12 -0600560 image.WriteSymbols()
Simon Glassb88e81c2019-07-20 12:23:24 -0600561 sizes_ok = image.ProcessEntryContents()
562 if sizes_ok:
563 break
564 image.ResetForPack()
Simon Glassaed6c0b2019-08-24 07:23:13 -0600565 tout.Info('Pack completed after %d pass(es)' % (pack_pass + 1))
Simon Glassb88e81c2019-07-20 12:23:24 -0600566 if not sizes_ok:
Simon Glass61ec04f2019-07-20 12:23:58 -0600567 image.Raise('Entries changed size after packing (tried %s passes)' %
Simon Glassb88e81c2019-07-20 12:23:24 -0600568 passes)
569
Simon Glassb88e81c2019-07-20 12:23:24 -0600570 image.BuildImage()
571 if write_map:
572 image.WriteMap()
Simon Glassb1cca952020-07-09 18:39:40 -0600573 missing_list = []
574 image.CheckMissing(missing_list)
575 if missing_list:
576 tout.Warning("Image '%s' is missing external blobs and is non-functional: %s" %
577 (image.name, ' '.join([e.name for e in missing_list])))
Simon Glassb2381432020-09-06 10:39:09 -0600578 _ShowHelpForMissingBlobs(missing_list)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100579 faked_list = []
580 image.CheckFakedBlobs(faked_list)
581 if faked_list:
Simon Glass790ba9f2022-01-12 13:10:36 -0700582 tout.Warning(
Simon Glass2cc8c1f2022-01-09 20:13:45 -0700583 "Image '%s' has faked external blobs and is non-functional: %s" %
584 (image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
585 for e in faked_list])))
Heiko Thierya89c8f22022-01-06 11:49:41 +0100586 return bool(missing_list) or bool(faked_list)
Simon Glassb88e81c2019-07-20 12:23:24 -0600587
588
Simon Glass53cd5d92019-07-08 14:25:29 -0600589def Binman(args):
Simon Glassbf7fd502016-11-25 20:15:51 -0700590 """The main control code for binman
591
592 This assumes that help and test options have already been dealt with. It
593 deals with the core task of building images.
594
595 Args:
Simon Glass53cd5d92019-07-08 14:25:29 -0600596 args: Command line arguments Namespace object
Simon Glassbf7fd502016-11-25 20:15:51 -0700597 """
Simon Glass8dbb7442019-08-24 07:22:44 -0600598 global Image
599 global state
600
Simon Glass53cd5d92019-07-08 14:25:29 -0600601 if args.full_help:
Paul Barker5fe50f92021-09-08 12:38:01 +0100602 tools.PrintFullHelp(
603 os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'README.rst')
604 )
Simon Glassbf7fd502016-11-25 20:15:51 -0700605 return 0
606
Simon Glass8dbb7442019-08-24 07:22:44 -0600607 # Put these here so that we can import this module without libfdt
Simon Glass07237982020-08-05 13:27:47 -0600608 from binman.image import Image
Simon Glass16287932020-04-17 18:09:03 -0600609 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600610
Simon Glass386c63c2022-01-09 20:13:50 -0700611 if args.cmd in ['ls', 'extract', 'replace', 'tool']:
Simon Glass96b6c502019-07-20 12:23:53 -0600612 try:
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600613 tout.Init(args.verbosity)
Simon Glass96b6c502019-07-20 12:23:53 -0600614 tools.PrepareOutputDir(None)
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600615 if args.cmd == 'ls':
616 ListEntries(args.image, args.paths)
Simon Glass61f564d2019-07-08 14:25:48 -0600617
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600618 if args.cmd == 'extract':
619 ExtractEntries(args.image, args.filename, args.outdir, args.paths,
Simon Glass943bf782021-11-23 21:09:50 -0700620 not args.uncompressed, args.format)
Simon Glass71ce0ba2019-07-08 14:25:52 -0600621
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600622 if args.cmd == 'replace':
623 ReplaceEntries(args.image, args.filename, args.indir, args.paths,
624 do_compress=not args.compressed,
625 allow_resize=not args.fix_size, write_map=args.map)
Simon Glass386c63c2022-01-09 20:13:50 -0700626
627 if args.cmd == 'tool':
628 tools.SetToolPaths(args.toolpath)
629 if args.list:
630 bintool.Bintool.list_all()
631 elif args.fetch:
632 if not args.bintools:
633 raise ValueError(
634 "Please specify bintools to fetch or 'all' or 'missing'")
635 bintool.Bintool.fetch_tools(bintool.FETCH_ANY,
636 args.bintools)
637 else:
638 raise ValueError("Invalid arguments to 'tool' subcommand")
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600639 except:
640 raise
Simon Glassa6cb9952019-07-20 12:24:15 -0600641 finally:
642 tools.FinaliseOutputDir()
643 return 0
644
Simon Glass0427bed2021-11-03 21:09:18 -0600645 elf_params = None
646 if args.update_fdt_in_elf:
647 elf_params = args.update_fdt_in_elf.split(',')
648 if len(elf_params) != 4:
649 raise ValueError('Invalid args %s to --update-fdt-in-elf: expected infile,outfile,begin_sym,end_sym' %
650 elf_params)
651
Simon Glassbf7fd502016-11-25 20:15:51 -0700652 # Try to figure out which device tree contains our image description
Simon Glass53cd5d92019-07-08 14:25:29 -0600653 if args.dt:
654 dtb_fname = args.dt
Simon Glassbf7fd502016-11-25 20:15:51 -0700655 else:
Simon Glass53cd5d92019-07-08 14:25:29 -0600656 board = args.board
Simon Glassbf7fd502016-11-25 20:15:51 -0700657 if not board:
658 raise ValueError('Must provide a board to process (use -b <board>)')
Simon Glass53cd5d92019-07-08 14:25:29 -0600659 board_pathname = os.path.join(args.build_dir, board)
Simon Glassbf7fd502016-11-25 20:15:51 -0700660 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
Simon Glass53cd5d92019-07-08 14:25:29 -0600661 if not args.indir:
662 args.indir = ['.']
663 args.indir.append(board_pathname)
Simon Glassbf7fd502016-11-25 20:15:51 -0700664
665 try:
Simon Glass53cd5d92019-07-08 14:25:29 -0600666 tout.Init(args.verbosity)
667 elf.debug = args.debug
668 cbfs_util.VERBOSE = args.verbosity > 2
669 state.use_fake_dtb = args.fake_dtb
Simon Glass0b6023e2021-03-18 20:25:06 +1300670
671 # Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc.
672 # When running tests this can be disabled using this flag. When not
673 # updating the FDT in image, it is not needed by binman, but we use it
674 # for consistency, so that the images look the same to U-Boot at
675 # runtime.
676 use_expanded = not args.no_expanded
Simon Glassbf7fd502016-11-25 20:15:51 -0700677 try:
Simon Glass53cd5d92019-07-08 14:25:29 -0600678 tools.SetInputDirs(args.indir)
679 tools.PrepareOutputDir(args.outdir, args.preserve)
680 tools.SetToolPaths(args.toolpath)
681 state.SetEntryArgs(args.entry_arg)
Simon Glassc69d19c2021-07-06 10:36:37 -0600682 state.SetThreads(args.threads)
Simon Glassecab8972018-07-06 10:27:40 -0600683
Simon Glassa8573c42019-07-20 12:23:27 -0600684 images = PrepareImagesAndDtbs(dtb_fname, args.image,
Simon Glass0b6023e2021-03-18 20:25:06 +1300685 args.update_fdt, use_expanded)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100686
Simon Glassc69d19c2021-07-06 10:36:37 -0600687 if args.test_section_timeout:
688 # Set the first image to timeout, used in testThreadTimeout()
689 images[list(images.keys())[0]].test_section_timeout = True
Heiko Thierya89c8f22022-01-06 11:49:41 +0100690 invalid = False
Simon Glassbf7fd502016-11-25 20:15:51 -0700691 for image in images.values():
Heiko Thierya89c8f22022-01-06 11:49:41 +0100692 invalid |= ProcessImage(image, args.update_fdt, args.map,
693 allow_missing=args.allow_missing,
694 allow_fake_blobs=args.fake_ext_blobs)
Simon Glass2a72cc72018-09-14 04:57:20 -0600695
696 # Write the updated FDTs to our output files
Simon Glass4bdd1152019-07-20 12:23:29 -0600697 for dtb_item in state.GetAllFdts():
Simon Glass2a72cc72018-09-14 04:57:20 -0600698 tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
699
Simon Glass0427bed2021-11-03 21:09:18 -0600700 if elf_params:
701 data = state.GetFdtForEtype('u-boot-dtb').GetContents()
702 elf.UpdateFile(*elf_params, data)
703
Heiko Thierya89c8f22022-01-06 11:49:41 +0100704 if invalid:
Simon Glassb2381432020-09-06 10:39:09 -0600705 tout.Warning("\nSome images are invalid")
Simon Glass03ebc202021-07-06 10:36:41 -0600706
707 # Use this to debug the time take to pack the image
708 #state.TimingShow()
Simon Glassbf7fd502016-11-25 20:15:51 -0700709 finally:
710 tools.FinaliseOutputDir()
711 finally:
712 tout.Uninit()
713
714 return 0