fdt_normal.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. def CheckErr(errnum, msg):
  20. if errnum:
  21. raise ValueError('Error %d: %s: %s' %
  22. (errnum, libfdt.fdt_strerror(errnum), msg))
  23. class Prop(PropBase):
  24. """A device tree property
  25. Properties:
  26. name: Property name (as per the device tree)
  27. value: Property value as a string of bytes, or a list of strings of
  28. bytes
  29. type: Value type
  30. """
  31. def __init__(self, node, offset, name, bytes):
  32. PropBase.__init__(self, node, offset, name)
  33. self.bytes = bytes
  34. if not bytes:
  35. self.type = fdt.TYPE_BOOL
  36. self.value = True
  37. return
  38. self.type, self.value = self.BytesToValue(bytes)
  39. def GetOffset(self):
  40. """Get the offset of a property
  41. Returns:
  42. The offset of the property (struct fdt_property) within the file
  43. """
  44. return self._node._fdt.GetStructOffset(self._offset)
  45. class Node(NodeBase):
  46. """A device tree node
  47. Properties:
  48. offset: Integer offset in the device tree
  49. name: Device tree node tname
  50. path: Full path to node, along with the node name itself
  51. _fdt: Device tree object
  52. subnodes: A list of subnodes for this node, each a Node object
  53. props: A dict of properties for this node, each a Prop object.
  54. Keyed by property name
  55. """
  56. def __init__(self, fdt, offset, name, path):
  57. NodeBase.__init__(self, fdt, offset, name, path)
  58. def Offset(self):
  59. """Returns the offset of a node, after checking the cache
  60. This should be used instead of self._offset directly, to ensure that
  61. the cache does not contain invalid offsets.
  62. """
  63. self._fdt.CheckCache()
  64. return self._offset
  65. def Scan(self):
  66. """Scan a node's properties and subnodes
  67. This fills in the props and subnodes properties, recursively
  68. searching into subnodes so that the entire tree is built.
  69. """
  70. self.props = self._fdt.GetProps(self)
  71. offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
  72. while offset >= 0:
  73. sep = '' if self.path[-1] == '/' else '/'
  74. name = libfdt.Name(self._fdt.GetFdt(), offset)
  75. path = self.path + sep + name
  76. node = Node(self._fdt, offset, name, path)
  77. self.subnodes.append(node)
  78. node.Scan()
  79. offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
  80. def Refresh(self, my_offset):
  81. """Fix up the _offset for each node, recursively
  82. Note: This does not take account of property offsets - these will not
  83. be updated.
  84. """
  85. if self._offset != my_offset:
  86. #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
  87. self._offset = my_offset
  88. offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
  89. for subnode in self.subnodes:
  90. subnode.Refresh(offset)
  91. offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
  92. def DeleteProp(self, prop_name):
  93. """Delete a property of a node
  94. The property is deleted and the offset cache is invalidated.
  95. Args:
  96. prop_name: Name of the property to delete
  97. Raises:
  98. ValueError if the property does not exist
  99. """
  100. CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
  101. "Node '%s': delete property: '%s'" % (self.path, prop_name))
  102. del self.props[prop_name]
  103. self._fdt.Invalidate()
  104. class FdtNormal(Fdt):
  105. """Provides simple access to a flat device tree blob using libfdt.
  106. Properties:
  107. _fdt: Device tree contents (bytearray)
  108. _cached_offsets: True if all the nodes have a valid _offset property,
  109. False if something has changed to invalidate the offsets
  110. """
  111. def __init__(self, fname):
  112. Fdt.__init__(self, fname)
  113. self._cached_offsets = False
  114. if self._fname:
  115. self._fname = fdt_util.EnsureCompiled(self._fname)
  116. with open(self._fname) as fd:
  117. self._fdt = bytearray(fd.read())
  118. def GetFdt(self):
  119. """Get the contents of the FDT
  120. Returns:
  121. The FDT contents as a string of bytes
  122. """
  123. return self._fdt
  124. def Flush(self):
  125. """Flush device tree changes back to the file"""
  126. with open(self._fname, 'wb') as fd:
  127. fd.write(self._fdt)
  128. def Pack(self):
  129. """Pack the device tree down to its minimum size"""
  130. CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
  131. fdt_len = libfdt.fdt_totalsize(self._fdt)
  132. del self._fdt[fdt_len:]
  133. def GetProps(self, node):
  134. """Get all properties from a node.
  135. Args:
  136. node: Full path to node name to look in.
  137. Returns:
  138. A dictionary containing all the properties, indexed by node name.
  139. The entries are Prop objects.
  140. Raises:
  141. ValueError: if the node does not exist.
  142. """
  143. props_dict = {}
  144. poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
  145. while poffset >= 0:
  146. dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
  147. prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff),
  148. libfdt.Data(dprop))
  149. props_dict[prop.name] = prop
  150. poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
  151. return props_dict
  152. def Invalidate(self):
  153. """Mark our offset cache as invalid"""
  154. self._cached_offsets = False
  155. def CheckCache(self):
  156. """Refresh the offset cache if needed"""
  157. if self._cached_offsets:
  158. return
  159. self.Refresh()
  160. self._cached_offsets = True
  161. def Refresh(self):
  162. """Refresh the offset cache"""
  163. self._root.Refresh(0)
  164. def GetStructOffset(self, offset):
  165. """Get the file offset of a given struct offset
  166. Args:
  167. offset: Offset within the 'struct' region of the device tree
  168. Returns:
  169. Position of @offset within the device tree binary
  170. """
  171. return libfdt.fdt_off_dt_struct(self._fdt) + offset
  172. @classmethod
  173. def Node(self, fdt, offset, name, path):
  174. """Create a new node
  175. This is used by Fdt.Scan() to create a new node using the correct
  176. class.
  177. Args:
  178. fdt: Fdt object
  179. offset: Offset of node
  180. name: Node name
  181. path: Full path to node
  182. """
  183. node = Node(fdt, offset, name, path)
  184. return node