blob: cce5c06d8c15a677fbd84468f74f3516673cc82a [file] [log] [blame]
Simon Glassa2a6f042016-07-04 11:58:11 -06001#!/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
Simon Glass1f941b62016-07-25 18:59:04 -06009import struct
10import sys
11
12import fdt
13from fdt import Fdt, NodeBase, PropBase
Simon Glassa2a6f042016-07-04 11:58:11 -060014import fdt_util
15import libfdt
Simon Glassa2a6f042016-07-04 11:58:11 -060016
17# This deals with a device tree, presenting it as a list of Node and Prop
18# objects, representing nodes and properties, respectively.
19#
20# This implementation uses a libfdt Python library to access the device tree,
21# so it is fairly efficient.
22
Simon Glassc719e422016-07-25 18:59:14 -060023def CheckErr(errnum, msg):
24 if errnum:
25 raise ValueError('Error %d: %s: %s' %
26 (errnum, libfdt.fdt_strerror(errnum), msg))
27
Simon Glass1f941b62016-07-25 18:59:04 -060028class Prop(PropBase):
Simon Glassa2a6f042016-07-04 11:58:11 -060029 """A device tree property
30
31 Properties:
32 name: Property name (as per the device tree)
33 value: Property value as a string of bytes, or a list of strings of
34 bytes
35 type: Value type
36 """
Simon Glass1f941b62016-07-25 18:59:04 -060037 def __init__(self, node, offset, name, bytes):
38 PropBase.__init__(self, node, offset, name)
39 self.bytes = bytes
Simon Glassa2a6f042016-07-04 11:58:11 -060040 if not bytes:
Simon Glassb1a5e262016-07-25 18:59:05 -060041 self.type = fdt.TYPE_BOOL
Simon Glassa2a6f042016-07-04 11:58:11 -060042 self.value = True
43 return
Simon Glassb1a5e262016-07-25 18:59:05 -060044 self.type, self.value = self.BytesToValue(bytes)
Simon Glassa2a6f042016-07-04 11:58:11 -060045
Simon Glass54cec6d2016-07-25 18:59:16 -060046 def GetOffset(self):
47 """Get the offset of a property
48
49 Returns:
50 The offset of the property (struct fdt_property) within the file
51 """
52 return self._node._fdt.GetStructOffset(self._offset)
53
Simon Glass1f941b62016-07-25 18:59:04 -060054class Node(NodeBase):
Simon Glassa2a6f042016-07-04 11:58:11 -060055 """A device tree node
56
57 Properties:
58 offset: Integer offset in the device tree
59 name: Device tree node tname
60 path: Full path to node, along with the node name itself
61 _fdt: Device tree object
62 subnodes: A list of subnodes for this node, each a Node object
63 props: A dict of properties for this node, each a Prop object.
64 Keyed by property name
65 """
66 def __init__(self, fdt, offset, name, path):
Simon Glass1f941b62016-07-25 18:59:04 -060067 NodeBase.__init__(self, fdt, offset, name, path)
Simon Glassa2a6f042016-07-04 11:58:11 -060068
Simon Glass14fb2922016-07-25 18:59:12 -060069 def Offset(self):
70 """Returns the offset of a node, after checking the cache
71
72 This should be used instead of self._offset directly, to ensure that
73 the cache does not contain invalid offsets.
74 """
75 self._fdt.CheckCache()
76 return self._offset
77
Simon Glassa2a6f042016-07-04 11:58:11 -060078 def Scan(self):
79 """Scan a node's properties and subnodes
80
81 This fills in the props and subnodes properties, recursively
82 searching into subnodes so that the entire tree is built.
83 """
Simon Glass6d928a72016-09-25 15:52:18 -060084 self.props = self._fdt.GetProps(self)
Simon Glassa2a6f042016-07-04 11:58:11 -060085
Simon Glass14fb2922016-07-25 18:59:12 -060086 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
Simon Glassa2a6f042016-07-04 11:58:11 -060087 while offset >= 0:
88 sep = '' if self.path[-1] == '/' else '/'
89 name = libfdt.Name(self._fdt.GetFdt(), offset)
90 path = self.path + sep + name
91 node = Node(self._fdt, offset, name, path)
92 self.subnodes.append(node)
93
94 node.Scan()
95 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
96
Simon Glass14fb2922016-07-25 18:59:12 -060097 def Refresh(self, my_offset):
98 """Fix up the _offset for each node, recursively
99
100 Note: This does not take account of property offsets - these will not
101 be updated.
102 """
103 if self._offset != my_offset:
104 #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
105 self._offset = my_offset
106 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
107 for subnode in self.subnodes:
108 subnode.Refresh(offset)
109 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
Simon Glassa2a6f042016-07-04 11:58:11 -0600110
Simon Glassc719e422016-07-25 18:59:14 -0600111 def DeleteProp(self, prop_name):
112 """Delete a property of a node
113
114 The property is deleted and the offset cache is invalidated.
115
116 Args:
117 prop_name: Name of the property to delete
118 Raises:
119 ValueError if the property does not exist
120 """
121 CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
122 "Node '%s': delete property: '%s'" % (self.path, prop_name))
123 del self.props[prop_name]
124 self._fdt.Invalidate()
125
Simon Glass1f941b62016-07-25 18:59:04 -0600126class FdtNormal(Fdt):
127 """Provides simple access to a flat device tree blob using libfdt.
Simon Glassa2a6f042016-07-04 11:58:11 -0600128
129 Properties:
Simon Glass1f941b62016-07-25 18:59:04 -0600130 _fdt: Device tree contents (bytearray)
131 _cached_offsets: True if all the nodes have a valid _offset property,
132 False if something has changed to invalidate the offsets
Simon Glassa2a6f042016-07-04 11:58:11 -0600133 """
Simon Glassa2a6f042016-07-04 11:58:11 -0600134 def __init__(self, fname):
Simon Glass1f941b62016-07-25 18:59:04 -0600135 Fdt.__init__(self, fname)
Simon Glass14fb2922016-07-25 18:59:12 -0600136 self._cached_offsets = False
Simon Glassefc9bf62016-07-25 18:59:10 -0600137 if self._fname:
138 self._fname = fdt_util.EnsureCompiled(self._fname)
139
140 with open(self._fname) as fd:
Simon Glassa930ecc2016-07-25 18:59:13 -0600141 self._fdt = bytearray(fd.read())
Simon Glassa2a6f042016-07-04 11:58:11 -0600142
143 def GetFdt(self):
144 """Get the contents of the FDT
145
146 Returns:
147 The FDT contents as a string of bytes
148 """
149 return self._fdt
150
Simon Glass32d98272016-07-25 18:59:15 -0600151 def Flush(self):
152 """Flush device tree changes back to the file"""
153 with open(self._fname, 'wb') as fd:
154 fd.write(self._fdt)
155
156 def Pack(self):
157 """Pack the device tree down to its minimum size"""
158 CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
159 fdt_len = libfdt.fdt_totalsize(self._fdt)
160 del self._fdt[fdt_len:]
161
Simon Glass6d928a72016-09-25 15:52:18 -0600162 def GetProps(self, node):
Simon Glassa2a6f042016-07-04 11:58:11 -0600163 """Get all properties from a node.
164
165 Args:
166 node: Full path to node name to look in.
167
168 Returns:
169 A dictionary containing all the properties, indexed by node name.
170 The entries are Prop objects.
171
172 Raises:
173 ValueError: if the node does not exist.
174 """
Simon Glassa2a6f042016-07-04 11:58:11 -0600175 props_dict = {}
Simon Glass6d928a72016-09-25 15:52:18 -0600176 poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset)
Simon Glassa2a6f042016-07-04 11:58:11 -0600177 while poffset >= 0:
178 dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
Simon Glass1f941b62016-07-25 18:59:04 -0600179 prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff),
180 libfdt.Data(dprop))
Simon Glassa2a6f042016-07-04 11:58:11 -0600181 props_dict[prop.name] = prop
182
183 poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
184 return props_dict
Simon Glass1f941b62016-07-25 18:59:04 -0600185
Simon Glass14fb2922016-07-25 18:59:12 -0600186 def Invalidate(self):
187 """Mark our offset cache as invalid"""
188 self._cached_offsets = False
189
190 def CheckCache(self):
191 """Refresh the offset cache if needed"""
192 if self._cached_offsets:
193 return
194 self.Refresh()
195 self._cached_offsets = True
196
197 def Refresh(self):
198 """Refresh the offset cache"""
199 self._root.Refresh(0)
200
Simon Glass54cec6d2016-07-25 18:59:16 -0600201 def GetStructOffset(self, offset):
202 """Get the file offset of a given struct offset
203
204 Args:
205 offset: Offset within the 'struct' region of the device tree
206 Returns:
207 Position of @offset within the device tree binary
208 """
209 return libfdt.fdt_off_dt_struct(self._fdt) + offset
210
Simon Glass1f941b62016-07-25 18:59:04 -0600211 @classmethod
212 def Node(self, fdt, offset, name, path):
213 """Create a new node
214
215 This is used by Fdt.Scan() to create a new node using the correct
216 class.
217
218 Args:
219 fdt: Fdt object
220 offset: Offset of node
221 name: Node name
222 path: Full path to node
223 """
224 node = Node(fdt, offset, name, path)
225 return node