blob: a179f78129884e60e0c1651c1d413e719b0da29f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass2574ef62016-11-25 20:15:51 -07002# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
Simon Glass2574ef62016-11-25 20:15:51 -07005# Creates binary images from input files controlled by a description
6#
7
8from collections import OrderedDict
Simon Glass220ff5f2020-08-05 13:27:46 -06009import glob
Simon Glass2574ef62016-11-25 20:15:51 -070010import os
Simon Glassc1dc2f82020-08-29 11:36:14 -060011import pkg_resources
Simon Glassa820af72020-09-06 10:39:09 -060012import re
Simon Glassc1dc2f82020-08-29 11:36:14 -060013
Simon Glass2574ef62016-11-25 20:15:51 -070014import sys
Simon Glassa997ea52020-04-17 18:09:04 -060015from patman import tools
Simon Glass2574ef62016-11-25 20:15:51 -070016
Simon Glass4eae9252022-01-09 20:13:50 -070017from binman import bintool
Simon Glassc585dd42020-04-17 18:09:03 -060018from binman import cbfs_util
19from binman import elf
Simon Glassa997ea52020-04-17 18:09:04 -060020from patman import command
21from patman import tout
Simon Glass2574ef62016-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 Glassa820af72020-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 Glass55ab0b62021-03-18 20:25:06 +130032def _ReadImageDesc(binman_node, use_expanded):
Simon Glass2574ef62016-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 Glass55ab0b62021-03-18 20:25:06 +130040 use_expanded: True if the FDT will be updated with the entry information
Simon Glass2574ef62016-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 Glass55ab0b62021-03-18 20:25:06 +130047 images[node.name] = Image(node.name, node,
48 use_expanded=use_expanded)
Simon Glass2574ef62016-11-25 20:15:51 -070049 else:
Simon Glass55ab0b62021-03-18 20:25:06 +130050 images['image'] = Image('image', binman_node, use_expanded=use_expanded)
Simon Glass2574ef62016-11-25 20:15:51 -070051 return images
52
Simon Glass22c92ca2017-05-27 07:38:29 -060053def _FindBinmanNode(dtb):
Simon Glass2574ef62016-11-25 20:15:51 -070054 """Find the 'binman' node in the device tree
55
56 Args:
Simon Glass22c92ca2017-05-27 07:38:29 -060057 dtb: Fdt object to scan
Simon Glass2574ef62016-11-25 20:15:51 -070058 Returns:
59 Node object of /binman node, or None if not found
60 """
Simon Glass22c92ca2017-05-27 07:38:29 -060061 for node in dtb.GetRoot().subnodes:
Simon Glass2574ef62016-11-25 20:15:51 -070062 if node.name == 'binman':
63 return node
64 return None
65
Simon Glassa820af72020-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):
Simon Glass011f1b32022-01-29 14:14:15 -0700102 tout.warning('\n%s:' % path)
Simon Glassa820af72020-09-06 10:39:09 -0600103 for line in text.splitlines():
Simon Glass011f1b32022-01-29 14:14:15 -0700104 tout.warning(' %s' % line)
Simon Glassa820af72020-09-06 10:39:09 -0600105
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 Glass220ff5f2020-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 Glassc1dc2f82020-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 Glass220ff5f2020-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 Glass29aa7362018-09-14 04:57:19 -0600138def WriteEntryDocs(modules, test_missing=None):
139 """Write out documentation for all entries
Simon Glass92307732018-07-06 10:27:40 -0600140
141 Args:
Simon Glass29aa7362018-09-14 04:57:19 -0600142 modules: List of Module objects to get docs for
Simon Glass620c4462022-01-09 20:14:11 -0700143 test_missing: Used for testing only, to force an entry's documentation
Simon Glass29aa7362018-09-14 04:57:19 -0600144 to show as missing even if it is present. Should be set to None in
145 normal use.
Simon Glass92307732018-07-06 10:27:40 -0600146 """
Simon Glassc585dd42020-04-17 18:09:03 -0600147 from binman.entry import Entry
Simon Glass969616c2018-07-17 13:25:36 -0600148 Entry.WriteDocs(modules, test_missing)
149
Simon Glassb2fd11d2019-07-08 14:25:48 -0600150
Simon Glass620c4462022-01-09 20:14:11 -0700151def write_bintool_docs(modules, test_missing=None):
152 """Write out documentation for all bintools
153
154 Args:
155 modules: List of Module objects to get docs for
156 test_missing: Used for testing only, to force an entry's documentation
157 to show as missing even if it is present. Should be set to None in
158 normal use.
159 """
160 bintool.Bintool.WriteDocs(modules, test_missing)
161
162
Simon Glassb2fd11d2019-07-08 14:25:48 -0600163def ListEntries(image_fname, entry_paths):
164 """List the entries in an image
165
166 This decodes the supplied image and displays a table of entries from that
167 image, preceded by a header.
168
169 Args:
170 image_fname: Image filename to process
171 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
172 'section/u-boot'])
173 """
174 image = Image.FromFile(image_fname)
175
176 entries, lines, widths = image.GetListEntries(entry_paths)
177
178 num_columns = len(widths)
179 for linenum, line in enumerate(lines):
180 if linenum == 1:
181 # Print header line
182 print('-' * (sum(widths) + num_columns * 2))
183 out = ''
184 for i, item in enumerate(line):
185 width = -widths[i]
186 if item.startswith('>'):
187 width = -width
188 item = item[1:]
189 txt = '%*s ' % (width, item)
190 out += txt
191 print(out.rstrip())
192
Simon Glass4c613bf2019-07-08 14:25:50 -0600193
194def ReadEntry(image_fname, entry_path, decomp=True):
195 """Extract an entry from an image
196
197 This extracts the data from a particular entry in an image
198
199 Args:
200 image_fname: Image filename to process
201 entry_path: Path to entry to extract
202 decomp: True to return uncompressed data, if the data is compress
203 False to return the raw data
204
205 Returns:
206 data extracted from the entry
207 """
Simon Glassb9ba4e02019-08-24 07:22:44 -0600208 global Image
Simon Glass90cd6f02020-08-05 13:27:47 -0600209 from binman.image import Image
Simon Glassb9ba4e02019-08-24 07:22:44 -0600210
Simon Glass4c613bf2019-07-08 14:25:50 -0600211 image = Image.FromFile(image_fname)
212 entry = image.FindEntryPath(entry_path)
213 return entry.ReadData(decomp)
214
215
Simon Glass637958f2021-11-23 21:09:50 -0700216def ShowAltFormats(image):
217 """Show alternative formats available for entries in the image
218
219 This shows a list of formats available.
220
221 Args:
222 image (Image): Image to check
223 """
224 alt_formats = {}
225 image.CheckAltFormats(alt_formats)
226 print('%-10s %-20s %s' % ('Flag (-F)', 'Entry type', 'Description'))
227 for name, val in alt_formats.items():
228 entry, helptext = val
229 print('%-10s %-20s %s' % (name, entry.etype, helptext))
230
231
Simon Glass980a2842019-07-08 14:25:52 -0600232def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
Simon Glass637958f2021-11-23 21:09:50 -0700233 decomp=True, alt_format=None):
Simon Glass980a2842019-07-08 14:25:52 -0600234 """Extract the data from one or more entries and write it to files
235
236 Args:
237 image_fname: Image filename to process
238 output_fname: Single output filename to use if extracting one file, None
239 otherwise
240 outdir: Output directory to use (for any number of files), else None
241 entry_paths: List of entry paths to extract
Simon Glassd48f94e2019-07-20 12:24:12 -0600242 decomp: True to decompress the entry data
Simon Glass980a2842019-07-08 14:25:52 -0600243
244 Returns:
245 List of EntryInfo records that were written
246 """
247 image = Image.FromFile(image_fname)
248
Simon Glass637958f2021-11-23 21:09:50 -0700249 if alt_format == 'list':
250 ShowAltFormats(image)
251 return
252
Simon Glass980a2842019-07-08 14:25:52 -0600253 # Output an entry to a single file, as a special case
254 if output_fname:
255 if not entry_paths:
Simon Glassa772d3f2019-07-20 12:24:14 -0600256 raise ValueError('Must specify an entry path to write with -f')
Simon Glass980a2842019-07-08 14:25:52 -0600257 if len(entry_paths) != 1:
Simon Glassa772d3f2019-07-20 12:24:14 -0600258 raise ValueError('Must specify exactly one entry path to write with -f')
Simon Glass980a2842019-07-08 14:25:52 -0600259 entry = image.FindEntryPath(entry_paths[0])
Simon Glass637958f2021-11-23 21:09:50 -0700260 data = entry.ReadData(decomp, alt_format)
Simon Glass80025522022-01-29 14:14:04 -0700261 tools.write_file(output_fname, data)
Simon Glass011f1b32022-01-29 14:14:15 -0700262 tout.notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname))
Simon Glass980a2842019-07-08 14:25:52 -0600263 return
264
265 # Otherwise we will output to a path given by the entry path of each entry.
266 # This means that entries will appear in subdirectories if they are part of
267 # a sub-section.
268 einfos = image.GetListEntries(entry_paths)[0]
Simon Glass011f1b32022-01-29 14:14:15 -0700269 tout.notice('%d entries match and will be written' % len(einfos))
Simon Glass980a2842019-07-08 14:25:52 -0600270 for einfo in einfos:
271 entry = einfo.entry
Simon Glass637958f2021-11-23 21:09:50 -0700272 data = entry.ReadData(decomp, alt_format)
Simon Glass980a2842019-07-08 14:25:52 -0600273 path = entry.GetPath()[1:]
274 fname = os.path.join(outdir, path)
275
276 # If this entry has children, create a directory for it and put its
277 # data in a file called 'root' in that directory
278 if entry.GetEntries():
Simon Glass4ef93d92021-03-18 20:24:51 +1300279 if fname and not os.path.exists(fname):
Simon Glass980a2842019-07-08 14:25:52 -0600280 os.makedirs(fname)
281 fname = os.path.join(fname, 'root')
Simon Glass011f1b32022-01-29 14:14:15 -0700282 tout.notice("Write entry '%s' size %x to '%s'" %
Simon Glass08349452021-01-06 21:35:13 -0700283 (entry.GetPath(), len(data), fname))
Simon Glass80025522022-01-29 14:14:04 -0700284 tools.write_file(fname, data)
Simon Glass980a2842019-07-08 14:25:52 -0600285 return einfos
286
287
Simon Glass274bd0e2019-07-20 12:24:13 -0600288def BeforeReplace(image, allow_resize):
289 """Handle getting an image ready for replacing entries in it
290
291 Args:
292 image: Image to prepare
293 """
294 state.PrepareFromLoadedData(image)
295 image.LoadData()
296
297 # If repacking, drop the old offset/size values except for the original
298 # ones, so we are only left with the constraints.
299 if allow_resize:
300 image.ResetForPack()
301
302
303def ReplaceOneEntry(image, entry, data, do_compress, allow_resize):
304 """Handle replacing a single entry an an image
305
306 Args:
307 image: Image to update
308 entry: Entry to write
309 data: Data to replace with
310 do_compress: True to compress the data if needed, False if data is
311 already compressed so should be used as is
312 allow_resize: True to allow entries to change size (this does a re-pack
313 of the entries), False to raise an exception
314 """
315 if not entry.WriteData(data, do_compress):
316 if not image.allow_repack:
317 entry.Raise('Entry data size does not match, but allow-repack is not present for this image')
318 if not allow_resize:
319 entry.Raise('Entry data size does not match, but resize is disabled')
320
321
322def AfterReplace(image, allow_resize, write_map):
323 """Handle write out an image after replacing entries in it
324
325 Args:
326 image: Image to write
327 allow_resize: True to allow entries to change size (this does a re-pack
328 of the entries), False to raise an exception
329 write_map: True to write a map file
330 """
Simon Glass011f1b32022-01-29 14:14:15 -0700331 tout.info('Processing image')
Simon Glass274bd0e2019-07-20 12:24:13 -0600332 ProcessImage(image, update_fdt=True, write_map=write_map,
333 get_contents=False, allow_resize=allow_resize)
334
335
336def WriteEntryToImage(image, entry, data, do_compress=True, allow_resize=True,
337 write_map=False):
338 BeforeReplace(image, allow_resize)
Simon Glass011f1b32022-01-29 14:14:15 -0700339 tout.info('Writing data to %s' % entry.GetPath())
Simon Glass274bd0e2019-07-20 12:24:13 -0600340 ReplaceOneEntry(image, entry, data, do_compress, allow_resize)
341 AfterReplace(image, allow_resize=allow_resize, write_map=write_map)
342
343
Simon Glassd48f94e2019-07-20 12:24:12 -0600344def WriteEntry(image_fname, entry_path, data, do_compress=True,
345 allow_resize=True, write_map=False):
Simon Glass3971c952019-07-20 12:24:11 -0600346 """Replace an entry in an image
347
348 This replaces the data in a particular entry in an image. This size of the
349 new data must match the size of the old data unless allow_resize is True.
350
351 Args:
352 image_fname: Image filename to process
353 entry_path: Path to entry to extract
354 data: Data to replace with
Simon Glassd48f94e2019-07-20 12:24:12 -0600355 do_compress: True to compress the data if needed, False if data is
Simon Glass3971c952019-07-20 12:24:11 -0600356 already compressed so should be used as is
357 allow_resize: True to allow entries to change size (this does a re-pack
358 of the entries), False to raise an exception
Simon Glassd48f94e2019-07-20 12:24:12 -0600359 write_map: True to write a map file
Simon Glass3971c952019-07-20 12:24:11 -0600360
361 Returns:
362 Image object that was updated
363 """
Simon Glass011f1b32022-01-29 14:14:15 -0700364 tout.info("Write entry '%s', file '%s'" % (entry_path, image_fname))
Simon Glass3971c952019-07-20 12:24:11 -0600365 image = Image.FromFile(image_fname)
366 entry = image.FindEntryPath(entry_path)
Simon Glass274bd0e2019-07-20 12:24:13 -0600367 WriteEntryToImage(image, entry, data, do_compress=do_compress,
368 allow_resize=allow_resize, write_map=write_map)
Simon Glass3971c952019-07-20 12:24:11 -0600369
Simon Glass3971c952019-07-20 12:24:11 -0600370 return image
371
Simon Glass30033c22019-07-20 12:24:15 -0600372
373def ReplaceEntries(image_fname, input_fname, indir, entry_paths,
374 do_compress=True, allow_resize=True, write_map=False):
375 """Replace the data from one or more entries from input files
376
377 Args:
378 image_fname: Image filename to process
Jan Kiszka8ea44432021-11-11 08:13:30 +0100379 input_fname: Single input filename to use if replacing one file, None
Simon Glass30033c22019-07-20 12:24:15 -0600380 otherwise
381 indir: Input directory to use (for any number of files), else None
Jan Kiszka8ea44432021-11-11 08:13:30 +0100382 entry_paths: List of entry paths to replace
Simon Glass30033c22019-07-20 12:24:15 -0600383 do_compress: True if the input data is uncompressed and may need to be
384 compressed if the entry requires it, False if the data is already
385 compressed.
386 write_map: True to write a map file
387
388 Returns:
389 List of EntryInfo records that were written
390 """
Jan Kiszkaafc8f292021-11-11 08:14:18 +0100391 image_fname = os.path.abspath(image_fname)
Simon Glass30033c22019-07-20 12:24:15 -0600392 image = Image.FromFile(image_fname)
393
394 # Replace an entry from a single file, as a special case
395 if input_fname:
396 if not entry_paths:
397 raise ValueError('Must specify an entry path to read with -f')
398 if len(entry_paths) != 1:
399 raise ValueError('Must specify exactly one entry path to write with -f')
400 entry = image.FindEntryPath(entry_paths[0])
Simon Glass80025522022-01-29 14:14:04 -0700401 data = tools.read_file(input_fname)
Simon Glass011f1b32022-01-29 14:14:15 -0700402 tout.notice("Read %#x bytes from file '%s'" % (len(data), input_fname))
Simon Glass30033c22019-07-20 12:24:15 -0600403 WriteEntryToImage(image, entry, data, do_compress=do_compress,
404 allow_resize=allow_resize, write_map=write_map)
405 return
406
407 # Otherwise we will input from a path given by the entry path of each entry.
408 # This means that files must appear in subdirectories if they are part of
409 # a sub-section.
410 einfos = image.GetListEntries(entry_paths)[0]
Simon Glass011f1b32022-01-29 14:14:15 -0700411 tout.notice("Replacing %d matching entries in image '%s'" %
Simon Glass30033c22019-07-20 12:24:15 -0600412 (len(einfos), image_fname))
413
414 BeforeReplace(image, allow_resize)
415
416 for einfo in einfos:
417 entry = einfo.entry
418 if entry.GetEntries():
Simon Glass011f1b32022-01-29 14:14:15 -0700419 tout.info("Skipping section entry '%s'" % entry.GetPath())
Simon Glass30033c22019-07-20 12:24:15 -0600420 continue
421
422 path = entry.GetPath()[1:]
423 fname = os.path.join(indir, path)
424
425 if os.path.exists(fname):
Simon Glass011f1b32022-01-29 14:14:15 -0700426 tout.notice("Write entry '%s' from file '%s'" %
Simon Glass30033c22019-07-20 12:24:15 -0600427 (entry.GetPath(), fname))
Simon Glass80025522022-01-29 14:14:04 -0700428 data = tools.read_file(fname)
Simon Glass30033c22019-07-20 12:24:15 -0600429 ReplaceOneEntry(image, entry, data, do_compress, allow_resize)
430 else:
Simon Glass011f1b32022-01-29 14:14:15 -0700431 tout.warning("Skipping entry '%s' from missing file '%s'" %
Simon Glass30033c22019-07-20 12:24:15 -0600432 (entry.GetPath(), fname))
433
434 AfterReplace(image, allow_resize=allow_resize, write_map=write_map)
435 return image
436
437
Simon Glass55ab0b62021-03-18 20:25:06 +1300438def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded):
Simon Glassd3151ff2019-07-20 12:23:27 -0600439 """Prepare the images to be processed and select the device tree
440
441 This function:
442 - reads in the device tree
443 - finds and scans the binman node to create all entries
444 - selects which images to build
445 - Updates the device tress with placeholder properties for offset,
446 image-pos, etc.
447
448 Args:
449 dtb_fname: Filename of the device tree file to use (.dts or .dtb)
450 selected_images: List of images to output, or None for all
451 update_fdt: True to update the FDT wth entry offsets, etc.
Simon Glass55ab0b62021-03-18 20:25:06 +1300452 use_expanded: True to use expanded versions of entries, if available.
453 So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This
454 is needed if update_fdt is True (although tests may disable it)
Simon Glass31ee50f2020-09-01 05:13:55 -0600455
456 Returns:
457 OrderedDict of images:
458 key: Image name (str)
459 value: Image object
Simon Glassd3151ff2019-07-20 12:23:27 -0600460 """
461 # Import these here in case libfdt.py is not available, in which case
462 # the above help option still works.
Simon Glassc585dd42020-04-17 18:09:03 -0600463 from dtoc import fdt
464 from dtoc import fdt_util
Simon Glassd3151ff2019-07-20 12:23:27 -0600465 global images
466
467 # Get the device tree ready by compiling it and copying the compiled
468 # output into a file in our output directly. Then scan it for use
469 # in binman.
470 dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
Simon Glass80025522022-01-29 14:14:04 -0700471 fname = tools.get_output_filename('u-boot.dtb.out')
472 tools.write_file(fname, tools.read_file(dtb_fname))
Simon Glassd3151ff2019-07-20 12:23:27 -0600473 dtb = fdt.FdtScan(fname)
474
475 node = _FindBinmanNode(dtb)
476 if not node:
477 raise ValueError("Device tree '%s' does not have a 'binman' "
478 "node" % dtb_fname)
479
Simon Glass55ab0b62021-03-18 20:25:06 +1300480 images = _ReadImageDesc(node, use_expanded)
Simon Glassd3151ff2019-07-20 12:23:27 -0600481
482 if select_images:
483 skip = []
484 new_images = OrderedDict()
485 for name, image in images.items():
486 if name in select_images:
487 new_images[name] = image
488 else:
489 skip.append(name)
490 images = new_images
Simon Glass011f1b32022-01-29 14:14:15 -0700491 tout.notice('Skipping images: %s' % ', '.join(skip))
Simon Glassd3151ff2019-07-20 12:23:27 -0600492
493 state.Prepare(images, dtb)
494
495 # Prepare the device tree by making sure that any missing
496 # properties are added (e.g. 'pos' and 'size'). The values of these
497 # may not be correct yet, but we add placeholders so that the
498 # size of the device tree is correct. Later, in
499 # SetCalculatedProperties() we will insert the correct values
500 # without changing the device-tree size, thus ensuring that our
501 # entry offsets remain the same.
502 for image in images.values():
Simon Glass4eae9252022-01-09 20:13:50 -0700503 image.CollectBintools()
Simon Glassd3151ff2019-07-20 12:23:27 -0600504 image.ExpandEntries()
505 if update_fdt:
Simon Glassacd6c6e2020-10-26 17:40:17 -0600506 image.AddMissingProperties(True)
Simon Glassd3151ff2019-07-20 12:23:27 -0600507 image.ProcessFdt(dtb)
508
Simon Glass5a300602019-07-20 12:23:29 -0600509 for dtb_item in state.GetAllFdts():
Simon Glassd3151ff2019-07-20 12:23:27 -0600510 dtb_item.Sync(auto_resize=True)
511 dtb_item.Pack()
512 dtb_item.Flush()
513 return images
514
515
Simon Glassf8a54bc2019-07-20 12:23:56 -0600516def ProcessImage(image, update_fdt, write_map, get_contents=True,
Heiko Thiery6d451362022-01-06 11:49:41 +0100517 allow_resize=True, allow_missing=False,
518 allow_fake_blobs=False):
Simon Glassb766c5e52019-07-20 12:23:24 -0600519 """Perform all steps for this image, including checking and # writing it.
520
521 This means that errors found with a later image will be reported after
522 earlier images are already completed and written, but that does not seem
523 important.
524
525 Args:
526 image: Image to process
527 update_fdt: True to update the FDT wth entry offsets, etc.
528 write_map: True to write a map file
Simon Glass072959a2019-07-20 12:23:50 -0600529 get_contents: True to get the image contents from files, etc., False if
530 the contents is already present
Simon Glassf8a54bc2019-07-20 12:23:56 -0600531 allow_resize: True to allow entries to change size (this does a re-pack
532 of the entries), False to raise an exception
Simon Glass5d94cc62020-07-09 18:39:38 -0600533 allow_missing: Allow blob_ext objects to be missing
Heiko Thiery6d451362022-01-06 11:49:41 +0100534 allow_fake_blobs: Allow blob_ext objects to be faked with dummy files
Simon Glassa003cd32020-07-09 18:39:40 -0600535
536 Returns:
Heiko Thiery6d451362022-01-06 11:49:41 +0100537 True if one or more external blobs are missing or faked,
538 False if all are present
Simon Glassb766c5e52019-07-20 12:23:24 -0600539 """
Simon Glass072959a2019-07-20 12:23:50 -0600540 if get_contents:
Simon Glass5d94cc62020-07-09 18:39:38 -0600541 image.SetAllowMissing(allow_missing)
Heiko Thiery6d451362022-01-06 11:49:41 +0100542 image.SetAllowFakeBlob(allow_fake_blobs)
Simon Glass072959a2019-07-20 12:23:50 -0600543 image.GetEntryContents()
Simon Glassb766c5e52019-07-20 12:23:24 -0600544 image.GetEntryOffsets()
545
546 # We need to pack the entries to figure out where everything
547 # should be placed. This sets the offset/size of each entry.
548 # However, after packing we call ProcessEntryContents() which
549 # may result in an entry changing size. In that case we need to
550 # do another pass. Since the device tree often contains the
551 # final offset/size information we try to make space for this in
552 # AddMissingProperties() above. However, if the device is
553 # compressed we cannot know this compressed size in advance,
554 # since changing an offset from 0x100 to 0x104 (for example) can
555 # alter the compressed size of the device tree. So we need a
556 # third pass for this.
Simon Glass37fdd142019-07-20 12:24:06 -0600557 passes = 5
Simon Glassb766c5e52019-07-20 12:23:24 -0600558 for pack_pass in range(passes):
559 try:
560 image.PackEntries()
Simon Glassb766c5e52019-07-20 12:23:24 -0600561 except Exception as e:
562 if write_map:
563 fname = image.WriteMap()
564 print("Wrote map file '%s' to show errors" % fname)
565 raise
566 image.SetImagePos()
567 if update_fdt:
568 image.SetCalculatedProperties()
Simon Glass5a300602019-07-20 12:23:29 -0600569 for dtb_item in state.GetAllFdts():
Simon Glassb766c5e52019-07-20 12:23:24 -0600570 dtb_item.Sync()
Simon Glassf8a54bc2019-07-20 12:23:56 -0600571 dtb_item.Flush()
Simon Glasse5943412019-08-24 07:23:12 -0600572 image.WriteSymbols()
Simon Glassb766c5e52019-07-20 12:23:24 -0600573 sizes_ok = image.ProcessEntryContents()
574 if sizes_ok:
575 break
576 image.ResetForPack()
Simon Glass011f1b32022-01-29 14:14:15 -0700577 tout.info('Pack completed after %d pass(es)' % (pack_pass + 1))
Simon Glassb766c5e52019-07-20 12:23:24 -0600578 if not sizes_ok:
Simon Glass9d8ee322019-07-20 12:23:58 -0600579 image.Raise('Entries changed size after packing (tried %s passes)' %
Simon Glassb766c5e52019-07-20 12:23:24 -0600580 passes)
581
Simon Glassb766c5e52019-07-20 12:23:24 -0600582 image.BuildImage()
583 if write_map:
584 image.WriteMap()
Simon Glassa003cd32020-07-09 18:39:40 -0600585 missing_list = []
586 image.CheckMissing(missing_list)
587 if missing_list:
Simon Glass011f1b32022-01-29 14:14:15 -0700588 tout.warning("Image '%s' is missing external blobs and is non-functional: %s" %
Simon Glassa003cd32020-07-09 18:39:40 -0600589 (image.name, ' '.join([e.name for e in missing_list])))
Simon Glassa820af72020-09-06 10:39:09 -0600590 _ShowHelpForMissingBlobs(missing_list)
Heiko Thiery6d451362022-01-06 11:49:41 +0100591 faked_list = []
592 image.CheckFakedBlobs(faked_list)
593 if faked_list:
Simon Glass011f1b32022-01-29 14:14:15 -0700594 tout.warning(
Simon Glassf9f34032022-01-09 20:13:45 -0700595 "Image '%s' has faked external blobs and is non-functional: %s" %
596 (image.name, ' '.join([os.path.basename(e.GetDefaultFilename())
597 for e in faked_list])))
Simon Glass66152ce2022-01-09 20:14:09 -0700598 missing_bintool_list = []
599 image.check_missing_bintools(missing_bintool_list)
600 if missing_bintool_list:
Simon Glass011f1b32022-01-29 14:14:15 -0700601 tout.warning(
Simon Glass66152ce2022-01-09 20:14:09 -0700602 "Image '%s' has missing bintools and is non-functional: %s" %
603 (image.name, ' '.join([os.path.basename(bintool.name)
604 for bintool in missing_bintool_list])))
605 return any([missing_list, faked_list, missing_bintool_list])
Simon Glassb766c5e52019-07-20 12:23:24 -0600606
607
Simon Glassf46732a2019-07-08 14:25:29 -0600608def Binman(args):
Simon Glass2574ef62016-11-25 20:15:51 -0700609 """The main control code for binman
610
611 This assumes that help and test options have already been dealt with. It
612 deals with the core task of building images.
613
614 Args:
Simon Glassf46732a2019-07-08 14:25:29 -0600615 args: Command line arguments Namespace object
Simon Glass2574ef62016-11-25 20:15:51 -0700616 """
Simon Glassb9ba4e02019-08-24 07:22:44 -0600617 global Image
618 global state
619
Simon Glassf46732a2019-07-08 14:25:29 -0600620 if args.full_help:
Simon Glass80025522022-01-29 14:14:04 -0700621 tools.print_full_help(
Paul Barker25ecd972021-09-08 12:38:01 +0100622 os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'README.rst')
623 )
Simon Glass2574ef62016-11-25 20:15:51 -0700624 return 0
625
Simon Glassb9ba4e02019-08-24 07:22:44 -0600626 # Put these here so that we can import this module without libfdt
Simon Glass90cd6f02020-08-05 13:27:47 -0600627 from binman.image import Image
Simon Glassc585dd42020-04-17 18:09:03 -0600628 from binman import state
Simon Glassb9ba4e02019-08-24 07:22:44 -0600629
Simon Glass4eae9252022-01-09 20:13:50 -0700630 if args.cmd in ['ls', 'extract', 'replace', 'tool']:
Simon Glass9b7f5002019-07-20 12:23:53 -0600631 try:
Simon Glass011f1b32022-01-29 14:14:15 -0700632 tout.init(args.verbosity)
Simon Glass80025522022-01-29 14:14:04 -0700633 tools.prepare_output_dir(None)
Simon Glassdf08cbb2019-09-15 18:10:36 -0600634 if args.cmd == 'ls':
635 ListEntries(args.image, args.paths)
Simon Glassb2fd11d2019-07-08 14:25:48 -0600636
Simon Glassdf08cbb2019-09-15 18:10:36 -0600637 if args.cmd == 'extract':
638 ExtractEntries(args.image, args.filename, args.outdir, args.paths,
Simon Glass637958f2021-11-23 21:09:50 -0700639 not args.uncompressed, args.format)
Simon Glass980a2842019-07-08 14:25:52 -0600640
Simon Glassdf08cbb2019-09-15 18:10:36 -0600641 if args.cmd == 'replace':
642 ReplaceEntries(args.image, args.filename, args.indir, args.paths,
643 do_compress=not args.compressed,
644 allow_resize=not args.fix_size, write_map=args.map)
Simon Glass4eae9252022-01-09 20:13:50 -0700645
646 if args.cmd == 'tool':
Simon Glass80025522022-01-29 14:14:04 -0700647 tools.set_tool_paths(args.toolpath)
Simon Glass4eae9252022-01-09 20:13:50 -0700648 if args.list:
649 bintool.Bintool.list_all()
650 elif args.fetch:
651 if not args.bintools:
652 raise ValueError(
653 "Please specify bintools to fetch or 'all' or 'missing'")
654 bintool.Bintool.fetch_tools(bintool.FETCH_ANY,
655 args.bintools)
656 else:
657 raise ValueError("Invalid arguments to 'tool' subcommand")
Simon Glassdf08cbb2019-09-15 18:10:36 -0600658 except:
659 raise
Simon Glass30033c22019-07-20 12:24:15 -0600660 finally:
Simon Glass80025522022-01-29 14:14:04 -0700661 tools.finalise_output_dir()
Simon Glass30033c22019-07-20 12:24:15 -0600662 return 0
663
Simon Glassadfb8492021-11-03 21:09:18 -0600664 elf_params = None
665 if args.update_fdt_in_elf:
666 elf_params = args.update_fdt_in_elf.split(',')
667 if len(elf_params) != 4:
668 raise ValueError('Invalid args %s to --update-fdt-in-elf: expected infile,outfile,begin_sym,end_sym' %
669 elf_params)
670
Simon Glass2574ef62016-11-25 20:15:51 -0700671 # Try to figure out which device tree contains our image description
Simon Glassf46732a2019-07-08 14:25:29 -0600672 if args.dt:
673 dtb_fname = args.dt
Simon Glass2574ef62016-11-25 20:15:51 -0700674 else:
Simon Glassf46732a2019-07-08 14:25:29 -0600675 board = args.board
Simon Glass2574ef62016-11-25 20:15:51 -0700676 if not board:
677 raise ValueError('Must provide a board to process (use -b <board>)')
Simon Glassf46732a2019-07-08 14:25:29 -0600678 board_pathname = os.path.join(args.build_dir, board)
Simon Glass2574ef62016-11-25 20:15:51 -0700679 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
Simon Glassf46732a2019-07-08 14:25:29 -0600680 if not args.indir:
681 args.indir = ['.']
682 args.indir.append(board_pathname)
Simon Glass2574ef62016-11-25 20:15:51 -0700683
684 try:
Simon Glass011f1b32022-01-29 14:14:15 -0700685 tout.init(args.verbosity)
Simon Glassf46732a2019-07-08 14:25:29 -0600686 elf.debug = args.debug
687 cbfs_util.VERBOSE = args.verbosity > 2
688 state.use_fake_dtb = args.fake_dtb
Simon Glass55ab0b62021-03-18 20:25:06 +1300689
690 # Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc.
691 # When running tests this can be disabled using this flag. When not
692 # updating the FDT in image, it is not needed by binman, but we use it
693 # for consistency, so that the images look the same to U-Boot at
694 # runtime.
695 use_expanded = not args.no_expanded
Simon Glass2574ef62016-11-25 20:15:51 -0700696 try:
Simon Glass80025522022-01-29 14:14:04 -0700697 tools.set_input_dirs(args.indir)
698 tools.prepare_output_dir(args.outdir, args.preserve)
699 tools.set_tool_paths(args.toolpath)
Simon Glassf46732a2019-07-08 14:25:29 -0600700 state.SetEntryArgs(args.entry_arg)
Simon Glass76f496d2021-07-06 10:36:37 -0600701 state.SetThreads(args.threads)
Simon Glass92307732018-07-06 10:27:40 -0600702
Simon Glassd3151ff2019-07-20 12:23:27 -0600703 images = PrepareImagesAndDtbs(dtb_fname, args.image,
Simon Glass55ab0b62021-03-18 20:25:06 +1300704 args.update_fdt, use_expanded)
Heiko Thiery6d451362022-01-06 11:49:41 +0100705
Simon Glass76f496d2021-07-06 10:36:37 -0600706 if args.test_section_timeout:
707 # Set the first image to timeout, used in testThreadTimeout()
708 images[list(images.keys())[0]].test_section_timeout = True
Heiko Thiery6d451362022-01-06 11:49:41 +0100709 invalid = False
Simon Glass66152ce2022-01-09 20:14:09 -0700710 bintool.Bintool.set_missing_list(
711 args.force_missing_bintools.split(',') if
712 args.force_missing_bintools else None)
Simon Glass2574ef62016-11-25 20:15:51 -0700713 for image in images.values():
Heiko Thiery6d451362022-01-06 11:49:41 +0100714 invalid |= ProcessImage(image, args.update_fdt, args.map,
715 allow_missing=args.allow_missing,
716 allow_fake_blobs=args.fake_ext_blobs)
Simon Glassbdb40312018-09-14 04:57:20 -0600717
718 # Write the updated FDTs to our output files
Simon Glass5a300602019-07-20 12:23:29 -0600719 for dtb_item in state.GetAllFdts():
Simon Glass80025522022-01-29 14:14:04 -0700720 tools.write_file(dtb_item._fname, dtb_item.GetContents())
Simon Glassbdb40312018-09-14 04:57:20 -0600721
Simon Glassadfb8492021-11-03 21:09:18 -0600722 if elf_params:
723 data = state.GetFdtForEtype('u-boot-dtb').GetContents()
724 elf.UpdateFile(*elf_params, data)
725
Heiko Thiery6d451362022-01-06 11:49:41 +0100726 if invalid:
Simon Glass011f1b32022-01-29 14:14:15 -0700727 tout.warning("\nSome images are invalid")
Simon Glass748a1d42021-07-06 10:36:41 -0600728
729 # Use this to debug the time take to pack the image
730 #state.TimingShow()
Simon Glass2574ef62016-11-25 20:15:51 -0700731 finally:
Simon Glass80025522022-01-29 14:14:04 -0700732 tools.finalise_output_dir()
Simon Glass2574ef62016-11-25 20:15:51 -0700733 finally:
Simon Glass011f1b32022-01-29 14:14:15 -0700734 tout.uninit()
Simon Glass2574ef62016-11-25 20:15:51 -0700735
736 return 0