binman: Move state information into a new module
[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 sys
11 import tools
12
13 import command
14 import elf
15 from image import Image
16 import state
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 def _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
42 def _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
55 def 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
67 def 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         try:
110             tools.SetInputDirs(options.indir)
111             tools.PrepareOutputDir(options.outdir, options.preserve)
112             state.SetEntryArgs(options.entry_arg)
113
114             # Get the device tree ready by compiling it and copying the compiled
115             # output into a file in our output directly. Then scan it for use
116             # in binman.
117             dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
118             fname = tools.GetOutputFilename('u-boot-out.dtb')
119             with open(dtb_fname) as infd:
120                 with open(fname, 'wb') as outfd:
121                     outfd.write(infd.read())
122             dtb = fdt.FdtScan(fname)
123
124             # Note the file so that GetFdt() can find it
125             state.fdt_files['u-boot.dtb'] = dtb
126             node = _FindBinmanNode(dtb)
127             if not node:
128                 raise ValueError("Device tree '%s' does not have a 'binman' "
129                                  "node" % dtb_fname)
130
131             images = _ReadImageDesc(node)
132
133             if options.image:
134                 skip = []
135                 for name, image in images.iteritems():
136                     if name not in options.image:
137                         del images[name]
138                         skip.append(name)
139                 if skip:
140                     print 'Skipping images: %s\n' % ', '.join(skip)
141
142             # Prepare the device tree by making sure that any missing
143             # properties are added (e.g. 'pos' and 'size'). The values of these
144             # may not be correct yet, but we add placeholders so that the
145             # size of the device tree is correct. Later, in
146             # SetCalculatedProperties() we will insert the correct values
147             # without changing the device-tree size, thus ensuring that our
148             # entry offsets remain the same.
149             for image in images.values():
150                 if options.update_fdt:
151                     image.AddMissingProperties()
152                 image.ProcessFdt(dtb)
153
154             dtb.Sync(auto_resize=True)
155             dtb.Pack()
156             dtb.Flush()
157
158             for image in images.values():
159                 # Perform all steps for this image, including checking and
160                 # writing it. This means that errors found with a later
161                 # image will be reported after earlier images are already
162                 # completed and written, but that does not seem important.
163                 image.GetEntryContents()
164                 image.GetEntryOffsets()
165                 image.PackEntries()
166                 image.CheckSize()
167                 image.CheckEntries()
168                 image.SetImagePos()
169                 if options.update_fdt:
170                     image.SetCalculatedProperties()
171                     dtb.Sync()
172                 image.ProcessEntryContents()
173                 image.WriteSymbols()
174                 image.BuildImage()
175                 if options.map:
176                     image.WriteMap()
177             with open(fname, 'wb') as outfd:
178                 outfd.write(dtb.GetContents())
179         finally:
180             tools.FinaliseOutputDir()
181     finally:
182         tout.Uninit()
183
184     return 0