X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=tools%2Fdtoc%2Ffdt.py;h=e24acf12804f2d05917f2eefe801569ed73d52d4;hb=960662404f492e7088e5a91833dfb0c19ef5450e;hp=816fdbe5258523a0c9f8074bd463229a9f60d302;hpb=babdbde68f1b993289462394f209f4010c761246;p=u-boot diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index 816fdbe525..e24acf1280 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -1,31 +1,34 @@ #!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ # # Copyright (C) 2016 Google, Inc # Written by Simon Glass # -# SPDX-License-Identifier: GPL-2.0+ -# import struct import sys import fdt_util +import libfdt +from libfdt import QUIET_NOTFOUND # This deals with a device tree, presenting it as an assortment of Node and # Prop objects, representing nodes and properties, respectively. This file -# contains the base classes and defines the high-level API. Most of the -# implementation is in the FdtFallback and FdtNormal subclasses. See -# fdt_select.py for how to create an Fdt object. +# contains the base classes and defines the high-level API. You can use +# FdtScan() as a convenience function to create and scan an Fdt. + +# This implementation uses a libfdt Python library to access the device tree, +# so it is fairly efficient. # A list of types we support -(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4) +(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5) def CheckErr(errnum, msg): if errnum: raise ValueError('Error %d: %s: %s' % (errnum, libfdt.fdt_strerror(errnum), msg)) -class PropBase: +class Prop: """A device tree property Properties: @@ -34,11 +37,17 @@ class PropBase: bytes type: Value type """ - def __init__(self, node, offset, name): + def __init__(self, node, offset, name, bytes): self._node = node self._offset = offset self.name = name self.value = None + self.bytes = str(bytes) + if not bytes: + self.type = TYPE_BOOL + self.value = True + return + self.type, self.value = self.BytesToValue(bytes) def GetPhandle(self): """Get a (single) phandle value from a property @@ -96,6 +105,7 @@ class PropBase: TYPE_INT: a byte-swapped integer stored as a 4-byte string TYPE_BYTE: a byte stored as a single-byte string """ + bytes = str(bytes) size = len(bytes) strings = bytes.split('\0') is_string = True @@ -129,6 +139,7 @@ class PropBase: else: return TYPE_INT, val + @classmethod def GetEmpty(self, type): """Get an empty / zero value of the given type @@ -147,15 +158,12 @@ class PropBase: def GetOffset(self): """Get the offset of a property - This can be implemented by subclasses. - Returns: - The offset of the property (struct fdt_property) within the - file, or None if not known. + The offset of the property (struct fdt_property) within the file """ - return None + return self._node._fdt.GetStructOffset(self._offset) -class NodeBase: +class Node: """A device tree node Properties: @@ -167,8 +175,9 @@ class NodeBase: props: A dict of properties for this node, each a Prop object. Keyed by property name """ - def __init__(self, fdt, offset, name, path): + def __init__(self, fdt, parent, offset, name, path): self._fdt = fdt + self.parent = parent self._offset = offset self.name = name self.path = path @@ -188,25 +197,69 @@ class NodeBase: return subnode return None + def Offset(self): + """Returns the offset of a node, after checking the cache + + This should be used instead of self._offset directly, to ensure that + the cache does not contain invalid offsets. + """ + self._fdt.CheckCache() + return self._offset + def Scan(self): - """Scan the subnodes of a node + """Scan a node's properties and subnodes - This should be implemented by subclasses + This fills in the props and subnodes properties, recursively + searching into subnodes so that the entire tree is built. + """ + fdt_obj = self._fdt._fdt_obj + self.props = self._fdt.GetProps(self) + phandle = fdt_obj.get_phandle(self.Offset()) + if phandle: + self._fdt.phandle_to_node[phandle] = self + + offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND) + while offset >= 0: + sep = '' if self.path[-1] == '/' else '/' + name = fdt_obj.get_name(offset) + path = self.path + sep + name + node = Node(self._fdt, self, offset, name, path) + self.subnodes.append(node) + + node.Scan() + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) + + def Refresh(self, my_offset): + """Fix up the _offset for each node, recursively + + Note: This does not take account of property offsets - these will not + be updated. """ - raise NotImplementedError() + fdt_obj = self._fdt._fdt_obj + if self._offset != my_offset: + self._offset = my_offset + offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) + for subnode in self.subnodes: + subnode.Refresh(offset) + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) def DeleteProp(self, prop_name): """Delete a property of a node - This should be implemented by subclasses + The property is deleted and the offset cache is invalidated. Args: prop_name: Name of the property to delete + Raises: + ValueError if the property does not exist """ - raise NotImplementedError() + CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name), + "Node '%s': delete property: '%s'" % (self.path, prop_name)) + del self.props[prop_name] + self._fdt.Invalidate() class Fdt: - """Provides simple access to a flat device tree blob. + """Provides simple access to a flat device tree blob using libfdts. Properties: fname: Filename of fdt @@ -214,6 +267,13 @@ class Fdt: """ def __init__(self, fname): self._fname = fname + self._cached_offsets = False + self.phandle_to_node = {} + if self._fname: + self._fname = fdt_util.EnsureCompiled(self._fname) + + with open(self._fname) as fd: + self._fdt_obj = libfdt.Fdt(fd.read()) def Scan(self, root='/'): """Scan a device tree, building up a tree of Node objects @@ -225,7 +285,7 @@ class Fdt: TODO(sjg@chromium.org): Implement the 'root' parameter """ - self._root = self.Node(self, 0, '/', '/') + self._root = self.Node(self, None, 0, '/', '/') self._root.Scan() def GetRoot(self): @@ -255,15 +315,109 @@ class Fdt: """Flush device tree changes back to the file If the device tree has changed in memory, write it back to the file. - Subclasses can implement this if needed. """ - pass + with open(self._fname, 'wb') as fd: + fd.write(self._fdt_obj.as_bytearray()) def Pack(self): """Pack the device tree down to its minimum size When nodes and properties shrink or are deleted, wasted space can - build up in the device tree binary. Subclasses can implement this - to remove that spare space. + build up in the device tree binary. """ - pass + CheckErr(self._fdt_obj.pack(), 'pack') + self.Invalidate() + + def GetContents(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a string of bytes + """ + return self._fdt_obj.as_bytearray() + + def GetFdtObj(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a libfdt.Fdt object + """ + return self._fdt_obj + + def CheckErr(self, errnum, msg): + if errnum: + raise ValueError('Error %d: %s: %s' % + (errnum, libfdt.fdt_strerror(errnum), msg)) + + def GetProps(self, node): + """Get all properties from a node. + + Args: + node: Full path to node name to look in. + + Returns: + A dictionary containing all the properties, indexed by node name. + The entries are Prop objects. + + Raises: + ValueError: if the node does not exist. + """ + props_dict = {} + poffset = self._fdt_obj.first_property_offset(node._offset, + QUIET_NOTFOUND) + while poffset >= 0: + p = self._fdt_obj.get_property_by_offset(poffset) + prop = Prop(node, poffset, p.name, p) + props_dict[prop.name] = prop + + poffset = self._fdt_obj.next_property_offset(poffset, + QUIET_NOTFOUND) + return props_dict + + def Invalidate(self): + """Mark our offset cache as invalid""" + self._cached_offsets = False + + def CheckCache(self): + """Refresh the offset cache if needed""" + if self._cached_offsets: + return + self.Refresh() + self._cached_offsets = True + + def Refresh(self): + """Refresh the offset cache""" + self._root.Refresh(0) + + def GetStructOffset(self, offset): + """Get the file offset of a given struct offset + + Args: + offset: Offset within the 'struct' region of the device tree + Returns: + Position of @offset within the device tree binary + """ + return self._fdt_obj.off_dt_struct() + offset + + @classmethod + def Node(self, fdt, parent, offset, name, path): + """Create a new node + + This is used by Fdt.Scan() to create a new node using the correct + class. + + Args: + fdt: Fdt object + parent: Parent node, or None if this is the root node + offset: Offset of node + name: Node name + path: Full path to node + """ + node = Node(fdt, parent, offset, name, path) + return node + +def FdtScan(fname): + """Returns a new Fdt object from the implementation we are using""" + dtb = Fdt(fname) + dtb.Scan() + return dtb