fdt_normal.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/python
  2. #
  3. # Copyright (C) 2016 Google, Inc
  4. # Written by Simon Glass <sjg@chromium.org>
  5. #
  6. # SPDX-License-Identifier: GPL-2.0+
  7. #
  8. import struct
  9. import sys
  10. import fdt
  11. from fdt import Fdt, NodeBase, PropBase
  12. import fdt_util
  13. import libfdt
  14. # This deals with a device tree, presenting it as a list of Node and Prop
  15. # objects, representing nodes and properties, respectively.
  16. #
  17. # This implementation uses a libfdt Python library to access the device tree,
  18. # so it is fairly efficient.
  19. class Prop(PropBase):
  20. """A device tree property
  21. Properties:
  22. name: Property name (as per the device tree)
  23. value: Property value as a string of bytes, or a list of strings of
  24. bytes
  25. type: Value type
  26. """
  27. def __init__(self, node, offset, name, bytes):
  28. PropBase.__init__(self, node, offset, name)
  29. self.bytes = bytes
  30. if not bytes:
  31. self.type = fdt.TYPE_BOOL
  32. self.value = True
  33. return
  34. self.type, self.value = self.BytesToValue(bytes)
  35. class Node(NodeBase):
  36. """A device tree node
  37. Properties:
  38. offset: Integer offset in the device tree
  39. name: Device tree node tname
  40. path: Full path to node, along with the node name itself
  41. _fdt: Device tree object
  42. subnodes: A list of subnodes for this node, each a Node object
  43. props: A dict of properties for this node, each a Prop object.
  44. Keyed by property name
  45. """
  46. def __init__(self, fdt, offset, name, path):
  47. NodeBase.__init__(self, fdt, offset, name, path)
  48. def Offset(self):
  49. """Returns the offset of a node, after checking the cache
  50. This should be used instead of self._offset directly, to ensure that
  51. the cache does not contain invalid offsets.
  52. """
  53. self._fdt.CheckCache()
  54. return self._offset
  55. def Scan(self):
  56. """Scan a node's properties and subnodes
  57. This fills in the props and subnodes properties, recursively
  58. searching into subnodes so that the entire tree is built.
  59. """
  60. self.props = self._fdt.GetProps(self, self.path)
  61. offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
  62. while offset >= 0:
  63. sep = '' if self.path[-1] == '/' else '/'
  64. name = libfdt.Name(self._fdt.GetFdt(), offset)
  65. path = self.path + sep + name
  66. node = Node(self._fdt, offset, name, path)
  67. self.subnodes.append(node)
  68. node.Scan()
  69. offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
  70. def Refresh(self, my_offset):
  71. """Fix up the _offset for each node, recursively
  72. Note: This does not take account of property offsets - these will not
  73. be updated.
  74. """
  75. if self._offset != my_offset:
  76. #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
  77. self._offset = my_offset
  78. offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
  79. for subnode in self.subnodes:
  80. subnode.Refresh(offset)
  81. offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
  82. class FdtNormal(Fdt):
  83. """Provides simple access to a flat device tree blob using libfdt.
  84. Properties:
  85. _fdt: Device tree contents (bytearray)
  86. _cached_offsets: True if all the nodes have a valid _offset property,
  87. False if something has changed to invalidate the offsets
  88. """
  89. def __init__(self, fname):
  90. Fdt.__init__(self, fname)
  91. self._cached_offsets = False
  92. if self._fname:
  93. self._fname = fdt_util.EnsureCompiled(self._fname)
  94. with open(self._fname) as fd:
  95. self._fdt = bytearray(fd.read())
  96. def GetFdt(self):
  97. """Get the contents of the FDT
  98. Returns:
  99. The FDT contents as a string of bytes
  100. """
  101. return self._fdt
  102. def GetProps(self, node, path):
  103. """Get all properties from a node.
  104. Args:
  105. node: Full path to node name to look in.
  106. Returns:
  107. A dictionary containing all the properties, indexed by node name.
  108. The entries are Prop objects.
  109. Raises:
  110. ValueError: if the node does not exist.
  111. """
  112. offset = libfdt.fdt_path_offset(self._fdt, path)
  113. if offset < 0:
  114. libfdt.Raise(offset)
  115. props_dict = {}
  116. poffset = libfdt.fdt_first_property_offset(self._fdt, offset)
  117. while poffset >= 0:
  118. dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
  119. prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff),
  120. libfdt.Data(dprop))
  121. props_dict[prop.name] = prop
  122. poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
  123. return props_dict
  124. def Invalidate(self):
  125. """Mark our offset cache as invalid"""
  126. self._cached_offsets = False
  127. def CheckCache(self):
  128. """Refresh the offset cache if needed"""
  129. if self._cached_offsets:
  130. return
  131. self.Refresh()
  132. self._cached_offsets = True
  133. def Refresh(self):
  134. """Refresh the offset cache"""
  135. self._root.Refresh(0)
  136. @classmethod
  137. def Node(self, fdt, offset, name, path):
  138. """Create a new node
  139. This is used by Fdt.Scan() to create a new node using the correct
  140. class.
  141. Args:
  142. fdt: Fdt object
  143. offset: Offset of node
  144. name: Node name
  145. path: Full path to node
  146. """
  147. node = Node(fdt, offset, name, path)
  148. return node