xref: /rk3399_rockchip-uboot/tools/dtoc/fdt.py (revision 7b75b4482ddee4ea8248f1f64f2b0d1c752dd273)
1a06a34b2SSimon Glass#!/usr/bin/python
2a06a34b2SSimon Glass#
3a06a34b2SSimon Glass# Copyright (C) 2016 Google, Inc
4a06a34b2SSimon Glass# Written by Simon Glass <sjg@chromium.org>
5a06a34b2SSimon Glass#
6a06a34b2SSimon Glass# SPDX-License-Identifier:      GPL-2.0+
7a06a34b2SSimon Glass#
8a06a34b2SSimon Glass
9a06a34b2SSimon Glassimport struct
10a06a34b2SSimon Glassimport sys
11a06a34b2SSimon Glass
12a06a34b2SSimon Glassimport fdt_util
13*7b75b448SSimon Glassimport libfdt
14a06a34b2SSimon Glass
15a06a34b2SSimon Glass# This deals with a device tree, presenting it as an assortment of Node and
16a06a34b2SSimon Glass# Prop objects, representing nodes and properties, respectively. This file
17*7b75b448SSimon Glass# contains the base classes and defines the high-level API. See fdt_select.py
18*7b75b448SSimon Glass# for how to create an Fdt object.
19*7b75b448SSimon Glass
20*7b75b448SSimon Glass# This implementation uses a libfdt Python library to access the device tree,
21*7b75b448SSimon Glass# so it is fairly efficient.
22a06a34b2SSimon Glass
23bc1dea36SSimon Glass# A list of types we support
24bc1dea36SSimon Glass(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4)
25bc1dea36SSimon Glass
26a06a34b2SSimon Glassdef CheckErr(errnum, msg):
27a06a34b2SSimon Glass    if errnum:
28a06a34b2SSimon Glass        raise ValueError('Error %d: %s: %s' %
29a06a34b2SSimon Glass            (errnum, libfdt.fdt_strerror(errnum), msg))
30a06a34b2SSimon Glass
31*7b75b448SSimon Glassclass Prop:
32a06a34b2SSimon Glass    """A device tree property
33a06a34b2SSimon Glass
34a06a34b2SSimon Glass    Properties:
35a06a34b2SSimon Glass        name: Property name (as per the device tree)
36a06a34b2SSimon Glass        value: Property value as a string of bytes, or a list of strings of
37a06a34b2SSimon Glass            bytes
38a06a34b2SSimon Glass        type: Value type
39a06a34b2SSimon Glass    """
40*7b75b448SSimon Glass    def __init__(self, node, offset, name, bytes):
41a06a34b2SSimon Glass        self._node = node
42a06a34b2SSimon Glass        self._offset = offset
43a06a34b2SSimon Glass        self.name = name
44a06a34b2SSimon Glass        self.value = None
45*7b75b448SSimon Glass        self.bytes = str(bytes)
46*7b75b448SSimon Glass        if not bytes:
47*7b75b448SSimon Glass            self.type = TYPE_BOOL
48*7b75b448SSimon Glass            self.value = True
49*7b75b448SSimon Glass            return
50*7b75b448SSimon Glass        self.type, self.value = self.BytesToValue(bytes)
51a06a34b2SSimon Glass
52c322a850SSimon Glass    def GetPhandle(self):
53c322a850SSimon Glass        """Get a (single) phandle value from a property
54c322a850SSimon Glass
55c322a850SSimon Glass        Gets the phandle valuie from a property and returns it as an integer
56c322a850SSimon Glass        """
57c322a850SSimon Glass        return fdt_util.fdt32_to_cpu(self.value[:4])
58c322a850SSimon Glass
59c322a850SSimon Glass    def Widen(self, newprop):
60c322a850SSimon Glass        """Figure out which property type is more general
61c322a850SSimon Glass
62c322a850SSimon Glass        Given a current property and a new property, this function returns the
63c322a850SSimon Glass        one that is less specific as to type. The less specific property will
64c322a850SSimon Glass        be ble to represent the data in the more specific property. This is
65c322a850SSimon Glass        used for things like:
66c322a850SSimon Glass
67c322a850SSimon Glass            node1 {
68c322a850SSimon Glass                compatible = "fred";
69c322a850SSimon Glass                value = <1>;
70c322a850SSimon Glass            };
71c322a850SSimon Glass            node1 {
72c322a850SSimon Glass                compatible = "fred";
73c322a850SSimon Glass                value = <1 2>;
74c322a850SSimon Glass            };
75c322a850SSimon Glass
76c322a850SSimon Glass        He we want to use an int array for 'value'. The first property
77c322a850SSimon Glass        suggests that a single int is enough, but the second one shows that
78c322a850SSimon Glass        it is not. Calling this function with these two propertes would
79c322a850SSimon Glass        update the current property to be like the second, since it is less
80c322a850SSimon Glass        specific.
81c322a850SSimon Glass        """
82c322a850SSimon Glass        if newprop.type < self.type:
83c322a850SSimon Glass            self.type = newprop.type
84c322a850SSimon Glass
85c322a850SSimon Glass        if type(newprop.value) == list and type(self.value) != list:
86c322a850SSimon Glass            self.value = [self.value]
87c322a850SSimon Glass
88c322a850SSimon Glass        if type(self.value) == list and len(newprop.value) > len(self.value):
89c322a850SSimon Glass            val = self.GetEmpty(self.type)
90c322a850SSimon Glass            while len(self.value) < len(newprop.value):
91c322a850SSimon Glass                self.value.append(val)
92c322a850SSimon Glass
93bc1dea36SSimon Glass    def BytesToValue(self, bytes):
94bc1dea36SSimon Glass        """Converts a string of bytes into a type and value
95bc1dea36SSimon Glass
96bc1dea36SSimon Glass        Args:
97bc1dea36SSimon Glass            A string containing bytes
98bc1dea36SSimon Glass
99bc1dea36SSimon Glass        Return:
100bc1dea36SSimon Glass            A tuple:
101bc1dea36SSimon Glass                Type of data
102bc1dea36SSimon Glass                Data, either a single element or a list of elements. Each element
103bc1dea36SSimon Glass                is one of:
104bc1dea36SSimon Glass                    TYPE_STRING: string value from the property
105bc1dea36SSimon Glass                    TYPE_INT: a byte-swapped integer stored as a 4-byte string
106bc1dea36SSimon Glass                    TYPE_BYTE: a byte stored as a single-byte string
107bc1dea36SSimon Glass        """
108b4360206SSimon Glass        bytes = str(bytes)
109bc1dea36SSimon Glass        size = len(bytes)
110bc1dea36SSimon Glass        strings = bytes.split('\0')
111bc1dea36SSimon Glass        is_string = True
112bc1dea36SSimon Glass        count = len(strings) - 1
113bc1dea36SSimon Glass        if count > 0 and not strings[-1]:
114bc1dea36SSimon Glass            for string in strings[:-1]:
115bc1dea36SSimon Glass                if not string:
116bc1dea36SSimon Glass                    is_string = False
117bc1dea36SSimon Glass                    break
118bc1dea36SSimon Glass                for ch in string:
119bc1dea36SSimon Glass                    if ch < ' ' or ch > '~':
120bc1dea36SSimon Glass                        is_string = False
121bc1dea36SSimon Glass                        break
122bc1dea36SSimon Glass        else:
123bc1dea36SSimon Glass            is_string = False
124bc1dea36SSimon Glass        if is_string:
125bc1dea36SSimon Glass            if count == 1:
126bc1dea36SSimon Glass                return TYPE_STRING, strings[0]
127bc1dea36SSimon Glass            else:
128bc1dea36SSimon Glass                return TYPE_STRING, strings[:-1]
129bc1dea36SSimon Glass        if size % 4:
130bc1dea36SSimon Glass            if size == 1:
131bc1dea36SSimon Glass                return TYPE_BYTE, bytes[0]
132bc1dea36SSimon Glass            else:
133bc1dea36SSimon Glass                return TYPE_BYTE, list(bytes)
134bc1dea36SSimon Glass        val = []
135bc1dea36SSimon Glass        for i in range(0, size, 4):
136bc1dea36SSimon Glass            val.append(bytes[i:i + 4])
137bc1dea36SSimon Glass        if size == 4:
138bc1dea36SSimon Glass            return TYPE_INT, val[0]
139bc1dea36SSimon Glass        else:
140bc1dea36SSimon Glass            return TYPE_INT, val
141bc1dea36SSimon Glass
142bc1dea36SSimon Glass    def GetEmpty(self, type):
143bc1dea36SSimon Glass        """Get an empty / zero value of the given type
144bc1dea36SSimon Glass
145bc1dea36SSimon Glass        Returns:
146bc1dea36SSimon Glass            A single value of the given type
147bc1dea36SSimon Glass        """
148bc1dea36SSimon Glass        if type == TYPE_BYTE:
149bc1dea36SSimon Glass            return chr(0)
150bc1dea36SSimon Glass        elif type == TYPE_INT:
151bc1dea36SSimon Glass            return struct.pack('<I', 0);
152bc1dea36SSimon Glass        elif type == TYPE_STRING:
153bc1dea36SSimon Glass            return ''
154bc1dea36SSimon Glass        else:
155bc1dea36SSimon Glass            return True
156bc1dea36SSimon Glass
157babdbde6SSimon Glass    def GetOffset(self):
158babdbde6SSimon Glass        """Get the offset of a property
159babdbde6SSimon Glass
160babdbde6SSimon Glass        Returns:
161*7b75b448SSimon Glass            The offset of the property (struct fdt_property) within the file
162babdbde6SSimon Glass        """
163*7b75b448SSimon Glass        return self._node._fdt.GetStructOffset(self._offset)
164babdbde6SSimon Glass
165*7b75b448SSimon Glassclass Node:
166a06a34b2SSimon Glass    """A device tree node
167a06a34b2SSimon Glass
168a06a34b2SSimon Glass    Properties:
169a06a34b2SSimon Glass        offset: Integer offset in the device tree
170a06a34b2SSimon Glass        name: Device tree node tname
171a06a34b2SSimon Glass        path: Full path to node, along with the node name itself
172a06a34b2SSimon Glass        _fdt: Device tree object
173a06a34b2SSimon Glass        subnodes: A list of subnodes for this node, each a Node object
174a06a34b2SSimon Glass        props: A dict of properties for this node, each a Prop object.
175a06a34b2SSimon Glass            Keyed by property name
176a06a34b2SSimon Glass    """
177a06a34b2SSimon Glass    def __init__(self, fdt, offset, name, path):
178a06a34b2SSimon Glass        self._fdt = fdt
179a06a34b2SSimon Glass        self._offset = offset
180a06a34b2SSimon Glass        self.name = name
181a06a34b2SSimon Glass        self.path = path
182a06a34b2SSimon Glass        self.subnodes = []
183a06a34b2SSimon Glass        self.props = {}
184a06a34b2SSimon Glass
185f7a2aeeeSSimon Glass    def _FindNode(self, name):
186f7a2aeeeSSimon Glass        """Find a node given its name
187f7a2aeeeSSimon Glass
188f7a2aeeeSSimon Glass        Args:
189f7a2aeeeSSimon Glass            name: Node name to look for
190f7a2aeeeSSimon Glass        Returns:
191f7a2aeeeSSimon Glass            Node object if found, else None
192f7a2aeeeSSimon Glass        """
193f7a2aeeeSSimon Glass        for subnode in self.subnodes:
194f7a2aeeeSSimon Glass            if subnode.name == name:
195f7a2aeeeSSimon Glass                return subnode
196f7a2aeeeSSimon Glass        return None
197f7a2aeeeSSimon Glass
198*7b75b448SSimon Glass    def Offset(self):
199*7b75b448SSimon Glass        """Returns the offset of a node, after checking the cache
200f7a2aeeeSSimon Glass
201*7b75b448SSimon Glass        This should be used instead of self._offset directly, to ensure that
202*7b75b448SSimon Glass        the cache does not contain invalid offsets.
203f7a2aeeeSSimon Glass        """
204*7b75b448SSimon Glass        self._fdt.CheckCache()
205*7b75b448SSimon Glass        return self._offset
206*7b75b448SSimon Glass
207*7b75b448SSimon Glass    def Scan(self):
208*7b75b448SSimon Glass        """Scan a node's properties and subnodes
209*7b75b448SSimon Glass
210*7b75b448SSimon Glass        This fills in the props and subnodes properties, recursively
211*7b75b448SSimon Glass        searching into subnodes so that the entire tree is built.
212*7b75b448SSimon Glass        """
213*7b75b448SSimon Glass        self.props = self._fdt.GetProps(self)
214*7b75b448SSimon Glass
215*7b75b448SSimon Glass        offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
216*7b75b448SSimon Glass        while offset >= 0:
217*7b75b448SSimon Glass            sep = '' if self.path[-1] == '/' else '/'
218*7b75b448SSimon Glass            name = self._fdt._fdt_obj.get_name(offset)
219*7b75b448SSimon Glass            path = self.path + sep + name
220*7b75b448SSimon Glass            node = Node(self._fdt, offset, name, path)
221*7b75b448SSimon Glass            self.subnodes.append(node)
222*7b75b448SSimon Glass
223*7b75b448SSimon Glass            node.Scan()
224*7b75b448SSimon Glass            offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
225*7b75b448SSimon Glass
226*7b75b448SSimon Glass    def Refresh(self, my_offset):
227*7b75b448SSimon Glass        """Fix up the _offset for each node, recursively
228*7b75b448SSimon Glass
229*7b75b448SSimon Glass        Note: This does not take account of property offsets - these will not
230*7b75b448SSimon Glass        be updated.
231*7b75b448SSimon Glass        """
232*7b75b448SSimon Glass        if self._offset != my_offset:
233*7b75b448SSimon Glass            #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
234*7b75b448SSimon Glass            self._offset = my_offset
235*7b75b448SSimon Glass        offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
236*7b75b448SSimon Glass        for subnode in self.subnodes:
237*7b75b448SSimon Glass            subnode.Refresh(offset)
238*7b75b448SSimon Glass            offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
239f7a2aeeeSSimon Glass
2402a70d897SSimon Glass    def DeleteProp(self, prop_name):
2412a70d897SSimon Glass        """Delete a property of a node
2422a70d897SSimon Glass
243*7b75b448SSimon Glass        The property is deleted and the offset cache is invalidated.
2442a70d897SSimon Glass
2452a70d897SSimon Glass        Args:
2462a70d897SSimon Glass            prop_name: Name of the property to delete
247*7b75b448SSimon Glass        Raises:
248*7b75b448SSimon Glass            ValueError if the property does not exist
2492a70d897SSimon Glass        """
250*7b75b448SSimon Glass        CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
251*7b75b448SSimon Glass                 "Node '%s': delete property: '%s'" % (self.path, prop_name))
252*7b75b448SSimon Glass        del self.props[prop_name]
253*7b75b448SSimon Glass        self._fdt.Invalidate()
2542a70d897SSimon Glass
255a06a34b2SSimon Glassclass Fdt:
256*7b75b448SSimon Glass    """Provides simple access to a flat device tree blob using libfdts.
257a06a34b2SSimon Glass
258a06a34b2SSimon Glass    Properties:
259a06a34b2SSimon Glass      fname: Filename of fdt
260a06a34b2SSimon Glass      _root: Root of device tree (a Node object)
261a06a34b2SSimon Glass    """
262a06a34b2SSimon Glass    def __init__(self, fname):
263a06a34b2SSimon Glass        self._fname = fname
264*7b75b448SSimon Glass        self._cached_offsets = False
265*7b75b448SSimon Glass        if self._fname:
266*7b75b448SSimon Glass            self._fname = fdt_util.EnsureCompiled(self._fname)
267*7b75b448SSimon Glass
268*7b75b448SSimon Glass            with open(self._fname) as fd:
269*7b75b448SSimon Glass                self._fdt = bytearray(fd.read())
270*7b75b448SSimon Glass                self._fdt_obj = libfdt.Fdt(self._fdt)
271f7a2aeeeSSimon Glass
272f7a2aeeeSSimon Glass    def Scan(self, root='/'):
273f7a2aeeeSSimon Glass        """Scan a device tree, building up a tree of Node objects
274f7a2aeeeSSimon Glass
275f7a2aeeeSSimon Glass        This fills in the self._root property
276f7a2aeeeSSimon Glass
277f7a2aeeeSSimon Glass        Args:
278f7a2aeeeSSimon Glass            root: Ignored
279f7a2aeeeSSimon Glass
280f7a2aeeeSSimon Glass        TODO(sjg@chromium.org): Implement the 'root' parameter
281f7a2aeeeSSimon Glass        """
282f7a2aeeeSSimon Glass        self._root = self.Node(self, 0, '/', '/')
283f7a2aeeeSSimon Glass        self._root.Scan()
284f7a2aeeeSSimon Glass
285f7a2aeeeSSimon Glass    def GetRoot(self):
286f7a2aeeeSSimon Glass        """Get the root Node of the device tree
287f7a2aeeeSSimon Glass
288f7a2aeeeSSimon Glass        Returns:
289f7a2aeeeSSimon Glass            The root Node object
290f7a2aeeeSSimon Glass        """
291f7a2aeeeSSimon Glass        return self._root
292f7a2aeeeSSimon Glass
293f7a2aeeeSSimon Glass    def GetNode(self, path):
294f7a2aeeeSSimon Glass        """Look up a node from its path
295f7a2aeeeSSimon Glass
296f7a2aeeeSSimon Glass        Args:
297f7a2aeeeSSimon Glass            path: Path to look up, e.g. '/microcode/update@0'
298f7a2aeeeSSimon Glass        Returns:
299f7a2aeeeSSimon Glass            Node object, or None if not found
300f7a2aeeeSSimon Glass        """
301f7a2aeeeSSimon Glass        node = self._root
302f7a2aeeeSSimon Glass        for part in path.split('/')[1:]:
303f7a2aeeeSSimon Glass            node = node._FindNode(part)
304f7a2aeeeSSimon Glass            if not node:
305f7a2aeeeSSimon Glass                return None
306f7a2aeeeSSimon Glass        return node
307f7a2aeeeSSimon Glass
308da5f7499SSimon Glass    def Flush(self):
309da5f7499SSimon Glass        """Flush device tree changes back to the file
310da5f7499SSimon Glass
311da5f7499SSimon Glass        If the device tree has changed in memory, write it back to the file.
312da5f7499SSimon Glass        """
313*7b75b448SSimon Glass        with open(self._fname, 'wb') as fd:
314*7b75b448SSimon Glass            fd.write(self._fdt)
315da5f7499SSimon Glass
316da5f7499SSimon Glass    def Pack(self):
317da5f7499SSimon Glass        """Pack the device tree down to its minimum size
318da5f7499SSimon Glass
319da5f7499SSimon Glass        When nodes and properties shrink or are deleted, wasted space can
320*7b75b448SSimon Glass        build up in the device tree binary.
321da5f7499SSimon Glass        """
322*7b75b448SSimon Glass        CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
323*7b75b448SSimon Glass        fdt_len = libfdt.fdt_totalsize(self._fdt)
324*7b75b448SSimon Glass        del self._fdt[fdt_len:]
325*7b75b448SSimon Glass
326*7b75b448SSimon Glass    def GetFdt(self):
327*7b75b448SSimon Glass        """Get the contents of the FDT
328*7b75b448SSimon Glass
329*7b75b448SSimon Glass        Returns:
330*7b75b448SSimon Glass            The FDT contents as a string of bytes
331*7b75b448SSimon Glass        """
332*7b75b448SSimon Glass        return self._fdt
333*7b75b448SSimon Glass
334*7b75b448SSimon Glass    def CheckErr(errnum, msg):
335*7b75b448SSimon Glass        if errnum:
336*7b75b448SSimon Glass            raise ValueError('Error %d: %s: %s' %
337*7b75b448SSimon Glass                (errnum, libfdt.fdt_strerror(errnum), msg))
338*7b75b448SSimon Glass
339*7b75b448SSimon Glass
340*7b75b448SSimon Glass    def GetProps(self, node):
341*7b75b448SSimon Glass        """Get all properties from a node.
342*7b75b448SSimon Glass
343*7b75b448SSimon Glass        Args:
344*7b75b448SSimon Glass            node: Full path to node name to look in.
345*7b75b448SSimon Glass
346*7b75b448SSimon Glass        Returns:
347*7b75b448SSimon Glass            A dictionary containing all the properties, indexed by node name.
348*7b75b448SSimon Glass            The entries are Prop objects.
349*7b75b448SSimon Glass
350*7b75b448SSimon Glass        Raises:
351*7b75b448SSimon Glass            ValueError: if the node does not exist.
352*7b75b448SSimon Glass        """
353*7b75b448SSimon Glass        props_dict = {}
354*7b75b448SSimon Glass        poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
355*7b75b448SSimon Glass        while poffset >= 0:
356*7b75b448SSimon Glass            p = self._fdt_obj.get_property_by_offset(poffset)
357*7b75b448SSimon Glass            prop = Prop(node, poffset, p.name, p.value)
358*7b75b448SSimon Glass            props_dict[prop.name] = prop
359*7b75b448SSimon Glass
360*7b75b448SSimon Glass            poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
361*7b75b448SSimon Glass        return props_dict
362*7b75b448SSimon Glass
363*7b75b448SSimon Glass    def Invalidate(self):
364*7b75b448SSimon Glass        """Mark our offset cache as invalid"""
365*7b75b448SSimon Glass        self._cached_offsets = False
366*7b75b448SSimon Glass
367*7b75b448SSimon Glass    def CheckCache(self):
368*7b75b448SSimon Glass        """Refresh the offset cache if needed"""
369*7b75b448SSimon Glass        if self._cached_offsets:
370*7b75b448SSimon Glass            return
371*7b75b448SSimon Glass        self.Refresh()
372*7b75b448SSimon Glass        self._cached_offsets = True
373*7b75b448SSimon Glass
374*7b75b448SSimon Glass    def Refresh(self):
375*7b75b448SSimon Glass        """Refresh the offset cache"""
376*7b75b448SSimon Glass        self._root.Refresh(0)
377*7b75b448SSimon Glass
378*7b75b448SSimon Glass    def GetStructOffset(self, offset):
379*7b75b448SSimon Glass        """Get the file offset of a given struct offset
380*7b75b448SSimon Glass
381*7b75b448SSimon Glass        Args:
382*7b75b448SSimon Glass            offset: Offset within the 'struct' region of the device tree
383*7b75b448SSimon Glass        Returns:
384*7b75b448SSimon Glass            Position of @offset within the device tree binary
385*7b75b448SSimon Glass        """
386*7b75b448SSimon Glass        return libfdt.fdt_off_dt_struct(self._fdt) + offset
387*7b75b448SSimon Glass
388*7b75b448SSimon Glass    @classmethod
389*7b75b448SSimon Glass    def Node(self, fdt, offset, name, path):
390*7b75b448SSimon Glass        """Create a new node
391*7b75b448SSimon Glass
392*7b75b448SSimon Glass        This is used by Fdt.Scan() to create a new node using the correct
393*7b75b448SSimon Glass        class.
394*7b75b448SSimon Glass
395*7b75b448SSimon Glass        Args:
396*7b75b448SSimon Glass            fdt: Fdt object
397*7b75b448SSimon Glass            offset: Offset of node
398*7b75b448SSimon Glass            name: Node name
399*7b75b448SSimon Glass            path: Full path to node
400*7b75b448SSimon Glass        """
401*7b75b448SSimon Glass        node = Node(fdt, offset, name, path)
402*7b75b448SSimon Glass        return node
403