binman: Add support for passing arguments to entries
[oweals/u-boot.git] / tools / binman / control.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # Creates binary images from input files controlled by a description
6 #
7
8 from collections import OrderedDict
9 import os
10 import re
11 import sys
12 import tools
13
14 import command
15 import elf
16 import fdt
17 import fdt_util
18 from image import Image
19 import tout
20
21 # List of images we plan to create
22 # Make this global so that it can be referenced from tests
23 images = OrderedDict()
24
25 # Records the device-tree files known to binman, keyed by filename (e.g.
26 # 'u-boot-spl.dtb')
27 fdt_files = {}
28
29 # Arguments passed to binman to provide arguments to entries
30 entry_args = {}
31
32
33 def _ReadImageDesc(binman_node):
34     """Read the image descriptions from the /binman node
35
36     This normally produces a single Image object called 'image'. But if
37     multiple images are present, they will all be returned.
38
39     Args:
40         binman_node: Node object of the /binman node
41     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:
47             images[node.name] = Image(node.name, node)
48     else:
49         images['image'] = Image('image', binman_node)
50     return images
51
52 def _FindBinmanNode(dtb):
53     """Find the 'binman' node in the device tree
54
55     Args:
56         dtb: Fdt object to scan
57     Returns:
58         Node object of /binman node, or None if not found
59     """
60     for node in dtb.GetRoot().subnodes:
61         if node.name == 'binman':
62             return node
63     return None
64
65 def GetFdt(fname):
66     """Get the Fdt object for a particular device-tree filename
67
68     Binman keeps track of at least one device-tree file called u-boot.dtb but
69     can also have others (e.g. for SPL). This function looks up the given
70     filename and returns the associated Fdt object.
71
72     Args:
73         fname: Filename to look up (e.g. 'u-boot.dtb').
74
75     Returns:
76         Fdt object associated with the filename
77     """
78     return fdt_files[fname]
79
80 def GetFdtPath(fname):
81     return fdt_files[fname]._fname
82
83 def SetEntryArgs(args):
84     global entry_args
85
86     entry_args = {}
87     if args:
88         for arg in args:
89             m = re.match('([^=]*)=(.*)', arg)
90             if not m:
91                 raise ValueError("Invalid entry arguemnt '%s'" % arg)
92             entry_args[m.group(1)] = m.group(2)
93
94 def GetEntryArg(name):
95     return entry_args.get(name)
96
97 def Binman(options, args):
98     """The main control code for binman
99
100     This assumes that help and test options have already been dealt with. It
101     deals with the core task of building images.
102
103     Args:
104         options: Command line options object
105         args: Command line arguments (list of strings)
106     """
107     global images
108
109     if options.full_help:
110         pager = os.getenv('PAGER')
111         if not pager:
112             pager = 'more'
113         fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
114                             'README')
115         command.Run(pager, fname)
116         return 0
117
118     # Try to figure out which device tree contains our image description
119     if options.dt:
120         dtb_fname = options.dt
121     else:
122         board = options.board
123         if not board:
124             raise ValueError('Must provide a board to process (use -b <board>)')
125         board_pathname = os.path.join(options.build_dir, board)
126         dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
127         if not options.indir:
128             options.indir = ['.']
129         options.indir.append(board_pathname)
130
131     try:
132         tout.Init(options.verbosity)
133         elf.debug = options.debug
134         try:
135             tools.SetInputDirs(options.indir)
136             tools.PrepareOutputDir(options.outdir, options.preserve)
137             SetEntryArgs(options.entry_arg)
138
139             # Get the device tree ready by compiling it and copying the compiled
140             # output into a file in our output directly. Then scan it for use
141             # in binman.
142             dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
143             fname = tools.GetOutputFilename('u-boot-out.dtb')
144             with open(dtb_fname) as infd:
145                 with open(fname, 'wb') as outfd:
146                     outfd.write(infd.read())
147             dtb = fdt.FdtScan(fname)
148
149             # Note the file so that GetFdt() can find it
150             fdt_files['u-boot.dtb'] = dtb
151             node = _FindBinmanNode(dtb)
152             if not node:
153                 raise ValueError("Device tree '%s' does not have a 'binman' "
154                                  "node" % dtb_fname)
155
156             images = _ReadImageDesc(node)
157
158             # Prepare the device tree by making sure that any missing
159             # properties are added (e.g. 'pos' and 'size'). The values of these
160             # may not be correct yet, but we add placeholders so that the
161             # size of the device tree is correct. Later, in
162             # SetCalculatedProperties() we will insert the correct values
163             # without changing the device-tree size, thus ensuring that our
164             # entry offsets remain the same.
165             for image in images.values():
166                 if options.update_fdt:
167                     image.AddMissingProperties()
168                 image.ProcessFdt(dtb)
169
170             dtb.Pack()
171             dtb.Flush()
172
173             for image in images.values():
174                 # Perform all steps for this image, including checking and
175                 # writing it. This means that errors found with a later
176                 # image will be reported after earlier images are already
177                 # completed and written, but that does not seem important.
178                 image.GetEntryContents()
179                 image.GetEntryOffsets()
180                 image.PackEntries()
181                 image.CheckSize()
182                 image.CheckEntries()
183                 image.SetImagePos()
184                 if options.update_fdt:
185                     image.SetCalculatedProperties()
186                 image.ProcessEntryContents()
187                 image.WriteSymbols()
188                 image.BuildImage()
189                 if options.map:
190                     image.WriteMap()
191             with open(fname, 'wb') as outfd:
192                 outfd.write(dtb.GetContents())
193         finally:
194             tools.FinaliseOutputDir()
195     finally:
196         tout.Uninit()
197
198     return 0