| # SPDX-License-Identifier: GPL-2.0+ | |
| # Copyright (c) 2018 Google, Inc | |
| # Written by Simon Glass <sjg@chromium.org> | |
| """# Entry-type module for a full map of the firmware image | |
| This handles putting an FDT into the image with just the information about the | |
| image. | |
| """ | |
| from binman.entry import Entry | |
| from u_boot_pylib import tools | |
| from u_boot_pylib import tout | |
| FDTMAP_MAGIC = b'_FDTMAP_' | |
| FDTMAP_HDR_LEN = 16 | |
| # These is imported if needed | |
| Fdt = None | |
| libfdt = None | |
| state = None | |
| def LocateFdtmap(data): | |
| """Search an image for an fdt map | |
| Args: | |
| data: Data to search | |
| Returns: | |
| Position of fdt map in data, or None if not found. Note that the | |
| position returned is of the FDT header, i.e. before the FDT data | |
| """ | |
| hdr_pos = data.find(FDTMAP_MAGIC) | |
| size = len(data) | |
| if hdr_pos != -1: | |
| hdr = data[hdr_pos:hdr_pos + FDTMAP_HDR_LEN] | |
| if len(hdr) == FDTMAP_HDR_LEN: | |
| return hdr_pos | |
| return None | |
| class Entry_fdtmap(Entry): | |
| """An entry which contains an FDT map | |
| Properties / Entry arguments: | |
| None | |
| An FDT map is just a header followed by an FDT containing a list of all the | |
| entries in the image. The root node corresponds to the image node in the | |
| original FDT, and an image-name property indicates the image name in that | |
| original tree. | |
| The header is the string _FDTMAP_ followed by 8 unused bytes. | |
| When used, this entry will be populated with an FDT map which reflects the | |
| entries in the current image. Hierarchy is preserved, and all offsets and | |
| sizes are included. | |
| Note that the -u option must be provided to ensure that binman updates the | |
| FDT with the position of each entry. | |
| Example output for a simple image with U-Boot and an FDT map:: | |
| / { | |
| image-name = "binman"; | |
| size = <0x00000112>; | |
| image-pos = <0x00000000>; | |
| offset = <0x00000000>; | |
| u-boot { | |
| size = <0x00000004>; | |
| image-pos = <0x00000000>; | |
| offset = <0x00000000>; | |
| }; | |
| fdtmap { | |
| size = <0x0000010e>; | |
| image-pos = <0x00000004>; | |
| offset = <0x00000004>; | |
| }; | |
| }; | |
| If allow-repack is used then 'orig-offset' and 'orig-size' properties are | |
| added as necessary. See the binman README. | |
| When extracting files, an alternative 'fdt' format is available for fdtmaps. | |
| Use `binman extract -F fdt ...` to use this. It will export a devicetree, | |
| without the fdtmap header, so it can be viewed with `fdtdump`. | |
| """ | |
| def __init__(self, section, etype, node): | |
| # Put these here to allow entry-docs and help to work without libfdt | |
| global libfdt | |
| global state | |
| global Fdt | |
| import libfdt | |
| from binman import state | |
| from dtoc.fdt import Fdt | |
| super().__init__(section, etype, node) | |
| self.alt_formats = ['fdt'] | |
| def CheckAltFormats(self, alt_formats): | |
| alt_formats['fdt'] = self, 'Extract the devicetree blob from the fdtmap' | |
| def _GetFdtmap(self): | |
| """Build an FDT map from the entries in the current image | |
| Returns: | |
| FDT map binary data | |
| """ | |
| def _AddNode(node): | |
| """Add a node to the FDT map""" | |
| for pname, prop in node.props.items(): | |
| fsw.property(pname, prop.bytes) | |
| for subnode in node.subnodes: | |
| with fsw.add_node(subnode.name): | |
| _AddNode(subnode) | |
| data = state.GetFdtContents('fdtmap')[1] | |
| # If we have an fdtmap it means that we are using this as the | |
| # fdtmap for this image. | |
| if data is None: | |
| # Get the FDT data into an Fdt object | |
| data = state.GetFdtContents()[1] | |
| infdt = Fdt.FromData(data) | |
| infdt.Scan() | |
| # Find the node for the image containing the Fdt-map entry | |
| path = self.section.GetPath() | |
| self.Detail("Fdtmap: Using section '%s' (path '%s')" % | |
| (self.section.name, path)) | |
| node = infdt.GetNode(path) | |
| if not node: | |
| self.Raise("Internal error: Cannot locate node for path '%s'" % | |
| path) | |
| # Build a new tree with all nodes and properties starting from that | |
| # node | |
| fsw = libfdt.FdtSw() | |
| fsw.finish_reservemap() | |
| with fsw.add_node(''): | |
| fsw.property_string('image-node', node.name) | |
| _AddNode(node) | |
| fdt = fsw.as_fdt() | |
| # Pack this new FDT and return its contents | |
| fdt.pack() | |
| outfdt = Fdt.FromData(fdt.as_bytearray()) | |
| data = outfdt.GetContents() | |
| data = FDTMAP_MAGIC + tools.get_bytes(0, 8) + data | |
| return data | |
| def ObtainContents(self): | |
| """Obtain a placeholder for the fdt-map contents""" | |
| self.SetContents(self._GetFdtmap()) | |
| return True | |
| def ProcessContents(self): | |
| """Write an updated version of the FDT map to this entry | |
| This is necessary since new data may have been written back to it during | |
| processing, e.g. the image-pos properties. | |
| """ | |
| return self.ProcessContentsUpdate(self._GetFdtmap()) | |
| def GetAltFormat(self, data, alt_format): | |
| if alt_format == 'fdt': | |
| return data[FDTMAP_HDR_LEN:] |