1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
5 # Entry-type module for producing a FIT
8 from collections import defaultdict, OrderedDict
11 from binman.entry import Entry
12 from dtoc import fdt_util
13 from dtoc.fdt import Fdt
14 from patman import tools
16 class Entry_fit(Entry):
17 """Entry containing a FIT
19 This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the
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.
25 For example, this creates an image containing a FIT with U-Boot SPL:
29 description = "Test FIT";
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
54 def __init__(self, section, etype, node):
57 _fit: FIT file being built
59 key: relative path to entry Node (from the base of the FIT)
60 value: List of Entry objects comprising the contents of this
63 super().__init__(section, etype, node)
65 self._fit_content = defaultdict(list)
72 def _ReadSubnodes(self):
73 def _AddNode(base_node, depth, node):
74 """Add a node to the FIT
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
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
86 for pname, prop in node.props.items():
87 if pname.startswith('fit,'):
88 self._fit_props[pname] = prop
90 fsw.property(pname, prop.bytes)
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
100 entry = Entry.Create(self.section, subnode)
102 self._fit_content[rel_path].append(entry)
104 with fsw.add_node(subnode.name):
105 _AddNode(base_node, depth + 1, subnode)
107 # Build a new tree with all nodes and properties starting from the
110 fsw.finish_reservemap()
111 with fsw.add_node(''):
112 _AddNode(self._node, 0, self._node)
115 # Pack this new FDT and scan it so we can add the data later
117 self._fdt = Fdt.FromData(fdt.as_bytearray())
120 def ObtainContents(self):
121 """Obtain the contents of the FIT
123 This adds the 'data' properties to the input ITB (Image-tree Binary)
124 then runs mkimage to process it.
126 data = self._BuildInput(self._fdt)
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)
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)
141 self.SetContents(tools.ReadFile(output_fname))
144 def _BuildInput(self, fdt):
145 """Finish the FIT by adding the 'data' properties to it
151 New fdt contents (bytes)
153 for path, entries in self._fit_content.items():
154 node = fdt.GetNode(path)
156 for entry in entries:
157 if not entry.ObtainContents():
159 data += entry.GetData()
160 node.AddData('data', data)
162 fdt.Sync(auto_resize=True)
163 data = fdt.GetContents()