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 U-Boot device tree files
6#
7
8from binman.entry import Entry
9from binman.etype.blob import Entry_blob
10from dtoc import fdt_util
11import struct
12
13# This is imported if needed
14state = None
15
16class Entry_blob_dtb(Entry_blob):
17    """A blob that holds a device tree
18
19    This is a blob containing a device tree. The contents of the blob are
20    obtained from the list of available device-tree files, managed by the
21    'state' module.
22
23    Additional attributes:
24        prepend: Header used (e.g. 'length')
25    """
26    def __init__(self, section, etype, node):
27        # Put this here to allow entry-docs and help to work without libfdt
28        global state
29        from binman import state
30
31        super().__init__(section, etype, node)
32        self.prepend = None
33
34    def ReadNode(self):
35        super().ReadNode()
36        self.prepend = fdt_util.GetString(self._node, 'prepend')
37        if self.prepend and self.prepend not in ['length']:
38            self.Raise("Invalid prepend in '%s': '%s'" %
39                       (self._node.name, self.prepend))
40
41    def ObtainContents(self, fake_size=0):
42        """Get the device-tree from the list held by the 'state' module"""
43        self._filename = self.GetDefaultFilename()
44        self._pathname, _ = state.GetFdtContents(self.GetFdtEtype())
45        return super().ReadBlobContents()
46
47    def ProcessContents(self):
48        """Re-read the DTB contents so that we get any calculated properties"""
49        _, indata = state.GetFdtContents(self.GetFdtEtype())
50
51        if self.compress == 'zstd' and self.prepend != 'length':
52            self.Raise('The zstd compression requires a length header')
53
54        data = self.CompressData(indata)
55        return self.ProcessContentsUpdate(data)
56
57    def GetFdtEtype(self):
58        """Get the entry type of this device tree
59
60        This can be 'u-boot-dtb', 'u-boot-spl-dtb' or 'u-boot-tpl-dtb'
61        Returns:
62            Entry type if any, e.g. 'u-boot-dtb'
63        """
64        return None
65
66    def GetFdts(self):
67        fname = self.GetDefaultFilename()
68        return {self.GetFdtEtype(): [self, fname]}
69
70    def WriteData(self, data, decomp=True):
71        ok = super().WriteData(data, decomp)
72
73        # Update the state module, since it has the authoritative record of the
74        # device trees used. If we don't do this, then state.GetFdtContents()
75        # will still return the old contents
76        state.UpdateFdtContents(self.GetFdtEtype(), data)
77        return ok
78
79    def CompressData(self, indata):
80        data = super().CompressData(indata)
81        if self.prepend == 'length':
82            hdr = struct.pack('<I', len(data))
83            data = hdr + data
84        return data
85
86    def DecompressData(self, indata):
87        if self.prepend == 'length':
88            data_len = struct.unpack('<I', indata[:4])[0]
89            indata = indata[4:4 + data_len]
90        data = super().DecompressData(indata)
91        return data
92