blob: f2cf608b1e881c7e778844251f39c055bd96cfe9 [file] [log] [blame]
Simon Glass76bce102016-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 Glassa06a34b2016-07-25 18:59:04 -06009import struct
10import sys
11
12import fdt
13from fdt import Fdt, NodeBase, PropBase
Simon Glass76bce102016-07-04 11:58:11 -060014import fdt_util
15import libfdt
Simon Glass76bce102016-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 Glass2a70d892016-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 Glassa06a34b2016-07-25 18:59:04 -060028class Prop(PropBase):
Simon Glass76bce102016-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 Glassa06a34b2016-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 Glass76bce102016-07-04 11:58:11 -060040 if not bytes:
Simon Glassbc1dea32016-07-25 18:59:05 -060041 self.type = fdt.TYPE_BOOL
Simon Glass76bce102016-07-04 11:58:11 -060042 self.value = True
43 return
Simon Glassbc1dea32016-07-25 18:59:05 -060044 self.type, self.value = self.BytesToValue(bytes)
Simon Glass76bce102016-07-04 11:58:11 -060045
Simon Glassa06a34b2016-07-25 18:59:04 -060046class Node(NodeBase):
Simon Glass76bce102016-07-04 11:58:11 -060047 """A device tree node
48
49 Properties:
50 offset: Integer offset in the device tree
51 name: Device tree node tname
52 path: Full path to node, along with the node name itself
53 _fdt: Device tree object
54 subnodes: A list of subnodes for this node, each a Node object
55 props: A dict of properties for this node, each a Prop object.
56 Keyed by property name
57 """
58 def __init__(self, fdt, offset, name, path):
Simon Glassa06a34b2016-07-25 18:59:04 -060059 NodeBase.__init__(self, fdt, offset, name, path)
Simon Glass76bce102016-07-04 11:58:11 -060060
Simon Glass346179f2016-07-25 18:59:12 -060061 def Offset(self):
62 """Returns the offset of a node, after checking the cache
63
64 This should be used instead of self._offset directly, to ensure that
65 the cache does not contain invalid offsets.
66 """
67 self._fdt.CheckCache()
68 return self._offset
69
Simon Glass76bce102016-07-04 11:58:11 -060070 def Scan(self):
71 """Scan a node's properties and subnodes
72
73 This fills in the props and subnodes properties, recursively
74 searching into subnodes so that the entire tree is built.
75 """
Simon Glass346179f2016-07-25 18:59:12 -060076 self.props = self._fdt.GetProps(self, self.path)
Simon Glass76bce102016-07-04 11:58:11 -060077
Simon Glass346179f2016-07-25 18:59:12 -060078 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
Simon Glass76bce102016-07-04 11:58:11 -060079 while offset >= 0:
80 sep = '' if self.path[-1] == '/' else '/'
81 name = libfdt.Name(self._fdt.GetFdt(), offset)
82 path = self.path + sep + name
83 node = Node(self._fdt, offset, name, path)
84 self.subnodes.append(node)
85
86 node.Scan()
87 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
88
Simon Glass346179f2016-07-25 18:59:12 -060089 def Refresh(self, my_offset):
90 """Fix up the _offset for each node, recursively
91
92 Note: This does not take account of property offsets - these will not
93 be updated.
94 """
95 if self._offset != my_offset:
96 #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
97 self._offset = my_offset
98 offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
99 for subnode in self.subnodes:
100 subnode.Refresh(offset)
101 offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
Simon Glass76bce102016-07-04 11:58:11 -0600102
Simon Glass2a70d892016-07-25 18:59:14 -0600103 def DeleteProp(self, prop_name):
104 """Delete a property of a node
105
106 The property is deleted and the offset cache is invalidated.
107
108 Args:
109 prop_name: Name of the property to delete
110 Raises:
111 ValueError if the property does not exist
112 """
113 CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
114 "Node '%s': delete property: '%s'" % (self.path, prop_name))
115 del self.props[prop_name]
116 self._fdt.Invalidate()
117
Simon Glassa06a34b2016-07-25 18:59:04 -0600118class FdtNormal(Fdt):
119 """Provides simple access to a flat device tree blob using libfdt.
Simon Glass76bce102016-07-04 11:58:11 -0600120
121 Properties:
Simon Glassa06a34b2016-07-25 18:59:04 -0600122 _fdt: Device tree contents (bytearray)
123 _cached_offsets: True if all the nodes have a valid _offset property,
124 False if something has changed to invalidate the offsets
Simon Glass76bce102016-07-04 11:58:11 -0600125 """
Simon Glass76bce102016-07-04 11:58:11 -0600126 def __init__(self, fname):
Simon Glassa06a34b2016-07-25 18:59:04 -0600127 Fdt.__init__(self, fname)
Simon Glass346179f2016-07-25 18:59:12 -0600128 self._cached_offsets = False
Simon Glass355c67c2016-07-25 18:59:10 -0600129 if self._fname:
130 self._fname = fdt_util.EnsureCompiled(self._fname)
131
132 with open(self._fname) as fd:
Simon Glass01708042016-07-25 18:59:13 -0600133 self._fdt = bytearray(fd.read())
Simon Glass76bce102016-07-04 11:58:11 -0600134
135 def GetFdt(self):
136 """Get the contents of the FDT
137
138 Returns:
139 The FDT contents as a string of bytes
140 """
141 return self._fdt
142
Simon Glassda5f7492016-07-25 18:59:15 -0600143 def Flush(self):
144 """Flush device tree changes back to the file"""
145 with open(self._fname, 'wb') as fd:
146 fd.write(self._fdt)
147
148 def Pack(self):
149 """Pack the device tree down to its minimum size"""
150 CheckErr(libfdt.fdt_pack(self._fdt), 'pack')
151 fdt_len = libfdt.fdt_totalsize(self._fdt)
152 del self._fdt[fdt_len:]
153
Simon Glass346179f2016-07-25 18:59:12 -0600154 def GetProps(self, node, path):
Simon Glass76bce102016-07-04 11:58:11 -0600155 """Get all properties from a node.
156
157 Args:
158 node: Full path to node name to look in.
159
160 Returns:
161 A dictionary containing all the properties, indexed by node name.
162 The entries are Prop objects.
163
164 Raises:
165 ValueError: if the node does not exist.
166 """
Simon Glass346179f2016-07-25 18:59:12 -0600167 offset = libfdt.fdt_path_offset(self._fdt, path)
Simon Glass76bce102016-07-04 11:58:11 -0600168 if offset < 0:
169 libfdt.Raise(offset)
170 props_dict = {}
171 poffset = libfdt.fdt_first_property_offset(self._fdt, offset)
172 while poffset >= 0:
173 dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
Simon Glassa06a34b2016-07-25 18:59:04 -0600174 prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff),
175 libfdt.Data(dprop))
Simon Glass76bce102016-07-04 11:58:11 -0600176 props_dict[prop.name] = prop
177
178 poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
179 return props_dict
Simon Glassa06a34b2016-07-25 18:59:04 -0600180
Simon Glass346179f2016-07-25 18:59:12 -0600181 def Invalidate(self):
182 """Mark our offset cache as invalid"""
183 self._cached_offsets = False
184
185 def CheckCache(self):
186 """Refresh the offset cache if needed"""
187 if self._cached_offsets:
188 return
189 self.Refresh()
190 self._cached_offsets = True
191
192 def Refresh(self):
193 """Refresh the offset cache"""
194 self._root.Refresh(0)
195
Simon Glassa06a34b2016-07-25 18:59:04 -0600196 @classmethod
197 def Node(self, fdt, offset, name, path):
198 """Create a new node
199
200 This is used by Fdt.Scan() to create a new node using the correct
201 class.
202
203 Args:
204 fdt: Fdt object
205 offset: Offset of node
206 name: Node name
207 path: Full path to node
208 """
209 node = Node(fdt, offset, name, path)
210 return node