xref: /openbmc/u-boot/tools/binman/control.py (revision 09d84117)
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 re
11import sys
12import tools
13
14import command
15import elf
16from 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
23# Records the device-tree files known to binman, keyed by filename (e.g.
24# 'u-boot-spl.dtb')
25fdt_files = {}
26
27# Arguments passed to binman to provide arguments to entries
28entry_args = {}
29
30
31def _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
50def _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
63def 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
81def 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
95def WriteEntryDocs(modules, test_missing=None):
96    from entry import Entry
97    Entry.WriteDocs(modules, test_missing)
98
99def 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
206