blob: 3c931d9aeb63290222f645561fa1214b8f8abc81 [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
9import os
Simon Glass91710b32018-07-17 13:25:32 -060010import re
Simon Glass2574ef62016-11-25 20:15:51 -070011import sys
12import tools
13
14import command
Simon Glass075a45c2017-11-13 18:55:00 -070015import elf
Simon Glass2574ef62016-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 Glass92307732018-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 Glass91710b32018-07-17 13:25:32 -060027# Arguments passed to binman to provide arguments to entries
28entry_args = {}
29
Simon Glass92307732018-07-06 10:27:40 -060030
Simon Glass2574ef62016-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 Glass22c92ca2017-05-27 07:38:29 -060050def _FindBinmanNode(dtb):
Simon Glass2574ef62016-11-25 20:15:51 -070051 """Find the 'binman' node in the device tree
52
53 Args:
Simon Glass22c92ca2017-05-27 07:38:29 -060054 dtb: Fdt object to scan
Simon Glass2574ef62016-11-25 20:15:51 -070055 Returns:
56 Node object of /binman node, or None if not found
57 """
Simon Glass22c92ca2017-05-27 07:38:29 -060058 for node in dtb.GetRoot().subnodes:
Simon Glass2574ef62016-11-25 20:15:51 -070059 if node.name == 'binman':
60 return node
61 return None
62
Simon Glass92307732018-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 Glass91710b32018-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 Glass2574ef62016-11-25 20:15:51 -070095def Binman(options, args):
96 """The main control code for binman
97
98 This assumes that help and test options have already been dealt with. It
99 deals with the core task of building images.
100
101 Args:
102 options: Command line options object
103 args: Command line arguments (list of strings)
104 """
105 global images
106
107 if options.full_help:
108 pager = os.getenv('PAGER')
109 if not pager:
110 pager = 'more'
111 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
112 'README')
113 command.Run(pager, fname)
114 return 0
115
116 # Try to figure out which device tree contains our image description
117 if options.dt:
118 dtb_fname = options.dt
119 else:
120 board = options.board
121 if not board:
122 raise ValueError('Must provide a board to process (use -b <board>)')
123 board_pathname = os.path.join(options.build_dir, board)
124 dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
125 if not options.indir:
126 options.indir = ['.']
127 options.indir.append(board_pathname)
128
129 try:
Simon Glass63a336b2018-07-17 13:25:34 -0600130 # Import these here in case libfdt.py is not available, in which case
131 # the above help option still works.
132 import fdt
133 import fdt_util
134
Simon Glass2574ef62016-11-25 20:15:51 -0700135 tout.Init(options.verbosity)
Simon Glass4ca8e042017-11-13 18:55:01 -0700136 elf.debug = options.debug
Simon Glass2574ef62016-11-25 20:15:51 -0700137 try:
138 tools.SetInputDirs(options.indir)
139 tools.PrepareOutputDir(options.outdir, options.preserve)
Simon Glass91710b32018-07-17 13:25:32 -0600140 SetEntryArgs(options.entry_arg)
Simon Glass92307732018-07-06 10:27:40 -0600141
142 # Get the device tree ready by compiling it and copying the compiled
143 # output into a file in our output directly. Then scan it for use
144 # in binman.
145 dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
146 fname = tools.GetOutputFilename('u-boot-out.dtb')
147 with open(dtb_fname) as infd:
148 with open(fname, 'wb') as outfd:
149 outfd.write(infd.read())
150 dtb = fdt.FdtScan(fname)
151
152 # Note the file so that GetFdt() can find it
153 fdt_files['u-boot.dtb'] = dtb
Simon Glass22c92ca2017-05-27 07:38:29 -0600154 node = _FindBinmanNode(dtb)
Simon Glass2574ef62016-11-25 20:15:51 -0700155 if not node:
156 raise ValueError("Device tree '%s' does not have a 'binman' "
157 "node" % dtb_fname)
Simon Glass92307732018-07-06 10:27:40 -0600158
Simon Glass2574ef62016-11-25 20:15:51 -0700159 images = _ReadImageDesc(node)
Simon Glass92307732018-07-06 10:27:40 -0600160
161 # Prepare the device tree by making sure that any missing
162 # properties are added (e.g. 'pos' and 'size'). The values of these
163 # may not be correct yet, but we add placeholders so that the
164 # size of the device tree is correct. Later, in
165 # SetCalculatedProperties() we will insert the correct values
166 # without changing the device-tree size, thus ensuring that our
Simon Glasse8561af2018-08-01 15:22:37 -0600167 # entry offsets remain the same.
Simon Glass92307732018-07-06 10:27:40 -0600168 for image in images.values():
Simon Glasse22f8fa2018-07-06 10:27:41 -0600169 if options.update_fdt:
170 image.AddMissingProperties()
Simon Glass92307732018-07-06 10:27:40 -0600171 image.ProcessFdt(dtb)
172
173 dtb.Pack()
174 dtb.Flush()
175
Simon Glass2574ef62016-11-25 20:15:51 -0700176 for image in images.values():
177 # Perform all steps for this image, including checking and
178 # writing it. This means that errors found with a later
179 # image will be reported after earlier images are already
180 # completed and written, but that does not seem important.
181 image.GetEntryContents()
Simon Glasse8561af2018-08-01 15:22:37 -0600182 image.GetEntryOffsets()
Simon Glass2574ef62016-11-25 20:15:51 -0700183 image.PackEntries()
184 image.CheckSize()
185 image.CheckEntries()
Simon Glass9dcc8612018-08-01 15:22:42 -0600186 image.SetImagePos()
Simon Glasse22f8fa2018-07-06 10:27:41 -0600187 if options.update_fdt:
188 image.SetCalculatedProperties()
Simon Glass2574ef62016-11-25 20:15:51 -0700189 image.ProcessEntryContents()
Simon Glass4ca8e042017-11-13 18:55:01 -0700190 image.WriteSymbols()
Simon Glass2574ef62016-11-25 20:15:51 -0700191 image.BuildImage()
Simon Glass30732662018-06-01 09:38:20 -0600192 if options.map:
193 image.WriteMap()
Simon Glassa87014e2018-07-06 10:27:42 -0600194 with open(fname, 'wb') as outfd:
195 outfd.write(dtb.GetContents())
Simon Glass2574ef62016-11-25 20:15:51 -0700196 finally:
197 tools.FinaliseOutputDir()
198 finally:
199 tout.Uninit()
200
201 return 0