Prepare v2023.10
[platform/kernel/u-boot.git] / tools / binman / etype / mkimage.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # Entry-type module for producing an image using mkimage
6 #
7
8 from collections import OrderedDict
9
10 from binman.entry import Entry
11 from binman.etype.section import Entry_section
12 from dtoc import fdt_util
13 from u_boot_pylib import tools
14
15 class Entry_mkimage(Entry_section):
16     """Binary produced by mkimage
17
18     Properties / Entry arguments:
19         - args: Arguments to pass
20         - data-to-imagename: Indicates that the -d data should be passed in as
21           the image name also (-n)
22         - multiple-data-files: boolean to tell binman to pass all files as
23           datafiles to mkimage instead of creating a temporary file the result
24           of datafiles concatenation
25         - filename: filename of output binary generated by mkimage
26
27     The data passed to mkimage via the -d flag is collected from subnodes of the
28     mkimage node, e.g.::
29
30         mkimage {
31             filename = "imximage.bin";
32             args = "-n test -T imximage";
33
34             u-boot-spl {
35             };
36         };
37
38     This calls mkimage to create an imximage with `u-boot-spl.bin` as the data
39     file, with mkimage being called like this::
40
41         mkimage -d <data_file> -n test -T imximage <output_file>
42
43     The output from mkimage then becomes part of the image produced by
44     binman but also is written into `imximage.bin` file. If you need to put
45     multiple things in the data file, you can use a section, or just multiple
46     subnodes like this::
47
48         mkimage {
49             args = "-n test -T imximage";
50
51             u-boot-spl {
52             };
53
54             u-boot-tpl {
55             };
56         };
57
58     Note that binman places the contents (here SPL and TPL) into a single file
59     and passes that to mkimage using the -d option.
60
61     To pass all datafiles untouched to mkimage::
62
63         mkimage {
64                 args = "-n rk3399 -T rkspi";
65                 multiple-data-files;
66
67                 u-boot-tpl {
68                 };
69
70                 u-boot-spl {
71                 };
72         };
73
74     This calls mkimage to create a Rockchip RK3399-specific first stage
75     bootloader, made of TPL+SPL. Since this first stage bootloader requires to
76     align the TPL and SPL but also some weird hacks that is handled by mkimage
77     directly, binman is told to not perform the concatenation of datafiles prior
78     to passing the data to mkimage.
79
80     To use CONFIG options in the arguments, use a string list instead, as in
81     this example which also produces four arguments::
82
83         mkimage {
84             args = "-n", CONFIG_SYS_SOC, "-T imximage";
85
86             u-boot-spl {
87             };
88         };
89
90     If you need to pass the input data in with the -n argument as well, then use
91     the 'data-to-imagename' property::
92
93         mkimage {
94             args = "-T imximage";
95             data-to-imagename;
96
97             u-boot-spl {
98             };
99         };
100
101     That will pass the data to mkimage both as the data file (with -d) and as
102     the image name (with -n). In both cases, a filename is passed as the
103     argument, with the actual data being in that file.
104
105     If need to pass different data in with -n, then use an `imagename` subnode::
106
107         mkimage {
108             args = "-T imximage";
109
110             imagename {
111                 blob {
112                     filename = "spl/u-boot-spl.cfgout"
113                 };
114             };
115
116             u-boot-spl {
117             };
118         };
119
120     This will pass in u-boot-spl as the input data and the .cfgout file as the
121     -n data.
122     """
123     def __init__(self, section, etype, node):
124         super().__init__(section, etype, node)
125         self._imagename = None
126         self._multiple_data_files = False
127
128     def ReadNode(self):
129         super().ReadNode()
130         self._multiple_data_files = fdt_util.GetBool(self._node,
131                                                      'multiple-data-files')
132         self._args = fdt_util.GetArgs(self._node, 'args')
133         self._data_to_imagename = fdt_util.GetBool(self._node,
134                                                    'data-to-imagename')
135         if self._data_to_imagename and self._node.FindNode('imagename'):
136             self.Raise('Cannot use both imagename node and data-to-imagename')
137
138     def ReadEntries(self):
139         """Read the subnodes to find out what should go in this image"""
140         for node in self._node.subnodes:
141             if self.IsSpecialSubnode(node):
142                 continue
143             entry = Entry.Create(self, node,
144                                  expanded=self.GetImage().use_expanded,
145                                  missing_etype=self.GetImage().missing_etype)
146             entry.ReadNode()
147             entry.SetPrefix(self._name_prefix)
148             if entry.name == 'imagename':
149                 self._imagename = entry
150             else:
151                 self._entries[entry.name] = entry
152
153     def BuildSectionData(self, required):
154         """Build mkimage entry contents
155
156         Runs mkimage to build the entry contents
157
158         Args:
159             required (bool): True if the data must be present, False if it is OK
160                 to return None
161
162         Returns:
163             bytes: Contents of the section
164         """
165         # Use a non-zero size for any fake files to keep mkimage happy
166         # Note that testMkimageImagename() relies on this 'mkimage' parameter
167         fake_size = 1024
168         if self._multiple_data_files:
169             fnames = []
170             uniq = self.GetUniqueName()
171             for entry in self._entries.values():
172                 # Put the contents in a temporary file
173                 ename = f'mkimage-in-{uniq}-{entry.name}'
174                 fname = tools.get_output_filename(ename)
175                 data = entry.GetData(required)
176                 tools.write_file(fname, data)
177                 fnames.append(fname)
178             input_fname = ":".join(fnames)
179             data = b''
180         else:
181             data, input_fname, uniq = self.collect_contents_to_file(
182                 self._entries.values(), 'mkimage', fake_size)
183         if self._imagename:
184             image_data, imagename_fname, _ = self.collect_contents_to_file(
185                 [self._imagename], 'mkimage-n', 1024)
186         outfile = self._filename if self._filename else 'mkimage-out.%s' % uniq
187         output_fname = tools.get_output_filename(outfile)
188
189         missing_list = []
190         self.CheckMissing(missing_list)
191         self.missing = bool(missing_list)
192         if self.missing:
193             return b''
194
195         args = ['-d', input_fname]
196         if self._data_to_imagename:
197             args += ['-n', input_fname]
198         elif self._imagename:
199             args += ['-n', imagename_fname]
200         args += self._args + [output_fname]
201         if self.mkimage.run_cmd(*args) is not None:
202             return tools.read_file(output_fname)
203         else:
204             # Bintool is missing; just use the input data as the output
205             self.record_missing_bintool(self.mkimage)
206             return data
207
208     def GetEntries(self):
209         # Make a copy so we don't change the original
210         entries = OrderedDict(self._entries)
211         if self._imagename:
212             entries['imagename'] = self._imagename
213         return entries
214
215     def AddBintools(self, btools):
216         super().AddBintools(btools)
217         self.mkimage = self.AddBintool(btools, 'mkimage')
218
219     def CheckEntries(self):
220         pass
221
222     def ProcessContents(self):
223         # The blob may have changed due to WriteSymbols()
224         ok = super().ProcessContents()
225         data = self.BuildSectionData(True)
226         ok2 = self.ProcessContentsUpdate(data)
227         return ok and ok2
228
229     def SetImagePos(self, image_pos):
230         """Set the position in the image
231
232         This sets each subentry's offsets, sizes and positions-in-image
233         according to where they ended up in the packed mkimage file.
234
235         NOTE: This assumes a legacy mkimage and assumes that the images are
236         written to the output in order. SoC-specific mkimage handling may not
237         conform to this, in which case these values may be wrong.
238
239         Args:
240             image_pos (int): Position of this entry in the image
241         """
242         # The mkimage header consists of 0x40 bytes, following by a table of
243         # offsets for each file
244         upto = 0x40
245
246         # Skip the 0-terminated list of offsets (assume a single image)
247         upto += 4 + 4
248         for entry in self.GetEntries().values():
249             entry.SetOffsetSize(upto, None)
250
251             # Give up if any entries lack a size
252             if entry.size is None:
253                 return
254             upto += entry.size
255
256         super().SetImagePos(image_pos)