blob: dedd3d75010a33babac830734c5e13a568a16028 [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
Simon Glass160a7662017-05-27 07:38:26 -060017# implementation is in the FdtNormal subclass. See fdt_select.py for how to
18# create an Fdt object.
Simon Glassa06a34b2016-07-25 18:59:04 -060019
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 """
Simon Glassb4360202017-05-27 07:38:22 -060099 bytes = str(bytes)
Simon Glassbc1dea32016-07-25 18:59:05 -0600100 size = len(bytes)
101 strings = bytes.split('\0')
102 is_string = True
103 count = len(strings) - 1
104 if count > 0 and not strings[-1]:
105 for string in strings[:-1]:
106 if not string:
107 is_string = False
108 break
109 for ch in string:
110 if ch < ' ' or ch > '~':
111 is_string = False
112 break
113 else:
114 is_string = False
115 if is_string:
116 if count == 1:
117 return TYPE_STRING, strings[0]
118 else:
119 return TYPE_STRING, strings[:-1]
120 if size % 4:
121 if size == 1:
122 return TYPE_BYTE, bytes[0]
123 else:
124 return TYPE_BYTE, list(bytes)
125 val = []
126 for i in range(0, size, 4):
127 val.append(bytes[i:i + 4])
128 if size == 4:
129 return TYPE_INT, val[0]
130 else:
131 return TYPE_INT, val
132
133 def GetEmpty(self, type):
134 """Get an empty / zero value of the given type
135
136 Returns:
137 A single value of the given type
138 """
139 if type == TYPE_BYTE:
140 return chr(0)
141 elif type == TYPE_INT:
142 return struct.pack('<I', 0);
143 elif type == TYPE_STRING:
144 return ''
145 else:
146 return True
147
Simon Glassbabdbde2016-07-25 18:59:16 -0600148 def GetOffset(self):
149 """Get the offset of a property
150
151 This can be implemented by subclasses.
152
153 Returns:
154 The offset of the property (struct fdt_property) within the
155 file, or None if not known.
156 """
157 return None
158
Simon Glassa06a34b2016-07-25 18:59:04 -0600159class NodeBase:
160 """A device tree node
161
162 Properties:
163 offset: Integer offset in the device tree
164 name: Device tree node tname
165 path: Full path to node, along with the node name itself
166 _fdt: Device tree object
167 subnodes: A list of subnodes for this node, each a Node object
168 props: A dict of properties for this node, each a Prop object.
169 Keyed by property name
170 """
171 def __init__(self, fdt, offset, name, path):
172 self._fdt = fdt
173 self._offset = offset
174 self.name = name
175 self.path = path
176 self.subnodes = []
177 self.props = {}
178
Simon Glassf7a2aee2016-07-25 18:59:07 -0600179 def _FindNode(self, name):
180 """Find a node given its name
181
182 Args:
183 name: Node name to look for
184 Returns:
185 Node object if found, else None
186 """
187 for subnode in self.subnodes:
188 if subnode.name == name:
189 return subnode
190 return None
191
192 def Scan(self):
193 """Scan the subnodes of a node
194
195 This should be implemented by subclasses
196 """
197 raise NotImplementedError()
198
Simon Glass2a70d892016-07-25 18:59:14 -0600199 def DeleteProp(self, prop_name):
200 """Delete a property of a node
201
202 This should be implemented by subclasses
203
204 Args:
205 prop_name: Name of the property to delete
206 """
207 raise NotImplementedError()
208
Simon Glassa06a34b2016-07-25 18:59:04 -0600209class Fdt:
210 """Provides simple access to a flat device tree blob.
211
212 Properties:
213 fname: Filename of fdt
214 _root: Root of device tree (a Node object)
215 """
216 def __init__(self, fname):
217 self._fname = fname
Simon Glassf7a2aee2016-07-25 18:59:07 -0600218
219 def Scan(self, root='/'):
220 """Scan a device tree, building up a tree of Node objects
221
222 This fills in the self._root property
223
224 Args:
225 root: Ignored
226
227 TODO(sjg@chromium.org): Implement the 'root' parameter
228 """
229 self._root = self.Node(self, 0, '/', '/')
230 self._root.Scan()
231
232 def GetRoot(self):
233 """Get the root Node of the device tree
234
235 Returns:
236 The root Node object
237 """
238 return self._root
239
240 def GetNode(self, path):
241 """Look up a node from its path
242
243 Args:
244 path: Path to look up, e.g. '/microcode/update@0'
245 Returns:
246 Node object, or None if not found
247 """
248 node = self._root
249 for part in path.split('/')[1:]:
250 node = node._FindNode(part)
251 if not node:
252 return None
253 return node
254
Simon Glassda5f7492016-07-25 18:59:15 -0600255 def Flush(self):
256 """Flush device tree changes back to the file
257
258 If the device tree has changed in memory, write it back to the file.
259 Subclasses can implement this if needed.
260 """
261 pass
262
263 def Pack(self):
264 """Pack the device tree down to its minimum size
265
266 When nodes and properties shrink or are deleted, wasted space can
267 build up in the device tree binary. Subclasses can implement this
268 to remove that spare space.
269 """
270 pass