xref: /openbmc/u-boot/tools/binman/control.py (revision 5396b2a9)
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
8from collections import OrderedDict
9import os
10import sys
11import tools
12
13import command
14import elf
15from image import Image
16import state
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
23def _ReadImageDesc(binman_node):
24    """Read the image descriptions from the /binman node
25
26    This normally produces a single Image object called 'image'. But if
27    multiple images are present, they will all be returned.
28
29    Args:
30        binman_node: Node object of the /binman node
31    Returns:
32        OrderedDict of Image objects, each of which describes an image
33    """
34    images = OrderedDict()
35    if 'multiple-images' in binman_node.props:
36        for node in binman_node.subnodes:
37            images[node.name] = Image(node.name, node)
38    else:
39        images['image'] = Image('image', binman_node)
40    return images
41
42def _FindBinmanNode(dtb):
43    """Find the 'binman' node in the device tree
44
45    Args:
46        dtb: Fdt object to scan
47    Returns:
48        Node object of /binman node, or None if not found
49    """
50    for node in dtb.GetRoot().subnodes:
51        if node.name == 'binman':
52            return node
53    return None
54
55def WriteEntryDocs(modules, test_missing=None):
56    """Write out documentation for all entries
57
58    Args:
59        modules: List of Module objects to get docs for
60        test_missing: Used for testing only, to force an entry's documeentation
61            to show as missing even if it is present. Should be set to None in
62            normal use.
63    """
64    from entry import Entry
65    Entry.WriteDocs(modules, test_missing)
66
67def Binman(options, args):
68    """The main control code for binman
69
70    This assumes that help and test options have already been dealt with. It
71    deals with the core task of building images.
72
73    Args:
74        options: Command line options object
75        args: Command line arguments (list of strings)
76    """
77    global images
78
79    if options.full_help:
80        pager = os.getenv('PAGER')
81        if not pager:
82            pager = 'more'
83        fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
84                            'README')
85        command.Run(pager, fname)
86        return 0
87
88    # Try to figure out which device tree contains our image description
89    if options.dt:
90        dtb_fname = options.dt
91    else:
92        board = options.board
93        if not board:
94            raise ValueError('Must provide a board to process (use -b <board>)')
95        board_pathname = os.path.join(options.build_dir, board)
96        dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
97        if not options.indir:
98            options.indir = ['.']
99        options.indir.append(board_pathname)
100
101    try:
102        # Import these here in case libfdt.py is not available, in which case
103        # the above help option still works.
104        import fdt
105        import fdt_util
106
107        tout.Init(options.verbosity)
108        elf.debug = options.debug
109        state.use_fake_dtb = options.fake_dtb
110        try:
111            tools.SetInputDirs(options.indir)
112            tools.PrepareOutputDir(options.outdir, options.preserve)
113            state.SetEntryArgs(options.entry_arg)
114
115            # Get the device tree ready by compiling it and copying the compiled
116            # output into a file in our output directly. Then scan it for use
117            # in binman.
118            dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
119            fname = tools.GetOutputFilename('u-boot.dtb.out')
120            tools.WriteFile(fname, tools.ReadFile(dtb_fname))
121            dtb = fdt.FdtScan(fname)
122
123            node = _FindBinmanNode(dtb)
124            if not node:
125                raise ValueError("Device tree '%s' does not have a 'binman' "
126                                 "node" % dtb_fname)
127
128            images = _ReadImageDesc(node)
129
130            if options.image:
131                skip = []
132                for name, image in images.iteritems():
133                    if name not in options.image:
134                        del images[name]
135                        skip.append(name)
136                if skip:
137                    print 'Skipping images: %s\n' % ', '.join(skip)
138
139            state.Prepare(images, dtb)
140
141            # Prepare the device tree by making sure that any missing
142            # properties are added (e.g. 'pos' and 'size'). The values of these
143            # may not be correct yet, but we add placeholders so that the
144            # size of the device tree is correct. Later, in
145            # SetCalculatedProperties() we will insert the correct values
146            # without changing the device-tree size, thus ensuring that our
147            # entry offsets remain the same.
148            for image in images.values():
149                image.ExpandEntries()
150                if options.update_fdt:
151                    image.AddMissingProperties()
152                image.ProcessFdt(dtb)
153
154            for dtb_item in state.GetFdts():
155                dtb_item.Sync(auto_resize=True)
156                dtb_item.Pack()
157                dtb_item.Flush()
158
159            for image in images.values():
160                # Perform all steps for this image, including checking and
161                # writing it. This means that errors found with a later
162                # image will be reported after earlier images are already
163                # completed and written, but that does not seem important.
164                image.GetEntryContents()
165                image.GetEntryOffsets()
166                try:
167                    image.PackEntries()
168                    image.CheckSize()
169                    image.CheckEntries()
170                except Exception as e:
171                    if options.map:
172                        fname = image.WriteMap()
173                        print "Wrote map file '%s' to show errors"  % fname
174                    raise
175                image.SetImagePos()
176                if options.update_fdt:
177                    image.SetCalculatedProperties()
178                    for dtb_item in state.GetFdts():
179                        dtb_item.Sync()
180                image.ProcessEntryContents()
181                image.WriteSymbols()
182                image.BuildImage()
183                if options.map:
184                    image.WriteMap()
185
186            # Write the updated FDTs to our output files
187            for dtb_item in state.GetFdts():
188                tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
189
190        finally:
191            tools.FinaliseOutputDir()
192    finally:
193        tout.Uninit()
194
195    return 0
196