blob: 813c8b1bf9edd0a07eeb42097b186865936d1736 [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
Simon Glass2ca84682019-05-14 15:53:37 -06008from __future__ import print_function
9
Simon Glassbf7fd502016-11-25 20:15:51 -070010from collections import OrderedDict
11import os
12import sys
13import tools
14
Simon Glassac62fba2019-07-08 13:18:53 -060015import cbfs_util
Simon Glassbf7fd502016-11-25 20:15:51 -070016import command
Simon Glass7fe91732017-11-13 18:55:00 -070017import elf
Simon Glassbf7fd502016-11-25 20:15:51 -070018from image import Image
Simon Glassc55a50f2018-09-14 04:57:19 -060019import state
Simon Glassbf7fd502016-11-25 20:15:51 -070020import tout
21
22# List of images we plan to create
23# Make this global so that it can be referenced from tests
24images = OrderedDict()
25
26def _ReadImageDesc(binman_node):
27 """Read the image descriptions from the /binman node
28
29 This normally produces a single Image object called 'image'. But if
30 multiple images are present, they will all be returned.
31
32 Args:
33 binman_node: Node object of the /binman node
34 Returns:
35 OrderedDict of Image objects, each of which describes an image
36 """
37 images = OrderedDict()
38 if 'multiple-images' in binman_node.props:
39 for node in binman_node.subnodes:
40 images[node.name] = Image(node.name, node)
41 else:
42 images['image'] = Image('image', binman_node)
43 return images
44
Simon Glassec3f3782017-05-27 07:38:29 -060045def _FindBinmanNode(dtb):
Simon Glassbf7fd502016-11-25 20:15:51 -070046 """Find the 'binman' node in the device tree
47
48 Args:
Simon Glassec3f3782017-05-27 07:38:29 -060049 dtb: Fdt object to scan
Simon Glassbf7fd502016-11-25 20:15:51 -070050 Returns:
51 Node object of /binman node, or None if not found
52 """
Simon Glassec3f3782017-05-27 07:38:29 -060053 for node in dtb.GetRoot().subnodes:
Simon Glassbf7fd502016-11-25 20:15:51 -070054 if node.name == 'binman':
55 return node
56 return None
57
Simon Glassc55a50f2018-09-14 04:57:19 -060058def WriteEntryDocs(modules, test_missing=None):
59 """Write out documentation for all entries
Simon Glassecab8972018-07-06 10:27:40 -060060
61 Args:
Simon Glassc55a50f2018-09-14 04:57:19 -060062 modules: List of Module objects to get docs for
63 test_missing: Used for testing only, to force an entry's documeentation
64 to show as missing even if it is present. Should be set to None in
65 normal use.
Simon Glassecab8972018-07-06 10:27:40 -060066 """
Simon Glassfd8d1f72018-07-17 13:25:36 -060067 from entry import Entry
68 Entry.WriteDocs(modules, test_missing)
69
Simon Glass61f564d2019-07-08 14:25:48 -060070
71def ListEntries(image_fname, entry_paths):
72 """List the entries in an image
73
74 This decodes the supplied image and displays a table of entries from that
75 image, preceded by a header.
76
77 Args:
78 image_fname: Image filename to process
79 entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
80 'section/u-boot'])
81 """
82 image = Image.FromFile(image_fname)
83
84 entries, lines, widths = image.GetListEntries(entry_paths)
85
86 num_columns = len(widths)
87 for linenum, line in enumerate(lines):
88 if linenum == 1:
89 # Print header line
90 print('-' * (sum(widths) + num_columns * 2))
91 out = ''
92 for i, item in enumerate(line):
93 width = -widths[i]
94 if item.startswith('>'):
95 width = -width
96 item = item[1:]
97 txt = '%*s ' % (width, item)
98 out += txt
99 print(out.rstrip())
100
Simon Glass53cd5d92019-07-08 14:25:29 -0600101def Binman(args):
Simon Glassbf7fd502016-11-25 20:15:51 -0700102 """The main control code for binman
103
104 This assumes that help and test options have already been dealt with. It
105 deals with the core task of building images.
106
107 Args:
Simon Glass53cd5d92019-07-08 14:25:29 -0600108 args: Command line arguments Namespace object
Simon Glassbf7fd502016-11-25 20:15:51 -0700109 """
110 global images
111
Simon Glass53cd5d92019-07-08 14:25:29 -0600112 if args.full_help:
Simon Glassbf7fd502016-11-25 20:15:51 -0700113 pager = os.getenv('PAGER')
114 if not pager:
115 pager = 'more'
116 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
117 'README')
118 command.Run(pager, fname)
119 return 0
120
Simon Glass61f564d2019-07-08 14:25:48 -0600121 if args.cmd == 'ls':
122 ListEntries(args.image, args.paths)
123 return 0
124
Simon Glassbf7fd502016-11-25 20:15:51 -0700125 # Try to figure out which device tree contains our image description
Simon Glass53cd5d92019-07-08 14:25:29 -0600126 if args.dt:
127 dtb_fname = args.dt
Simon Glassbf7fd502016-11-25 20:15:51 -0700128 else:
Simon Glass53cd5d92019-07-08 14:25:29 -0600129 board = args.board
Simon Glassbf7fd502016-11-25 20:15:51 -0700130 if not board:
131 raise ValueError('Must provide a board to process (use -b <board>)')
Simon Glass53cd5d92019-07-08 14:25:29 -0600132 board_pathname = os.path.join(args.build_dir, board)
Simon Glassbf7fd502016-11-25 20:15:51 -0700133 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
Simon Glass53cd5d92019-07-08 14:25:29 -0600134 if not args.indir:
135 args.indir = ['.']
136 args.indir.append(board_pathname)
Simon Glassbf7fd502016-11-25 20:15:51 -0700137
138 try:
Simon Glass9b1a8042018-07-17 13:25:34 -0600139 # Import these here in case libfdt.py is not available, in which case
140 # the above help option still works.
141 import fdt
142 import fdt_util
143
Simon Glass53cd5d92019-07-08 14:25:29 -0600144 tout.Init(args.verbosity)
145 elf.debug = args.debug
146 cbfs_util.VERBOSE = args.verbosity > 2
147 state.use_fake_dtb = args.fake_dtb
Simon Glassbf7fd502016-11-25 20:15:51 -0700148 try:
Simon Glass53cd5d92019-07-08 14:25:29 -0600149 tools.SetInputDirs(args.indir)
150 tools.PrepareOutputDir(args.outdir, args.preserve)
151 tools.SetToolPaths(args.toolpath)
152 state.SetEntryArgs(args.entry_arg)
Simon Glassecab8972018-07-06 10:27:40 -0600153
154 # Get the device tree ready by compiling it and copying the compiled
155 # output into a file in our output directly. Then scan it for use
156 # in binman.
157 dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
Simon Glass6ed45ba2018-09-14 04:57:24 -0600158 fname = tools.GetOutputFilename('u-boot.dtb.out')
159 tools.WriteFile(fname, tools.ReadFile(dtb_fname))
Simon Glassecab8972018-07-06 10:27:40 -0600160 dtb = fdt.FdtScan(fname)
161
Simon Glassec3f3782017-05-27 07:38:29 -0600162 node = _FindBinmanNode(dtb)
Simon Glassbf7fd502016-11-25 20:15:51 -0700163 if not node:
164 raise ValueError("Device tree '%s' does not have a 'binman' "
165 "node" % dtb_fname)
Simon Glassecab8972018-07-06 10:27:40 -0600166
Simon Glassbf7fd502016-11-25 20:15:51 -0700167 images = _ReadImageDesc(node)
Simon Glassecab8972018-07-06 10:27:40 -0600168
Simon Glass53cd5d92019-07-08 14:25:29 -0600169 if args.image:
Simon Glass0bfa7b02018-09-14 04:57:12 -0600170 skip = []
Simon Glass58632a72019-05-17 22:00:45 -0600171 new_images = OrderedDict()
Simon Glass50979152019-05-14 15:53:41 -0600172 for name, image in images.items():
Simon Glass53cd5d92019-07-08 14:25:29 -0600173 if name in args.image:
Simon Glass58632a72019-05-17 22:00:45 -0600174 new_images[name] = image
175 else:
Simon Glass0bfa7b02018-09-14 04:57:12 -0600176 skip.append(name)
Simon Glass58632a72019-05-17 22:00:45 -0600177 images = new_images
Simon Glass53cd5d92019-07-08 14:25:29 -0600178 if skip and args.verbosity >= 2:
Simon Glass2ca84682019-05-14 15:53:37 -0600179 print('Skipping images: %s' % ', '.join(skip))
Simon Glass0bfa7b02018-09-14 04:57:12 -0600180
Simon Glass539aece2018-09-14 04:57:22 -0600181 state.Prepare(images, dtb)
Simon Glass2a72cc72018-09-14 04:57:20 -0600182
Simon Glassecab8972018-07-06 10:27:40 -0600183 # Prepare the device tree by making sure that any missing
184 # properties are added (e.g. 'pos' and 'size'). The values of these
185 # may not be correct yet, but we add placeholders so that the
186 # size of the device tree is correct. Later, in
187 # SetCalculatedProperties() we will insert the correct values
188 # without changing the device-tree size, thus ensuring that our
Simon Glass3ab95982018-08-01 15:22:37 -0600189 # entry offsets remain the same.
Simon Glassecab8972018-07-06 10:27:40 -0600190 for image in images.values():
Simon Glass0a98b282018-09-14 04:57:28 -0600191 image.ExpandEntries()
Simon Glass53cd5d92019-07-08 14:25:29 -0600192 if args.update_fdt:
Simon Glass078ab1a2018-07-06 10:27:41 -0600193 image.AddMissingProperties()
Simon Glassecab8972018-07-06 10:27:40 -0600194 image.ProcessFdt(dtb)
195
Simon Glass2a72cc72018-09-14 04:57:20 -0600196 for dtb_item in state.GetFdts():
197 dtb_item.Sync(auto_resize=True)
198 dtb_item.Pack()
199 dtb_item.Flush()
Simon Glassecab8972018-07-06 10:27:40 -0600200
Simon Glassbf7fd502016-11-25 20:15:51 -0700201 for image in images.values():
202 # Perform all steps for this image, including checking and
203 # writing it. This means that errors found with a later
204 # image will be reported after earlier images are already
205 # completed and written, but that does not seem important.
206 image.GetEntryContents()
Simon Glass3ab95982018-08-01 15:22:37 -0600207 image.GetEntryOffsets()
Simon Glassc52c9e72019-07-08 14:25:37 -0600208
209 # We need to pack the entries to figure out where everything
210 # should be placed. This sets the offset/size of each entry.
211 # However, after packing we call ProcessEntryContents() which
212 # may result in an entry changing size. In that case we need to
213 # do another pass. Since the device tree often contains the
214 # final offset/size information we try to make space for this in
215 # AddMissingProperties() above. However, if the device is
216 # compressed we cannot know this compressed size in advance,
217 # since changing an offset from 0x100 to 0x104 (for example) can
218 # alter the compressed size of the device tree. So we need a
219 # third pass for this.
220 passes = 3
221 for pack_pass in range(passes):
222 try:
223 image.PackEntries()
224 image.CheckSize()
225 image.CheckEntries()
226 except Exception as e:
227 if args.map:
228 fname = image.WriteMap()
229 print("Wrote map file '%s' to show errors" % fname)
230 raise
231 image.SetImagePos()
232 if args.update_fdt:
233 image.SetCalculatedProperties()
234 for dtb_item in state.GetFdts():
235 dtb_item.Sync()
236 sizes_ok = image.ProcessEntryContents()
237 if sizes_ok:
238 break
239 image.ResetForPack()
240 if not sizes_ok:
241 image.Raise('Entries expanded after packing (tried %s passes)' %
242 passes)
243
Simon Glass19790632017-11-13 18:55:01 -0700244 image.WriteSymbols()
Simon Glassbf7fd502016-11-25 20:15:51 -0700245 image.BuildImage()
Simon Glass53cd5d92019-07-08 14:25:29 -0600246 if args.map:
Simon Glass3b0c3822018-06-01 09:38:20 -0600247 image.WriteMap()
Simon Glass2a72cc72018-09-14 04:57:20 -0600248
249 # Write the updated FDTs to our output files
250 for dtb_item in state.GetFdts():
251 tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
252
Simon Glassbf7fd502016-11-25 20:15:51 -0700253 finally:
254 tools.FinaliseOutputDir()
255 finally:
256 tout.Uninit()
257
258 return 0