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