Merge branch 'master' of git://git.denx.de/u-boot-spi
[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 from image import Image
17 import tout
18
19 # List of images we plan to create
20 # Make this global so that it can be referenced from tests
21 images = OrderedDict()
22
23 # Records the device-tree files known to binman, keyed by filename (e.g.
24 # 'u-boot-spl.dtb')
25 fdt_files = {}
26
27 # Arguments passed to binman to provide arguments to entries
28 entry_args = {}
29
30
31 def _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
50 def _FindBinmanNode(dtb):
51     """Find the 'binman' node in the device tree
52
53     Args:
54         dtb: Fdt object to scan
55     Returns:
56         Node object of /binman node, or None if not found
57     """
58     for node in dtb.GetRoot().subnodes:
59         if node.name == 'binman':
60             return node
61     return None
62
63 def 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
78 def GetFdtPath(fname):
79     return fdt_files[fname]._fname
80
81 def 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
92 def GetEntryArg(name):
93     return entry_args.get(name)
94
95 def WriteEntryDocs(modules, test_missing=None):
96     from entry import Entry
97     Entry.WriteDocs(modules, test_missing)
98
99 def 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:
134         # 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
139         tout.Init(options.verbosity)
140         elf.debug = options.debug
141         try:
142             tools.SetInputDirs(options.indir)
143             tools.PrepareOutputDir(options.outdir, options.preserve)
144             SetEntryArgs(options.entry_arg)
145
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
158             node = _FindBinmanNode(dtb)
159             if not node:
160                 raise ValueError("Device tree '%s' does not have a 'binman' "
161                                  "node" % dtb_fname)
162
163             images = _ReadImageDesc(node)
164
165             # Prepare the device tree by making sure that any missing
166             # properties are added (e.g. 'pos' and 'size'). The values of these
167             # may not be correct yet, but we add placeholders so that the
168             # size of the device tree is correct. Later, in
169             # SetCalculatedProperties() we will insert the correct values
170             # without changing the device-tree size, thus ensuring that our
171             # entry offsets remain the same.
172             for image in images.values():
173                 if options.update_fdt:
174                     image.AddMissingProperties()
175                 image.ProcessFdt(dtb)
176
177             dtb.Pack()
178             dtb.Flush()
179
180             for image in images.values():
181                 # Perform all steps for this image, including checking and
182                 # writing it. This means that errors found with a later
183                 # image will be reported after earlier images are already
184                 # completed and written, but that does not seem important.
185                 image.GetEntryContents()
186                 image.GetEntryOffsets()
187                 image.PackEntries()
188                 image.CheckSize()
189                 image.CheckEntries()
190                 image.SetImagePos()
191                 if options.update_fdt:
192                     image.SetCalculatedProperties()
193                 image.ProcessEntryContents()
194                 image.WriteSymbols()
195                 image.BuildImage()
196                 if options.map:
197                     image.WriteMap()
198             with open(fname, 'wb') as outfd:
199                 outfd.write(dtb.GetContents())
200         finally:
201             tools.FinaliseOutputDir()
202     finally:
203         tout.Uninit()
204
205     return 0