blob: a179f78129884e60e0c1651c1d413e719b0da29f [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):
Simon Glassf3385a52022-01-29 14:14:15 -0700102 tout.warning('\n%s:' % path)
Simon Glassb2381432020-09-06 10:39:09 -0600103 for line in text.splitlines():
Simon Glassf3385a52022-01-29 14:14:15 -0700104 tout.warning(' %s' % line)
Simon Glassb2381432020-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 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
Simon Glassbc570642022-01-09 20:14:11 -0700143 test_missing: Used for testing only, to force an entry's documentation
Simon Glassc55a50f2018-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 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
Simon Glassbc570642022-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 Glass61f564d2019-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 Glassf667e452019-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 Glass8dbb7442019-08-24 07:22:44 -0600208 global Image
Simon Glass07237982020-08-05 13:27:47 -0600209 from binman.image import Image
Simon Glass8dbb7442019-08-24 07:22:44 -0600210
Simon Glassf667e452019-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 Glass943bf782021-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 Glass71ce0ba2019-07-08 14:25:52 -0600232def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
Simon Glass943bf782021-11-23 21:09:50 -0700233 decomp=True, alt_format=None):
Simon Glass71ce0ba2019-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 Glass3ad804e2019-07-20 12:24:12 -0600242 decomp: True to decompress the entry data
Simon Glass71ce0ba2019-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 Glass943bf782021-11-23 21:09:50 -0700249 if alt_format == 'list':
250 ShowAltFormats(image)
251 return
252
Simon Glass71ce0ba2019-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 Glassbb5edc12019-07-20 12:24:14 -0600256 raise ValueError('Must specify an entry path to write with -f')
Simon Glass71ce0ba2019-07-08 14:25:52 -0600257 if len(entry_paths) != 1:
Simon Glassbb5edc12019-07-20 12:24:14 -0600258 raise ValueError('Must specify exactly one entry path to write with -f')
Simon Glass71ce0ba2019-07-08 14:25:52 -0600259 entry = image.FindEntryPath(entry_paths[0])
Simon Glass943bf782021-11-23 21:09:50 -0700260 data = entry.ReadData(decomp, alt_format)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700261 tools.write_file(output_fname, data)
Simon Glassf3385a52022-01-29 14:14:15 -0700262 tout.notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname))
Simon Glass71ce0ba2019-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 Glassf3385a52022-01-29 14:14:15 -0700269 tout.notice('%d entries match and will be written' % len(einfos))
Simon Glass71ce0ba2019-07-08 14:25:52 -0600270 for einfo in einfos:
271 entry = einfo.entry
Simon Glass943bf782021-11-23 21:09:50 -0700272 data = entry.ReadData(decomp, alt_format)
Simon Glass71ce0ba2019-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 Glass862ddf92021-03-18 20:24:51 +1300279 if fname and not os.path.exists(fname):
Simon Glass71ce0ba2019-07-08 14:25:52 -0600280 os.makedirs(fname)
281 fname = os.path.join(fname, 'root')
Simon Glassf3385a52022-01-29 14:14:15 -0700282 tout.notice("Write entry '%s' size %x to '%s'" %
Simon Glass5b378e42021-01-06 21:35:13 -0700283 (entry.GetPath(), len(data), fname))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700284 tools.write_file(fname, data)
Simon Glass71ce0ba2019-07-08 14:25:52 -0600285 return einfos
286
287
Simon Glassd7fa4e42019-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 Glassf3385a52022-01-29 14:14:15 -0700331 tout.info('Processing image')
Simon Glassd7fa4e42019-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 Glassf3385a52022-01-29 14:14:15 -0700339 tout.info('Writing data to %s' % entry.GetPath())
Simon Glassd7fa4e42019-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 Glass3ad804e2019-07-20 12:24:12 -0600344def WriteEntry(image_fname, entry_path, data, do_compress=True,
345 allow_resize=True, write_map=False):
Simon Glass22a76b72019-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 Glass3ad804e2019-07-20 12:24:12 -0600355 do_compress: True to compress the data if needed, False if data is
Simon Glass22a76b72019-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 Glass3ad804e2019-07-20 12:24:12 -0600359 write_map: True to write a map file
Simon Glass22a76b72019-07-20 12:24:11 -0600360
361 Returns:
362 Image object that was updated
363 """
Simon Glassf3385a52022-01-29 14:14:15 -0700364 tout.info("Write entry '%s', file '%s'" % (entry_path, image_fname))
Simon Glass22a76b72019-07-20 12:24:11 -0600365 image = Image.FromFile(image_fname)
366 entry = image.FindEntryPath(entry_path)
Simon Glassd7fa4e42019-07-20 12:24:13 -0600367 WriteEntryToImage(image, entry, data, do_compress=do_compress,
368 allow_resize=allow_resize, write_map=write_map)
Simon Glass22a76b72019-07-20 12:24:11 -0600369
Simon Glass22a76b72019-07-20 12:24:11 -0600370 return image
371
Simon Glassa6cb9952019-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 Kiszka89cc0522021-11-11 08:13:30 +0100379 input_fname: Single input filename to use if replacing one file, None
Simon Glassa6cb9952019-07-20 12:24:15 -0600380 otherwise
381 indir: Input directory to use (for any number of files), else None
Jan Kiszka89cc0522021-11-11 08:13:30 +0100382 entry_paths: List of entry paths to replace
Simon Glassa6cb9952019-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 Kiszkac700f102021-11-11 08:14:18 +0100391 image_fname = os.path.abspath(image_fname)
Simon Glassa6cb9952019-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 Glassc1aa66e2022-01-29 14:14:04 -0700401 data = tools.read_file(input_fname)
Simon Glassf3385a52022-01-29 14:14:15 -0700402 tout.notice("Read %#x bytes from file '%s'" % (len(data), input_fname))
Simon Glassa6cb9952019-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 Glassf3385a52022-01-29 14:14:15 -0700411 tout.notice("Replacing %d matching entries in image '%s'" %
Simon Glassa6cb9952019-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 Glassf3385a52022-01-29 14:14:15 -0700419 tout.info("Skipping section entry '%s'" % entry.GetPath())
Simon Glassa6cb9952019-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 Glassf3385a52022-01-29 14:14:15 -0700426 tout.notice("Write entry '%s' from file '%s'" %
Simon Glassa6cb9952019-07-20 12:24:15 -0600427 (entry.GetPath(), fname))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700428 data = tools.read_file(fname)
Simon Glassa6cb9952019-07-20 12:24:15 -0600429 ReplaceOneEntry(image, entry, data, do_compress, allow_resize)
430 else:
Simon Glassf3385a52022-01-29 14:14:15 -0700431 tout.warning("Skipping entry '%s' from missing file '%s'" %
Simon Glassa6cb9952019-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 Glass0b6023e2021-03-18 20:25:06 +1300438def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded):
Simon Glassa8573c42019-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 Glass0b6023e2021-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 Glasse9d336d2020-09-01 05:13:55 -0600455
456 Returns:
457 OrderedDict of images:
458 key: Image name (str)
459 value: Image object
Simon Glassa8573c42019-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 Glass16287932020-04-17 18:09:03 -0600463 from dtoc import fdt
464 from dtoc import fdt_util
Simon Glassa8573c42019-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 Glassc1aa66e2022-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 Glassa8573c42019-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 Glass0b6023e2021-03-18 20:25:06 +1300480 images = _ReadImageDesc(node, use_expanded)
Simon Glassa8573c42019-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 Glassf3385a52022-01-29 14:14:15 -0700491 tout.notice('Skipping images: %s' % ', '.join(skip))
Simon Glassa8573c42019-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 Glass386c63c2022-01-09 20:13:50 -0700503 image.CollectBintools()
Simon Glassa8573c42019-07-20 12:23:27 -0600504 image.ExpandEntries()
505 if update_fdt:
Simon Glassa9fad072020-10-26 17:40:17 -0600506 image.AddMissingProperties(True)
Simon Glassa8573c42019-07-20 12:23:27 -0600507 image.ProcessFdt(dtb)
508
Simon Glass4bdd1152019-07-20 12:23:29 -0600509 for dtb_item in state.GetAllFdts():
Simon Glassa8573c42019-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 Glass51014aa2019-07-20 12:23:56 -0600516def ProcessImage(image, update_fdt, write_map, get_contents=True,
Heiko Thierya89c8f22022-01-06 11:49:41 +0100517 allow_resize=True, allow_missing=False,
518 allow_fake_blobs=False):
Simon Glassb88e81c2019-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 Glass10f9d002019-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 Glass51014aa2019-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 Glass4f9f1052020-07-09 18:39:38 -0600533 allow_missing: Allow blob_ext objects to be missing
Heiko Thierya89c8f22022-01-06 11:49:41 +0100534 allow_fake_blobs: Allow blob_ext objects to be faked with dummy files
Simon Glassb1cca952020-07-09 18:39:40 -0600535
536 Returns:
Heiko Thierya89c8f22022-01-06 11:49:41 +0100537 True if one or more external blobs are missing or faked,
538 False if all are present
Simon Glassb88e81c2019-07-20 12:23:24 -0600539 """
Simon Glass10f9d002019-07-20 12:23:50 -0600540 if get_contents:
Simon Glass4f9f1052020-07-09 18:39:38 -0600541 image.SetAllowMissing(allow_missing)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100542 image.SetAllowFakeBlob(allow_fake_blobs)
Simon Glass10f9d002019-07-20 12:23:50 -0600543 image.GetEntryContents()
Simon Glassb88e81c2019-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 Glasseb0f4a42019-07-20 12:24:06 -0600557 passes = 5
Simon Glassb88e81c2019-07-20 12:23:24 -0600558 for pack_pass in range(passes):
559 try:
560 image.PackEntries()
Simon Glassb88e81c2019-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 Glass4bdd1152019-07-20 12:23:29 -0600569 for dtb_item in state.GetAllFdts():
Simon Glassb88e81c2019-07-20 12:23:24 -0600570 dtb_item.Sync()
Simon Glass51014aa2019-07-20 12:23:56 -0600571 dtb_item.Flush()
Simon Glass261cbe02019-08-24 07:23:12 -0600572 image.WriteSymbols()
Simon Glassb88e81c2019-07-20 12:23:24 -0600573 sizes_ok = image.ProcessEntryContents()
574 if sizes_ok:
575 break
576 image.ResetForPack()
Simon Glassf3385a52022-01-29 14:14:15 -0700577 tout.info('Pack completed after %d pass(es)' % (pack_pass + 1))
Simon Glassb88e81c2019-07-20 12:23:24 -0600578 if not sizes_ok:
Simon Glass61ec04f2019-07-20 12:23:58 -0600579 image.Raise('Entries changed size after packing (tried %s passes)' %
Simon Glassb88e81c2019-07-20 12:23:24 -0600580 passes)
581
Simon Glassb88e81c2019-07-20 12:23:24 -0600582 image.BuildImage()
583 if write_map:
584 image.WriteMap()
Simon Glassb1cca952020-07-09 18:39:40 -0600585 missing_list = []
586 image.CheckMissing(missing_list)
587 if missing_list:
Simon Glassf3385a52022-01-29 14:14:15 -0700588 tout.warning("Image '%s' is missing external blobs and is non-functional: %s" %
Simon Glassb1cca952020-07-09 18:39:40 -0600589 (image.name, ' '.join([e.name for e in missing_list])))
Simon Glassb2381432020-09-06 10:39:09 -0600590 _ShowHelpForMissingBlobs(missing_list)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100591 faked_list = []
592 image.CheckFakedBlobs(faked_list)
593 if faked_list:
Simon Glassf3385a52022-01-29 14:14:15 -0700594 tout.warning(
Simon Glass2cc8c1f2022-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 Glass4f9ee832022-01-09 20:14:09 -0700598 missing_bintool_list = []
599 image.check_missing_bintools(missing_bintool_list)
600 if missing_bintool_list:
Simon Glassf3385a52022-01-29 14:14:15 -0700601 tout.warning(
Simon Glass4f9ee832022-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 Glassb88e81c2019-07-20 12:23:24 -0600606
607
Simon Glass53cd5d92019-07-08 14:25:29 -0600608def Binman(args):
Simon Glassbf7fd502016-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 Glass53cd5d92019-07-08 14:25:29 -0600615 args: Command line arguments Namespace object
Simon Glassbf7fd502016-11-25 20:15:51 -0700616 """
Simon Glass8dbb7442019-08-24 07:22:44 -0600617 global Image
618 global state
619
Simon Glass53cd5d92019-07-08 14:25:29 -0600620 if args.full_help:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700621 tools.print_full_help(
Paul Barker5fe50f92021-09-08 12:38:01 +0100622 os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'README.rst')
623 )
Simon Glassbf7fd502016-11-25 20:15:51 -0700624 return 0
625
Simon Glass8dbb7442019-08-24 07:22:44 -0600626 # Put these here so that we can import this module without libfdt
Simon Glass07237982020-08-05 13:27:47 -0600627 from binman.image import Image
Simon Glass16287932020-04-17 18:09:03 -0600628 from binman import state
Simon Glass8dbb7442019-08-24 07:22:44 -0600629
Simon Glass386c63c2022-01-09 20:13:50 -0700630 if args.cmd in ['ls', 'extract', 'replace', 'tool']:
Simon Glass96b6c502019-07-20 12:23:53 -0600631 try:
Simon Glassf3385a52022-01-29 14:14:15 -0700632 tout.init(args.verbosity)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700633 tools.prepare_output_dir(None)
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600634 if args.cmd == 'ls':
635 ListEntries(args.image, args.paths)
Simon Glass61f564d2019-07-08 14:25:48 -0600636
Simon Glass7bc4f0f2019-09-15 18:10:36 -0600637 if args.cmd == 'extract':
638 ExtractEntries(args.image, args.filename, args.outdir, args.paths,
Simon Glass943bf782021-11-23 21:09:50 -0700639 not args.uncompressed, args.format)
Simon Glass71ce0ba2019-07-08 14:25:52 -0600640
Simon Glass7bc4f0f2019-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 Glass386c63c2022-01-09 20:13:50 -0700645
646 if args.cmd == 'tool':
Simon Glassc1aa66e2022-01-29 14:14:04 -0700647 tools.set_tool_paths(args.toolpath)
Simon Glass386c63c2022-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 Glass7bc4f0f2019-09-15 18:10:36 -0600658 except:
659 raise
Simon Glassa6cb9952019-07-20 12:24:15 -0600660 finally:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700661 tools.finalise_output_dir()
Simon Glassa6cb9952019-07-20 12:24:15 -0600662 return 0
663
Simon Glass0427bed2021-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 Glassbf7fd502016-11-25 20:15:51 -0700671 # Try to figure out which device tree contains our image description
Simon Glass53cd5d92019-07-08 14:25:29 -0600672 if args.dt:
673 dtb_fname = args.dt
Simon Glassbf7fd502016-11-25 20:15:51 -0700674 else:
Simon Glass53cd5d92019-07-08 14:25:29 -0600675 board = args.board
Simon Glassbf7fd502016-11-25 20:15:51 -0700676 if not board:
677 raise ValueError('Must provide a board to process (use -b <board>)')
Simon Glass53cd5d92019-07-08 14:25:29 -0600678 board_pathname = os.path.join(args.build_dir, board)
Simon Glassbf7fd502016-11-25 20:15:51 -0700679 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
Simon Glass53cd5d92019-07-08 14:25:29 -0600680 if not args.indir:
681 args.indir = ['.']
682 args.indir.append(board_pathname)
Simon Glassbf7fd502016-11-25 20:15:51 -0700683
684 try:
Simon Glassf3385a52022-01-29 14:14:15 -0700685 tout.init(args.verbosity)
Simon Glass53cd5d92019-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 Glass0b6023e2021-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 Glassbf7fd502016-11-25 20:15:51 -0700696 try:
Simon Glassc1aa66e2022-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 Glass53cd5d92019-07-08 14:25:29 -0600700 state.SetEntryArgs(args.entry_arg)
Simon Glassc69d19c2021-07-06 10:36:37 -0600701 state.SetThreads(args.threads)
Simon Glassecab8972018-07-06 10:27:40 -0600702
Simon Glassa8573c42019-07-20 12:23:27 -0600703 images = PrepareImagesAndDtbs(dtb_fname, args.image,
Simon Glass0b6023e2021-03-18 20:25:06 +1300704 args.update_fdt, use_expanded)
Heiko Thierya89c8f22022-01-06 11:49:41 +0100705
Simon Glassc69d19c2021-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 Thierya89c8f22022-01-06 11:49:41 +0100709 invalid = False
Simon Glass4f9ee832022-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 Glassbf7fd502016-11-25 20:15:51 -0700713 for image in images.values():
Heiko Thierya89c8f22022-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 Glass2a72cc72018-09-14 04:57:20 -0600717
718 # Write the updated FDTs to our output files
Simon Glass4bdd1152019-07-20 12:23:29 -0600719 for dtb_item in state.GetAllFdts():
Simon Glassc1aa66e2022-01-29 14:14:04 -0700720 tools.write_file(dtb_item._fname, dtb_item.GetContents())
Simon Glass2a72cc72018-09-14 04:57:20 -0600721
Simon Glass0427bed2021-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 Thierya89c8f22022-01-06 11:49:41 +0100726 if invalid:
Simon Glassf3385a52022-01-29 14:14:15 -0700727 tout.warning("\nSome images are invalid")
Simon Glass03ebc202021-07-06 10:36:41 -0600728
729 # Use this to debug the time take to pack the image
730 #state.TimingShow()
Simon Glassbf7fd502016-11-25 20:15:51 -0700731 finally:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700732 tools.finalise_output_dir()
Simon Glassbf7fd502016-11-25 20:15:51 -0700733 finally:
Simon Glassf3385a52022-01-29 14:14:15 -0700734 tout.uninit()
Simon Glassbf7fd502016-11-25 20:15:51 -0700735
736 return 0