# A list of types we support
class Type(IntEnum):
+ # Types in order from widest to narrowest
(BYTE, INT, STRING, BOOL, INT64) = range(5)
- def is_wider_than(self, other):
- """Check if another type is 'wider' than this one
+ def needs_widening(self, other):
+ """Check if this type needs widening to hold a value from another type
- A wider type is one that holds more information than an earlier one,
- similar to the concept of type-widening in C.
+ A wider type is one that can hold a wider array of information than
+ another one, or is less restrictive, so it can hold the information of
+ another type as well as its own. This is similar to the concept of
+ type-widening in C.
This uses a simple arithmetic comparison, since type values are in order
- from narrowest (BYTE) to widest (INT64).
+ from widest (BYTE) to narrowest (INT64).
Args:
other: Other type to compare against
"""A device tree property
Properties:
+ node: Node containing this property
+ offset: Offset of the property (None if still to be synced)
name: Property name (as per the device tree)
value: Property value as a string of bytes, or a list of strings of
bytes
self.name = name
self.value = None
self.bytes = bytes(data)
- self.dirty = False
+ self.dirty = offset is None
if not data:
self.type = Type.BOOL
self.value = True
update the current property to be like the second, since it is less
specific.
"""
- if self.type.is_wider_than(newprop.type):
+ if self.type.needs_widening(newprop.type):
+
+ # A boolean has an empty value: if it exists it is True and if not
+ # it is False. So when widening we always start with an empty list
+ # since the only valid integer property would be an empty list of
+ # integers.
+ # e.g. this is a boolean:
+ # some-prop;
+ # and it would be widened to int list by:
+ # some-prop = <1 2>;
+ if self.type == Type.BOOL:
+ self.type = Type.INT
+ self.value = [self.GetEmpty(self.type)]
if self.type == Type.INT and newprop.type == Type.BYTE:
if type(self.value) == list:
new_value = []
self.value = new_value
self.type = newprop.type
- if type(newprop.value) == list and type(self.value) != list:
- self.value = [self.value]
+ if type(newprop.value) == list:
+ if type(self.value) != list:
+ self.value = [self.value]
- if type(self.value) == list and len(newprop.value) > len(self.value):
- val = self.GetEmpty(self.type)
- while len(self.value) < len(newprop.value):
- self.value.append(val)
+ if len(newprop.value) > len(self.value):
+ val = self.GetEmpty(self.type)
+ while len(self.value) < len(newprop.value):
+ self.value.append(val)
@classmethod
def GetEmpty(self, type):
Raises:
FdtException if auto_resize is False and there is not enough space
"""
- if self._offset is None or self.dirty:
+ if self.dirty:
node = self._node
fdt_obj = node._fdt._fdt_obj
+ node_name = fdt_obj.get_name(node._offset)
+ if node_name and node_name != node.name:
+ raise ValueError("Internal error, node '%s' name mismatch '%s'" %
+ (node.path, node_name))
+
if auto_resize:
while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
(libfdt.NOSPACE,)) == -libfdt.NOSPACE:
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
else:
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
+ self.dirty = False
class Node:
"""A device tree node
Properties:
- offset: Integer offset in the device tree
+ parent: Parent Node
+ offset: Integer offset in the device tree (None if to be synced)
name: Device tree node tname
path: Full path to node, along with the node name itself
_fdt: Device tree object
fdt_obj = self._fdt._fdt_obj
if self._offset != my_offset:
self._offset = my_offset
+ name = fdt_obj.get_name(self._offset)
+ if name and self.name != name:
+ raise ValueError("Internal error, node '%s' name mismatch '%s'" %
+ (self.path, name))
+
offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
for subnode in self.subnodes:
if subnode.name != fdt_obj.get_name(offset):
p = fdt_obj.get_property_by_offset(poffset)
prop = self.props.get(p.name)
if not prop:
- raise ValueError("Internal error, property '%s' missing, "
- 'offset %d' % (p.name, poffset))
+ raise ValueError("Internal error, node '%s' property '%s' missing, "
+ 'offset %d' % (self.path, p.name, poffset))
prop.RefreshOffset(poffset)
poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
prop_name: Name of property
"""
self.props[prop_name] = Prop(self, None, prop_name,
- tools.GetBytes(0, 4))
+ tools.get_bytes(0, 4))
def AddEmptyProp(self, prop_name, len):
"""Add a property with a fixed data size, for filling in later
prop_name: Name of property
len: Length of data in property
"""
- value = tools.GetBytes(0, len)
+ value = tools.get_bytes(0, len)
self.props[prop_name] = Prop(self, None, prop_name, value)
def _CheckProp(self, prop_name):
Args:
prop_name: Name of property to add
val: Bytes value of property
+
+ Returns:
+ Prop added
"""
- self.props[prop_name] = Prop(self, None, prop_name, val)
+ prop = Prop(self, None, prop_name, val)
+ self.props[prop_name] = prop
+ return prop
def AddString(self, prop_name, val):
"""Add a new string property to a node
Args:
prop_name: Name of property to add
val: String value of property
+
+ Returns:
+ Prop added
"""
- if sys.version_info[0] >= 3: # pragma: no cover
- val = bytes(val, 'utf-8')
- self.AddData(prop_name, val + b'\0')
+ val = bytes(val, 'utf-8')
+ return self.AddData(prop_name, val + b'\0')
+
+ def AddStringList(self, prop_name, val):
+ """Add a new string-list property to a node
+
+ The device tree is marked dirty so that the value will be written to
+ the blob on the next sync.
+
+ Args:
+ prop_name: Name of property to add
+ val (list of str): List of strings to add
+
+ Returns:
+ Prop added
+ """
+ out = b''
+ for string in val:
+ out += bytes(string, 'utf-8') + b'\0'
+ return self.AddData(prop_name, out)
+
+ def AddInt(self, prop_name, val):
+ """Add a new integer property to a node
+
+ The device tree is marked dirty so that the value will be written to
+ the blob on the next sync.
+
+ Args:
+ prop_name: Name of property to add
+ val: Integer value of property
+
+ Returns:
+ Prop added
+ """
+ return self.AddData(prop_name, struct.pack('>I', val))
def AddSubnode(self, name):
"""Add a new subnode to the node
self.subnodes.append(subnode)
return subnode
+ def Delete(self):
+ """Delete a node
+
+ The node is deleted and the offset cache is invalidated.
+
+ Args:
+ node (Node): Node to delete
+
+ Raises:
+ ValueError if the node does not exist
+ """
+ CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
+ "Node '%s': delete" % self.path)
+ parent = self.parent
+ self._fdt.Invalidate()
+ parent.subnodes.remove(self)
+
def Sync(self, auto_resize=False):
"""Sync node changes back to the device tree
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
+ Returns:
+ True if the node had to be added, False if it already existed
+
Raises:
FdtException if auto_resize is False and there is not enough space
"""
+ added = False
if self._offset is None:
# The subnode doesn't exist yet, so add it
fdt_obj = self._fdt._fdt_obj
else:
offset = fdt_obj.add_subnode(self.parent._offset, self.name)
self._offset = offset
+ added = True
- # Sync subnodes in reverse so that we don't disturb node offsets for
- # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
- # node offsets.
+ # Sync the existing subnodes first, so that we can rely on the offsets
+ # being correct. As soon as we add new subnodes, it pushes all the
+ # existing subnodes up.
for node in reversed(self.subnodes):
- node.Sync(auto_resize)
+ if node._offset is not None:
+ node.Sync(auto_resize)
- # Sync properties now, whose offsets should not have been disturbed.
- # We do this after subnodes, since this disturbs the offsets of these
- # properties. Note that new properties will have an offset of None here,
- # which Python 3 cannot sort against int. So use a large value instead
- # to ensure that the new properties are added first.
+ # Sync subnodes in reverse so that we get the expected order. Each
+ # new node goes at the start of the subnode list. This avoids an O(n^2)
+ # rescan of node offsets.
+ num_added = 0
+ for node in reversed(self.subnodes):
+ if node.Sync(auto_resize):
+ num_added += 1
+ if num_added:
+ # Reorder our list of nodes to put the new ones first, since that's
+ # what libfdt does
+ old_count = len(self.subnodes) - num_added
+ subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
+ self.subnodes = subnodes
+
+ # Sync properties now, whose offsets should not have been disturbed,
+ # since properties come before subnodes. This is done after all the
+ # subnode processing above, since updating properties can disturb the
+ # offsets of those subnodes.
+ # Properties are synced in reverse order, with new properties added
+ # before existing properties are synced. This ensures that the offsets
+ # of earlier properties are not disturbed.
+ # Note that new properties will have an offset of None here, which
+ # Python cannot sort against int. So use a large value instead so that
+ # new properties are added first.
prop_list = sorted(self.props.values(),
key=lambda prop: prop._offset or 1 << 31,
reverse=True)
for prop in prop_list:
prop.Sync(auto_resize)
+ return added
class Fdt:
Raises:
FdtException if auto_resize is False and there is not enough space
"""
+ self.CheckCache()
self._root.Sync(auto_resize)
- self.Invalidate()
+ self.Refresh()
def Pack(self):
"""Pack the device tree down to its minimum size
build up in the device tree binary.
"""
CheckErr(self._fdt_obj.pack(), 'pack')
- self.Invalidate()
+ self.Refresh()
def GetContents(self):
"""Get the contents of the FDT
if self._cached_offsets:
return
self.Refresh()
- self._cached_offsets = True
def Refresh(self):
"""Refresh the offset cache"""
self._root.Refresh(0)
+ self._cached_offsets = True
def GetStructOffset(self, offset):
"""Get the file offset of a given struct offset