blob: 813c8b1bf9edd0a07eeb42097b186865936d1736 [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
Simon Glass7cca27d2019-05-14 15:53:37 -06008from __future__ import print_function
9
Simon Glass2574ef62016-11-25 20:15:51 -070010from collections import OrderedDict
11import os
12import sys
13import tools
14
Simon Glass1de34482019-07-08 13:18:53 -060015import cbfs_util
Simon Glass2574ef62016-11-25 20:15:51 -070016import command
Simon Glass075a45c2017-11-13 18:55:00 -070017import elf
Simon Glass2574ef62016-11-25 20:15:51 -070018from image import Image
Simon Glass29aa7362018-09-14 04:57:19 -060019import state
Simon Glass2574ef62016-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 Glass22c92ca2017-05-27 07:38:29 -060045def _FindBinmanNode(dtb):
Simon Glass2574ef62016-11-25 20:15:51 -070046 """Find the 'binman' node in the device tree
47
48 Args:
Simon Glass22c92ca2017-05-27 07:38:29 -060049 dtb: Fdt object to scan
Simon Glass2574ef62016-11-25 20:15:51 -070050 Returns:
51 Node object of /binman node, or None if not found
52 """
Simon Glass22c92ca2017-05-27 07:38:29 -060053 for node in dtb.GetRoot().subnodes:
Simon Glass2574ef62016-11-25 20:15:51 -070054 if node.name == 'binman':
55 return node
56 return None
57
Simon Glass29aa7362018-09-14 04:57:19 -060058def WriteEntryDocs(modules, test_missing=None):
59 """Write out documentation for all entries
Simon Glass92307732018-07-06 10:27:40 -060060
61 Args:
Simon Glass29aa7362018-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 Glass92307732018-07-06 10:27:40 -060066 """
Simon Glass969616c2018-07-17 13:25:36 -060067 from entry import Entry
68 Entry.WriteDocs(modules, test_missing)
69
Simon Glassb2fd11d2019-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 Glassf46732a2019-07-08 14:25:29 -0600101def Binman(args):
Simon Glass2574ef62016-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 Glassf46732a2019-07-08 14:25:29 -0600108 args: Command line arguments Namespace object
Simon Glass2574ef62016-11-25 20:15:51 -0700109 """
110 global images
111
Simon Glassf46732a2019-07-08 14:25:29 -0600112 if args.full_help:
Simon Glass2574ef62016-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 Glassb2fd11d2019-07-08 14:25:48 -0600121 if args.cmd == 'ls':
122 ListEntries(args.image, args.paths)
123 return 0
124
Simon Glass2574ef62016-11-25 20:15:51 -0700125 # Try to figure out which device tree contains our image description
Simon Glassf46732a2019-07-08 14:25:29 -0600126 if args.dt:
127 dtb_fname = args.dt
Simon Glass2574ef62016-11-25 20:15:51 -0700128 else:
Simon Glassf46732a2019-07-08 14:25:29 -0600129 board = args.board
Simon Glass2574ef62016-11-25 20:15:51 -0700130 if not board:
131 raise ValueError('Must provide a board to process (use -b <board>)')
Simon Glassf46732a2019-07-08 14:25:29 -0600132 board_pathname = os.path.join(args.build_dir, board)
Simon Glass2574ef62016-11-25 20:15:51 -0700133 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
Simon Glassf46732a2019-07-08 14:25:29 -0600134 if not args.indir:
135 args.indir = ['.']
136 args.indir.append(board_pathname)
Simon Glass2574ef62016-11-25 20:15:51 -0700137
138 try:
Simon Glass63a336b2018-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 Glassf46732a2019-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 Glass2574ef62016-11-25 20:15:51 -0700148 try:
Simon Glassf46732a2019-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 Glass92307732018-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 Glasse219aa42018-09-14 04:57:24 -0600158 fname = tools.GetOutputFilename('u-boot.dtb.out')
159 tools.WriteFile(fname, tools.ReadFile(dtb_fname))
Simon Glass92307732018-07-06 10:27:40 -0600160 dtb = fdt.FdtScan(fname)
161
Simon Glass22c92ca2017-05-27 07:38:29 -0600162 node = _FindBinmanNode(dtb)
Simon Glass2574ef62016-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 Glass92307732018-07-06 10:27:40 -0600166
Simon Glass2574ef62016-11-25 20:15:51 -0700167 images = _ReadImageDesc(node)
Simon Glass92307732018-07-06 10:27:40 -0600168
Simon Glassf46732a2019-07-08 14:25:29 -0600169 if args.image:
Simon Glass3b376c32018-09-14 04:57:12 -0600170 skip = []
Simon Glass42775432019-05-17 22:00:45 -0600171 new_images = OrderedDict()
Simon Glass5f3645b2019-05-14 15:53:41 -0600172 for name, image in images.items():
Simon Glassf46732a2019-07-08 14:25:29 -0600173 if name in args.image:
Simon Glass42775432019-05-17 22:00:45 -0600174 new_images[name] = image
175 else:
Simon Glass3b376c32018-09-14 04:57:12 -0600176 skip.append(name)
Simon Glass42775432019-05-17 22:00:45 -0600177 images = new_images
Simon Glassf46732a2019-07-08 14:25:29 -0600178 if skip and args.verbosity >= 2:
Simon Glass7cca27d2019-05-14 15:53:37 -0600179 print('Skipping images: %s' % ', '.join(skip))
Simon Glass3b376c32018-09-14 04:57:12 -0600180
Simon Glass0c9d5b52018-09-14 04:57:22 -0600181 state.Prepare(images, dtb)
Simon Glassbdb40312018-09-14 04:57:20 -0600182
Simon Glass92307732018-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 Glasse8561af2018-08-01 15:22:37 -0600189 # entry offsets remain the same.
Simon Glass92307732018-07-06 10:27:40 -0600190 for image in images.values():
Simon Glassac6328c2018-09-14 04:57:28 -0600191 image.ExpandEntries()
Simon Glassf46732a2019-07-08 14:25:29 -0600192 if args.update_fdt:
Simon Glasse22f8fa2018-07-06 10:27:41 -0600193 image.AddMissingProperties()
Simon Glass92307732018-07-06 10:27:40 -0600194 image.ProcessFdt(dtb)
195
Simon Glassbdb40312018-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 Glass92307732018-07-06 10:27:40 -0600200
Simon Glass2574ef62016-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 Glasse8561af2018-08-01 15:22:37 -0600207 image.GetEntryOffsets()
Simon Glasse61b6f62019-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 Glass4ca8e042017-11-13 18:55:01 -0700244 image.WriteSymbols()
Simon Glass2574ef62016-11-25 20:15:51 -0700245 image.BuildImage()
Simon Glassf46732a2019-07-08 14:25:29 -0600246 if args.map:
Simon Glass30732662018-06-01 09:38:20 -0600247 image.WriteMap()
Simon Glassbdb40312018-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 Glass2574ef62016-11-25 20:15:51 -0700253 finally:
254 tools.FinaliseOutputDir()
255 finally:
256 tout.Uninit()
257
258 return 0