1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2018 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4
5"""# Entry-type module for a full map of the firmware image
6
7This handles putting an FDT into the image with just the information about the
8image.
9"""
10
11from binman.entry import Entry
12from u_boot_pylib import tools
13from u_boot_pylib import tout
14
15FDTMAP_MAGIC   = b'_FDTMAP_'
16FDTMAP_HDR_LEN = 16
17
18# These is imported if needed
19Fdt = None
20libfdt = None
21state = None
22
23def LocateFdtmap(data):
24    """Search an image for an fdt map
25
26    Args:
27        data: Data to search
28
29    Returns:
30        Position of fdt map in data, or None if not found. Note that the
31            position returned is of the FDT header, i.e. before the FDT data
32    """
33    hdr_pos = data.find(FDTMAP_MAGIC)
34    size = len(data)
35    if hdr_pos != -1:
36        hdr = data[hdr_pos:hdr_pos + FDTMAP_HDR_LEN]
37        if len(hdr) == FDTMAP_HDR_LEN:
38            return hdr_pos
39    return None
40
41class Entry_fdtmap(Entry):
42    """An entry which contains an FDT map
43
44    Properties / Entry arguments:
45        None
46
47    An FDT map is just a header followed by an FDT containing a list of all the
48    entries in the image. The root node corresponds to the image node in the
49    original FDT, and an image-name property indicates the image name in that
50    original tree.
51
52    The header is the string _FDTMAP_ followed by 8 unused bytes.
53
54    When used, this entry will be populated with an FDT map which reflects the
55    entries in the current image. Hierarchy is preserved, and all offsets and
56    sizes are included.
57
58    Note that the -u option must be provided to ensure that binman updates the
59    FDT with the position of each entry.
60
61    Example output for a simple image with U-Boot and an FDT map::
62
63        / {
64            image-name = "binman";
65            size = <0x00000112>;
66            image-pos = <0x00000000>;
67            offset = <0x00000000>;
68            u-boot {
69                size = <0x00000004>;
70                image-pos = <0x00000000>;
71                offset = <0x00000000>;
72            };
73            fdtmap {
74                size = <0x0000010e>;
75                image-pos = <0x00000004>;
76                offset = <0x00000004>;
77            };
78        };
79
80    If allow-repack is used then 'orig-offset' and 'orig-size' properties are
81    added as necessary. See the binman README.
82
83    When extracting files, an alternative 'fdt' format is available for fdtmaps.
84    Use `binman extract -F fdt ...` to use this. It will export a devicetree,
85    without the fdtmap header, so it can be viewed with `fdtdump`.
86    """
87    def __init__(self, section, etype, node):
88        # Put these here to allow entry-docs and help to work without libfdt
89        global libfdt
90        global state
91        global Fdt
92
93        import libfdt
94        from binman import state
95        from dtoc.fdt import Fdt
96
97        super().__init__(section, etype, node)
98        self.alt_formats = ['fdt']
99
100    def CheckAltFormats(self, alt_formats):
101        alt_formats['fdt'] = self, 'Extract the devicetree blob from the fdtmap'
102
103    def _GetFdtmap(self):
104        """Build an FDT map from the entries in the current image
105
106        Returns:
107            FDT map binary data
108        """
109        def _AddNode(node):
110            """Add a node to the FDT map"""
111            for pname, prop in node.props.items():
112                fsw.property(pname, prop.bytes)
113            for subnode in node.subnodes:
114                with fsw.add_node(subnode.name):
115                    _AddNode(subnode)
116
117        data = state.GetFdtContents('fdtmap')[1]
118        # If we have an fdtmap it means that we are using this as the
119        # fdtmap for this image.
120        if data is None:
121            # Get the FDT data into an Fdt object
122            data = state.GetFdtContents()[1]
123            infdt = Fdt.FromData(data)
124            infdt.Scan()
125
126            # Find the node for the image containing the Fdt-map entry
127            path = self.section.GetPath()
128            self.Detail("Fdtmap: Using section '%s' (path '%s')" %
129                        (self.section.name, path))
130            node = infdt.GetNode(path)
131            if not node:
132                self.Raise("Internal error: Cannot locate node for path '%s'" %
133                           path)
134
135            # Build a new tree with all nodes and properties starting from that
136            # node
137            fsw = libfdt.FdtSw()
138            fsw.finish_reservemap()
139            with fsw.add_node(''):
140                fsw.property_string('image-node', node.name)
141                _AddNode(node)
142            fdt = fsw.as_fdt()
143
144            # Pack this new FDT and return its contents
145            fdt.pack()
146            outfdt = Fdt.FromData(fdt.as_bytearray())
147            data = outfdt.GetContents()
148        data = FDTMAP_MAGIC + tools.get_bytes(0, 8) + data
149        return data
150
151    def ObtainContents(self):
152        """Obtain a placeholder for the fdt-map contents"""
153        self.SetContents(self._GetFdtmap())
154        return True
155
156    def ProcessContents(self):
157        """Write an updated version of the FDT map to this entry
158
159        This is necessary since new data may have been written back to it during
160        processing, e.g. the image-pos properties.
161        """
162        return self.ProcessContentsUpdate(self._GetFdtmap())
163
164    def GetAltFormat(self, data, alt_format):
165        if alt_format == 'fdt':
166            return data[FDTMAP_HDR_LEN:]
167