]>
Commit | Line | Data |
---|---|---|
76bce10d SG |
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 | ||
a06a34b2 SG |
9 | import struct |
10 | import sys | |
11 | ||
12 | import fdt | |
13 | from fdt import Fdt, NodeBase, PropBase | |
76bce10d SG |
14 | import fdt_util |
15 | import libfdt | |
76bce10d SG |
16 | |
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 | ||
2a70d897 SG |
23 | def CheckErr(errnum, msg): |
24 | if errnum: | |
25 | raise ValueError('Error %d: %s: %s' % | |
26 | (errnum, libfdt.fdt_strerror(errnum), msg)) | |
27 | ||
a06a34b2 | 28 | class Prop(PropBase): |
76bce10d SG |
29 | """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 | """ | |
a06a34b2 SG |
37 | def __init__(self, node, offset, name, bytes): |
38 | PropBase.__init__(self, node, offset, name) | |
39 | self.bytes = bytes | |
76bce10d | 40 | if not bytes: |
bc1dea36 | 41 | self.type = fdt.TYPE_BOOL |
76bce10d SG |
42 | self.value = True |
43 | return | |
bc1dea36 | 44 | self.type, self.value = self.BytesToValue(bytes) |
76bce10d | 45 | |
babdbde6 SG |
46 | 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 | ||
a06a34b2 | 54 | class Node(NodeBase): |
76bce10d SG |
55 | """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): | |
a06a34b2 | 67 | NodeBase.__init__(self, fdt, offset, name, path) |
76bce10d | 68 | |
346179f0 SG |
69 | 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 | ||
76bce10d SG |
78 | 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 | """ | |
8828254c | 84 | self.props = self._fdt.GetProps(self) |
76bce10d | 85 | |
346179f0 | 86 | offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset()) |
76bce10d SG |
87 | 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 | ||
346179f0 SG |
97 | 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) | |
76bce10d | 110 | |
2a70d897 SG |
111 | 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 | ||
a06a34b2 SG |
126 | class FdtNormal(Fdt): |
127 | """Provides simple access to a flat device tree blob using libfdt. | |
76bce10d SG |
128 | |
129 | Properties: | |
a06a34b2 SG |
130 | _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 | |
76bce10d | 133 | """ |
76bce10d | 134 | def __init__(self, fname): |
a06a34b2 | 135 | Fdt.__init__(self, fname) |
346179f0 | 136 | self._cached_offsets = False |
355c67c3 SG |
137 | if self._fname: |
138 | self._fname = fdt_util.EnsureCompiled(self._fname) | |
139 | ||
140 | with open(self._fname) as fd: | |
0170804f | 141 | self._fdt = bytearray(fd.read()) |
76bce10d SG |
142 | |
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 | ||
da5f7499 SG |
151 | 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 | ||
8828254c | 162 | def GetProps(self, node): |
76bce10d SG |
163 | """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 | """ | |
76bce10d | 175 | props_dict = {} |
8828254c | 176 | poffset = libfdt.fdt_first_property_offset(self._fdt, node._offset) |
76bce10d SG |
177 | while poffset >= 0: |
178 | dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset) | |
a06a34b2 SG |
179 | prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff), |
180 | libfdt.Data(dprop)) | |
76bce10d SG |
181 | props_dict[prop.name] = prop |
182 | ||
183 | poffset = libfdt.fdt_next_property_offset(self._fdt, poffset) | |
184 | return props_dict | |
a06a34b2 | 185 | |
346179f0 SG |
186 | 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 | ||
babdbde6 SG |
201 | 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 | ||
a06a34b2 SG |
211 | @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 |