Prepare v2023.10
[platform/kernel/u-boot.git] / tools / binman / etype / fdtmap.py
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
7 This handles putting an FDT into the image with just the information about the
8 image.
9 """
10
11 from binman.entry import Entry
12 from u_boot_pylib import tools
13 from u_boot_pylib import tout
14
15 FDTMAP_MAGIC   = b'_FDTMAP_'
16 FDTMAP_HDR_LEN = 16
17
18 # These is imported if needed
19 Fdt = None
20 libfdt = None
21 state = None
22
23 def 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
41 class 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:]