blob: 8c48008fc7535327446c6977148ad7717c1b1cec [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
9import os
Simon Glass53af22a2018-07-17 13:25:32 -060010import re
Simon Glassbf7fd502016-11-25 20:15:51 -070011import sys
12import tools
13
14import command
Simon Glass7fe91732017-11-13 18:55:00 -070015import elf
Simon Glassbf7fd502016-11-25 20:15:51 -070016from image import Image
17import tout
18
19# List of images we plan to create
20# Make this global so that it can be referenced from tests
21images = OrderedDict()
22
Simon Glassecab8972018-07-06 10:27:40 -060023# Records the device-tree files known to binman, keyed by filename (e.g.
24# 'u-boot-spl.dtb')
25fdt_files = {}
26
Simon Glass53af22a2018-07-17 13:25:32 -060027# Arguments passed to binman to provide arguments to entries
28entry_args = {}
29
Simon Glassecab8972018-07-06 10:27:40 -060030
Simon Glassbf7fd502016-11-25 20:15:51 -070031def _ReadImageDesc(binman_node):
32 """Read the image descriptions from the /binman node
33
34 This normally produces a single Image object called 'image'. But if
35 multiple images are present, they will all be returned.
36
37 Args:
38 binman_node: Node object of the /binman node
39 Returns:
40 OrderedDict of Image objects, each of which describes an image
41 """
42 images = OrderedDict()
43 if 'multiple-images' in binman_node.props:
44 for node in binman_node.subnodes:
45 images[node.name] = Image(node.name, node)
46 else:
47 images['image'] = Image('image', binman_node)
48 return images
49
Simon Glassec3f3782017-05-27 07:38:29 -060050def _FindBinmanNode(dtb):
Simon Glassbf7fd502016-11-25 20:15:51 -070051 """Find the 'binman' node in the device tree
52
53 Args:
Simon Glassec3f3782017-05-27 07:38:29 -060054 dtb: Fdt object to scan
Simon Glassbf7fd502016-11-25 20:15:51 -070055 Returns:
56 Node object of /binman node, or None if not found
57 """
Simon Glassec3f3782017-05-27 07:38:29 -060058 for node in dtb.GetRoot().subnodes:
Simon Glassbf7fd502016-11-25 20:15:51 -070059 if node.name == 'binman':
60 return node
61 return None
62
Simon Glassecab8972018-07-06 10:27:40 -060063def GetFdt(fname):
64 """Get the Fdt object for a particular device-tree filename
65
66 Binman keeps track of at least one device-tree file called u-boot.dtb but
67 can also have others (e.g. for SPL). This function looks up the given
68 filename and returns the associated Fdt object.
69
70 Args:
71 fname: Filename to look up (e.g. 'u-boot.dtb').
72
73 Returns:
74 Fdt object associated with the filename
75 """
76 return fdt_files[fname]
77
78def GetFdtPath(fname):
79 return fdt_files[fname]._fname
80
Simon Glass53af22a2018-07-17 13:25:32 -060081def SetEntryArgs(args):
82 global entry_args
83
84 entry_args = {}
85 if args:
86 for arg in args:
87 m = re.match('([^=]*)=(.*)', arg)
88 if not m:
89 raise ValueError("Invalid entry arguemnt '%s'" % arg)
90 entry_args[m.group(1)] = m.group(2)
91
92def GetEntryArg(name):
93 return entry_args.get(name)
94
Simon Glassfd8d1f72018-07-17 13:25:36 -060095def WriteEntryDocs(modules, test_missing=None):
96 from entry import Entry
97 Entry.WriteDocs(modules, test_missing)
98
Simon Glassbf7fd502016-11-25 20:15:51 -070099def Binman(options, args):
100 """The main control code for binman
101
102 This assumes that help and test options have already been dealt with. It
103 deals with the core task of building images.
104
105 Args:
106 options: Command line options object
107 args: Command line arguments (list of strings)
108 """
109 global images
110
111 if options.full_help:
112 pager = os.getenv('PAGER')
113 if not pager:
114 pager = 'more'
115 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
116 'README')
117 command.Run(pager, fname)
118 return 0
119
120 # Try to figure out which device tree contains our image description
121 if options.dt:
122 dtb_fname = options.dt
123 else:
124 board = options.board
125 if not board:
126 raise ValueError('Must provide a board to process (use -b <board>)')
127 board_pathname = os.path.join(options.build_dir, board)
128 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
129 if not options.indir:
130 options.indir = ['.']
131 options.indir.append(board_pathname)
132
133 try:
Simon Glass9b1a8042018-07-17 13:25:34 -0600134 # Import these here in case libfdt.py is not available, in which case
135 # the above help option still works.
136 import fdt
137 import fdt_util
138
Simon Glassbf7fd502016-11-25 20:15:51 -0700139 tout.Init(options.verbosity)
Simon Glass19790632017-11-13 18:55:01 -0700140 elf.debug = options.debug
Simon Glassbf7fd502016-11-25 20:15:51 -0700141 try:
142 tools.SetInputDirs(options.indir)
143 tools.PrepareOutputDir(options.outdir, options.preserve)
Simon Glass53af22a2018-07-17 13:25:32 -0600144 SetEntryArgs(options.entry_arg)
Simon Glassecab8972018-07-06 10:27:40 -0600145
146 # Get the device tree ready by compiling it and copying the compiled
147 # output into a file in our output directly. Then scan it for use
148 # in binman.
149 dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
150 fname = tools.GetOutputFilename('u-boot-out.dtb')
151 with open(dtb_fname) as infd:
152 with open(fname, 'wb') as outfd:
153 outfd.write(infd.read())
154 dtb = fdt.FdtScan(fname)
155
156 # Note the file so that GetFdt() can find it
157 fdt_files['u-boot.dtb'] = dtb
Simon Glassec3f3782017-05-27 07:38:29 -0600158 node = _FindBinmanNode(dtb)
Simon Glassbf7fd502016-11-25 20:15:51 -0700159 if not node:
160 raise ValueError("Device tree '%s' does not have a 'binman' "
161 "node" % dtb_fname)
Simon Glassecab8972018-07-06 10:27:40 -0600162
Simon Glassbf7fd502016-11-25 20:15:51 -0700163 images = _ReadImageDesc(node)
Simon Glassecab8972018-07-06 10:27:40 -0600164
Simon Glass0bfa7b02018-09-14 04:57:12 -0600165 if options.image:
166 skip = []
167 for name, image in images.iteritems():
168 if name not in options.image:
169 del images[name]
170 skip.append(name)
171 if skip:
172 print 'Skipping images: %s\n' % ', '.join(skip)
173
Simon Glassecab8972018-07-06 10:27:40 -0600174 # Prepare the device tree by making sure that any missing
175 # properties are added (e.g. 'pos' and 'size'). The values of these
176 # may not be correct yet, but we add placeholders so that the
177 # size of the device tree is correct. Later, in
178 # SetCalculatedProperties() we will insert the correct values
179 # without changing the device-tree size, thus ensuring that our
Simon Glass3ab95982018-08-01 15:22:37 -0600180 # entry offsets remain the same.
Simon Glassecab8972018-07-06 10:27:40 -0600181 for image in images.values():
Simon Glass078ab1a2018-07-06 10:27:41 -0600182 if options.update_fdt:
183 image.AddMissingProperties()
Simon Glassecab8972018-07-06 10:27:40 -0600184 image.ProcessFdt(dtb)
185
186 dtb.Pack()
187 dtb.Flush()
188
Simon Glassbf7fd502016-11-25 20:15:51 -0700189 for image in images.values():
190 # Perform all steps for this image, including checking and
191 # writing it. This means that errors found with a later
192 # image will be reported after earlier images are already
193 # completed and written, but that does not seem important.
194 image.GetEntryContents()
Simon Glass3ab95982018-08-01 15:22:37 -0600195 image.GetEntryOffsets()
Simon Glassbf7fd502016-11-25 20:15:51 -0700196 image.PackEntries()
197 image.CheckSize()
198 image.CheckEntries()
Simon Glassdbf6be92018-08-01 15:22:42 -0600199 image.SetImagePos()
Simon Glass078ab1a2018-07-06 10:27:41 -0600200 if options.update_fdt:
201 image.SetCalculatedProperties()
Simon Glassbf7fd502016-11-25 20:15:51 -0700202 image.ProcessEntryContents()
Simon Glass19790632017-11-13 18:55:01 -0700203 image.WriteSymbols()
Simon Glassbf7fd502016-11-25 20:15:51 -0700204 image.BuildImage()
Simon Glass3b0c3822018-06-01 09:38:20 -0600205 if options.map:
206 image.WriteMap()
Simon Glass16b8d6b2018-07-06 10:27:42 -0600207 with open(fname, 'wb') as outfd:
208 outfd.write(dtb.GetContents())
Simon Glassbf7fd502016-11-25 20:15:51 -0700209 finally:
210 tools.FinaliseOutputDir()
211 finally:
212 tout.Uninit()
213
214 return 0