blob: 816fdbe5258523a0c9f8074bd463229a9f60d302 [file] [log] [blame]
Simon Glassa06a34b2016-07-25 18:59:04 -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
9import struct
10import sys
11
12import fdt_util
13
14# This deals with a device tree, presenting it as an assortment of Node and
15# Prop objects, representing nodes and properties, respectively. This file
16# contains the base classes and defines the high-level API. Most of the
17# implementation is in the FdtFallback and FdtNormal subclasses. See
18# fdt_select.py for how to create an Fdt object.
19
Simon Glassbc1dea32016-07-25 18:59:05 -060020# A list of types we support
21(TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4)
22
Simon Glassa06a34b2016-07-25 18:59:04 -060023def CheckErr(errnum, msg):
24 if errnum:
25 raise ValueError('Error %d: %s: %s' %
26 (errnum, libfdt.fdt_strerror(errnum), msg))
27
28class PropBase:
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 """
37 def __init__(self, node, offset, name):
38 self._node = node
39 self._offset = offset
40 self.name = name
41 self.value = None
42
Simon Glassc322a852016-07-25 18:59:06 -060043 def GetPhandle(self):
44 """Get a (single) phandle value from a property
45
46 Gets the phandle valuie from a property and returns it as an integer
47 """
48 return fdt_util.fdt32_to_cpu(self.value[:4])
49
50 def Widen(self, newprop):
51 """Figure out which property type is more general
52
53 Given a current property and a new property, this function returns the
54 one that is less specific as to type. The less specific property will
55 be ble to represent the data in the more specific property. This is
56 used for things like:
57
58 node1 {
59 compatible = "fred";
60 value = <1>;
61 };
62 node1 {
63 compatible = "fred";
64 value = <1 2>;
65 };
66
67 He we want to use an int array for 'value'. The first property
68 suggests that a single int is enough, but the second one shows that
69 it is not. Calling this function with these two propertes would
70 update the current property to be like the second, since it is less
71 specific.
72 """
73 if newprop.type < self.type:
74 self.type = newprop.type
75
76 if type(newprop.value) == list and type(self.value) != list:
77 self.value = [self.value]
78
79 if type(self.value) == list and len(newprop.value) > len(self.value):
80 val = self.GetEmpty(self.type)
81 while len(self.value) < len(newprop.value):
82 self.value.append(val)
83
Simon Glassbc1dea32016-07-25 18:59:05 -060084 def BytesToValue(self, bytes):
85 """Converts a string of bytes into a type and value
86
87 Args:
88 A string containing bytes
89
90 Return:
91 A tuple:
92 Type of data
93 Data, either a single element or a list of elements. Each element
94 is one of:
95 TYPE_STRING: string value from the property
96 TYPE_INT: a byte-swapped integer stored as a 4-byte string
97 TYPE_BYTE: a byte stored as a single-byte string
98 """
99 size = len(bytes)
100 strings = bytes.split('\0')
101 is_string = True
102 count = len(strings) - 1
103 if count > 0 and not strings[-1]:
104 for string in strings[:-1]:
105 if not string:
106 is_string = False
107 break
108 for ch in string:
109 if ch < ' ' or ch > '~':
110 is_string = False
111 break
112 else:
113 is_string = False
114 if is_string:
115 if count == 1:
116 return TYPE_STRING, strings[0]
117 else:
118 return TYPE_STRING, strings[:-1]
119 if size % 4:
120 if size == 1:
121 return TYPE_BYTE, bytes[0]
122 else:
123 return TYPE_BYTE, list(bytes)
124 val = []
125 for i in range(0, size, 4):
126 val.append(bytes[i:i + 4])
127 if size == 4:
128 return TYPE_INT, val[0]
129 else:
130 return TYPE_INT, val
131
132 def GetEmpty(self, type):
133 """Get an empty / zero value of the given type
134
135 Returns:
136 A single value of the given type
137 """
138 if type == TYPE_BYTE:
139 return chr(0)
140 elif type == TYPE_INT:
141 return struct.pack('<I', 0);
142 elif type == TYPE_STRING:
143 return ''
144 else:
145 return True
146
Simon Glassbabdbde2016-07-25 18:59:16 -0600147 def GetOffset(self):
148 """Get the offset of a property
149
150 This can be implemented by subclasses.
151
152 Returns:
153 The offset of the property (struct fdt_property) within the
154 file, or None if not known.
155 """
156 return None
157
Simon Glassa06a34b2016-07-25 18:59:04 -0600158class NodeBase:
159 """A device tree node
160
161 Properties:
162 offset: Integer offset in the device tree
163 name: Device tree node tname
164 path: Full path to node, along with the node name itself
165 _fdt: Device tree object
166 subnodes: A list of subnodes for this node, each a Node object
167 props: A dict of properties for this node, each a Prop object.
168 Keyed by property name
169 """
170 def __init__(self, fdt, offset, name, path):
171 self._fdt = fdt
172 self._offset = offset
173 self.name = name
174 self.path = path
175 self.subnodes = []
176 self.props = {}
177
Simon Glassf7a2aee2016-07-25 18:59:07 -0600178 def _FindNode(self, name):
179 """Find a node given its name
180
181 Args:
182 name: Node name to look for
183 Returns:
184 Node object if found, else None
185 """
186 for subnode in self.subnodes:
187 if subnode.name == name:
188 return subnode
189 return None
190
191 def Scan(self):
192 """Scan the subnodes of a node
193
194 This should be implemented by subclasses
195 """
196 raise NotImplementedError()
197
Simon Glass2a70d892016-07-25 18:59:14 -0600198 def DeleteProp(self, prop_name):
199 """Delete a property of a node
200
201 This should be implemented by subclasses
202
203 Args:
204 prop_name: Name of the property to delete
205 """
206 raise NotImplementedError()
207
Simon Glassa06a34b2016-07-25 18:59:04 -0600208class Fdt:
209 """Provides simple access to a flat device tree blob.
210
211 Properties:
212 fname: Filename of fdt
213 _root: Root of device tree (a Node object)
214 """
215 def __init__(self, fname):
216 self._fname = fname
Simon Glassf7a2aee2016-07-25 18:59:07 -0600217
218 def Scan(self, root='/'):
219 """Scan a device tree, building up a tree of Node objects
220
221 This fills in the self._root property
222
223 Args:
224 root: Ignored
225
226 TODO(sjg@chromium.org): Implement the 'root' parameter
227 """
228 self._root = self.Node(self, 0, '/', '/')
229 self._root.Scan()
230
231 def GetRoot(self):
232 """Get the root Node of the device tree
233
234 Returns:
235 The root Node object
236 """
237 return self._root
238
239 def GetNode(self, path):
240 """Look up a node from its path
241
242 Args:
243 path: Path to look up, e.g. '/microcode/update@0'
244 Returns:
245 Node object, or None if not found
246 """
247 node = self._root
248 for part in path.split('/')[1:]:
249 node = node._FindNode(part)
250 if not node:
251 return None
252 return node
253
Simon Glassda5f7492016-07-25 18:59:15 -0600254 def Flush(self):
255 """Flush device tree changes back to the file
256
257 If the device tree has changed in memory, write it back to the file.
258 Subclasses can implement this if needed.
259 """
260 pass
261
262 def Pack(self):
263 """Pack the device tree down to its minimum size
264
265 When nodes and properties shrink or are deleted, wasted space can
266 build up in the device tree binary. Subclasses can implement this
267 to remove that spare space.
268 """
269 pass