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 an image header which points to the FDT map
6
7This creates an 8-byte entry with a magic number and the offset of the FDT map
8(which is another entry in the image), relative to the start or end of the
9image.
10"""
11
12import struct
13
14from binman.entry import Entry
15from dtoc import fdt_util
16
17IMAGE_HEADER_MAGIC = b'BinM'
18IMAGE_HEADER_LEN   = 8
19
20def LocateHeaderOffset(data):
21    """Search an image for an image header
22
23    Args:
24        data: Data to search
25
26    Returns:
27        Offset of image header in the image, or None if not found
28    """
29    hdr_pos = data.find(IMAGE_HEADER_MAGIC)
30    if hdr_pos != -1:
31        size = len(data)
32        hdr = data[hdr_pos:hdr_pos + IMAGE_HEADER_LEN]
33        if len(hdr) == IMAGE_HEADER_LEN:
34            offset = struct.unpack('<I', hdr[4:])[0]
35            if hdr_pos == len(data) - IMAGE_HEADER_LEN:
36                pos = size + offset - (1 << 32)
37            else:
38                pos = offset
39            return pos
40    return None
41
42class Entry_image_header(Entry):
43    """An entry which contains a pointer to the FDT map
44
45    Properties / Entry arguments:
46        location: Location of header ("start" or "end" of image). This is
47            optional. If omitted then the entry must have an offset property.
48
49    This adds an 8-byte entry to the start or end of the image, pointing to the
50    location of the FDT map. The format is a magic number followed by an offset
51    from the start or end of the image, in twos-compliment format.
52
53    This entry must be in the top-level part of the image.
54
55    NOTE: If the location is at the start/end, you will probably need to specify
56    sort-by-offset for the image, unless you actually put the image header
57    first/last in the entry list.
58    """
59    def __init__(self, section, etype, node):
60        super().__init__(section, etype, node)
61        self.location = fdt_util.GetString(self._node, 'location')
62
63    def _GetHeader(self):
64        image_pos = self.GetSiblingImagePos('fdtmap')
65        if image_pos == False:
66            self.Raise("'image_header' section must have an 'fdtmap' sibling")
67        elif image_pos is None:
68            # This will be available when called from ProcessContents(), but not
69            # when called from ObtainContents()
70            offset = 0xffffffff
71        else:
72            image_size = self.section.GetImageSize() or 0
73            base = (0 if self.location != 'end' else image_size)
74            offset = (image_pos - base) & 0xffffffff
75        data = IMAGE_HEADER_MAGIC + struct.pack('<I', offset)
76        return data
77
78    def ObtainContents(self):
79        """Obtain a placeholder for the header contents"""
80        self.SetContents(self._GetHeader())
81        return True
82
83    def Pack(self, offset):
84        """Special pack method to set the offset to start/end of image"""
85        if not self.offset:
86            if self.location not in ['start', 'end']:
87                self.Raise("Invalid location '%s', expected 'start' or 'end'" %
88                           self.location)
89            order = self.GetSiblingOrder()
90            if self.location != order and not self.section.GetSort():
91                self.Raise("Invalid sibling order '%s' for image-header: Must be at '%s' to match location" %
92                           (order, self.location))
93            if self.location != 'end':
94                offset = 0
95            else:
96                image_size = self.section.GetImageSize()
97                if image_size is None:
98                    # We don't know the image, but this must be the last entry,
99                    # so we can assume it goes
100                    offset = offset
101                else:
102                    offset = image_size - IMAGE_HEADER_LEN
103        offset += self.section.GetStartOffset()
104        return super().Pack(offset)
105
106    def ProcessContents(self):
107        """Write an updated version of the FDT map to this entry
108
109        This is necessary since image_pos is not available when ObtainContents()
110        is called, since by then the entries have not been packed in the image.
111        """
112        return self.ProcessContentsUpdate(self._GetHeader())
113