clk: fixed-rate: Enable DM_FLAG_PRE_RELOC flag
[platform/kernel/u-boot.git] / tools / binman / etype / fit.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 a FIT
6 #
7
8 from collections import defaultdict, OrderedDict
9 import libfdt
10
11 from binman.entry import Entry
12 from dtoc import fdt_util
13 from dtoc.fdt import Fdt
14 from patman import tools
15
16 class Entry_fit(Entry):
17     """Entry containing a FIT
18
19     This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the
20     input provided.
21
22     Nodes for the FIT should be written out in the binman configuration just as
23     they would be in a file passed to mkimage.
24
25     For example, this creates an image containing a FIT with U-Boot SPL:
26
27         binman {
28             fit {
29                 description = "Test FIT";
30
31                 images {
32                     kernel@1 {
33                         description = "SPL";
34                         os = "u-boot";
35                         type = "rkspi";
36                         arch = "arm";
37                         compression = "none";
38                         load = <0>;
39                         entry = <0>;
40
41                         u-boot-spl {
42                         };
43                     };
44                 };
45             };
46         };
47
48     Properties:
49         fit,external-offset: Indicates that the contents of the FIT are external
50             and provides the external offset. This is passsed to mkimage via
51             the -E and -p flags.
52
53     """
54     def __init__(self, section, etype, node):
55         """
56         Members:
57             _fit: FIT file being built
58             _fit_content: dict:
59                 key: relative path to entry Node (from the base of the FIT)
60                 value: List of Entry objects comprising the contents of this
61                     node
62         """
63         super().__init__(section, etype, node)
64         self._fit = None
65         self._fit_content = defaultdict(list)
66         self._fit_props = {}
67
68     def ReadNode(self):
69         self._ReadSubnodes()
70         super().ReadNode()
71
72     def _ReadSubnodes(self):
73         def _AddNode(base_node, depth, node):
74             """Add a node to the FIT
75
76             Args:
77                 base_node: Base Node of the FIT (with 'description' property)
78                 depth: Current node depth (0 is the base node)
79                 node: Current node to process
80
81             There are two cases to deal with:
82                 - hash and signature nodes which become part of the FIT
83                 - binman entries which are used to define the 'data' for each
84                   image
85             """
86             for pname, prop in node.props.items():
87                 if pname.startswith('fit,'):
88                     self._fit_props[pname] = prop
89                 else:
90                     fsw.property(pname, prop.bytes)
91
92             rel_path = node.path[len(base_node.path):]
93             has_images = depth == 2 and rel_path.startswith('/images/')
94             for subnode in node.subnodes:
95                 if has_images and not (subnode.name.startswith('hash') or
96                                        subnode.name.startswith('signature')):
97                     # This is a content node. We collect all of these together
98                     # and put them in the 'data' property. They do not appear
99                     # in the FIT.
100                     entry = Entry.Create(self.section, subnode)
101                     entry.ReadNode()
102                     self._fit_content[rel_path].append(entry)
103                 else:
104                     with fsw.add_node(subnode.name):
105                         _AddNode(base_node, depth + 1, subnode)
106
107         # Build a new tree with all nodes and properties starting from the
108         # entry node
109         fsw = libfdt.FdtSw()
110         fsw.finish_reservemap()
111         with fsw.add_node(''):
112             _AddNode(self._node, 0, self._node)
113         fdt = fsw.as_fdt()
114
115         # Pack this new FDT and scan it so we can add the data later
116         fdt.pack()
117         self._fdt = Fdt.FromData(fdt.as_bytearray())
118         self._fdt.Scan()
119
120     def ObtainContents(self):
121         """Obtain the contents of the FIT
122
123         This adds the 'data' properties to the input ITB (Image-tree Binary)
124         then runs mkimage to process it.
125         """
126         data = self._BuildInput(self._fdt)
127         if data == False:
128             return False
129         uniq = self.GetUniqueName()
130         input_fname = tools.GetOutputFilename('%s.itb' % uniq)
131         output_fname = tools.GetOutputFilename('%s.fit' % uniq)
132         tools.WriteFile(input_fname, data)
133         tools.WriteFile(output_fname, data)
134
135         args = []
136         ext_offset = self._fit_props.get('fit,external-offset')
137         if ext_offset is not None:
138             args += ['-E', '-p', '%x' % fdt_util.fdt32_to_cpu(ext_offset.value)]
139         tools.Run('mkimage', '-t', '-F', output_fname, *args)
140
141         self.SetContents(tools.ReadFile(output_fname))
142         return True
143
144     def _BuildInput(self, fdt):
145         """Finish the FIT by adding the 'data' properties to it
146
147         Arguments:
148             fdt: FIT to update
149
150         Returns:
151             New fdt contents (bytes)
152         """
153         for path, entries in self._fit_content.items():
154             node = fdt.GetNode(path)
155             data = b''
156             for entry in entries:
157                 if not entry.ObtainContents():
158                     return False
159                 data += entry.GetData()
160             node.AddData('data', data)
161
162         fdt.Sync(auto_resize=True)
163         data = fdt.GetContents()
164         return data