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