dtoc: Allow syncing of the device tree back to a file

At present we require the caller to manually update the device tree using
individual calls to libfdt functions. This is not ideal. It would be
better if we could make changes using the Python structure and then call a
Sync() function to write them back.

Add this feature to the Fdt class. Update binman and the tests to match.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2018-09-14 04:57:13 -06:00
parent 0bfa7b09ba
commit fa80c25c09
3 changed files with 91 additions and 10 deletions

View File

@ -183,6 +183,7 @@ def Binman(options, args):
image.AddMissingProperties()
image.ProcessFdt(dtb)
dtb.Sync(auto_resize=True)
dtb.Pack()
dtb.Flush()
@ -199,6 +200,7 @@ def Binman(options, args):
image.SetImagePos()
if options.update_fdt:
image.SetCalculatedProperties()
dtb.Sync()
image.ProcessEntryContents()
image.WriteSymbols()
image.BuildImage()

View File

@ -43,6 +43,7 @@ class Prop:
self.name = name
self.value = None
self.bytes = str(bytes)
self.dirty = False
if not bytes:
self.type = TYPE_BOOL
self.value = True
@ -160,6 +161,45 @@ class Prop:
self._node._fdt.CheckCache()
return self._node._fdt.GetStructOffset(self._offset)
def SetInt(self, val):
"""Set the integer value of the property
The device tree is marked dirty so that the value will be written to
the block on the next sync.
Args:
val: Integer value (32-bit, single cell)
"""
self.bytes = struct.pack('>I', val);
self.value = val
self.type = TYPE_INT
self.dirty = True
def Sync(self, auto_resize=False):
"""Sync property changes back to the device tree
This updates the device tree blob with any changes to this property
since the last sync.
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
if self._offset is None or self.dirty:
node = self._node
fdt_obj = node._fdt._fdt_obj
if auto_resize:
while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
(libfdt.NOSPACE,)) == -libfdt.NOSPACE:
fdt_obj.resize(fdt_obj.totalsize() + 1024)
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
else:
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
class Node:
"""A device tree node
@ -284,13 +324,7 @@ class Node:
Args:
prop_name: Name of property
"""
fdt_obj = self._fdt._fdt_obj
if fdt_obj.setprop_u32(self.Offset(), prop_name, 0,
(libfdt.NOSPACE,)) == -libfdt.NOSPACE:
fdt_obj.resize(fdt_obj.totalsize() + 1024)
fdt_obj.setprop_u32(self.Offset(), prop_name, 0)
self.props[prop_name] = Prop(self, -1, prop_name, '\0' * 4)
self._fdt.Invalidate()
self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4)
def SetInt(self, prop_name, val):
"""Update an integer property int the device tree.
@ -301,8 +335,34 @@ class Node:
prop_name: Name of property
val: Value to set
"""
fdt_obj = self._fdt._fdt_obj
fdt_obj.setprop_u32(self.Offset(), prop_name, val)
self.props[prop_name].SetInt(val)
def Sync(self, auto_resize=False):
"""Sync node changes back to the device tree
This updates the device tree blob with any changes to this node and its
subnodes since the last sync.
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
# 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.
for node in reversed(self.subnodes):
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.
prop_list = sorted(self.props.values(), key=lambda prop: prop._offset,
reverse=True)
for prop in prop_list:
prop.Sync(auto_resize)
class Fdt:
@ -381,6 +441,19 @@ class Fdt:
with open(self._fname, 'wb') as fd:
fd.write(self._fdt_obj.as_bytearray())
def Sync(self, auto_resize=False):
"""Make sure any DT changes are written to the blob
Args:
auto_resize: Resize the device tree automatically if it does not
have enough space for the update
Raises:
FdtException if auto_resize is False and there is not enough space
"""
self._root.Sync(auto_resize)
self.Invalidate()
def Pack(self):
"""Pack the device tree down to its minimum size

View File

@ -337,6 +337,7 @@ class TestProp(unittest.TestCase):
self.node.AddZeroProp('one')
self.node.AddZeroProp('two')
self.node.AddZeroProp('three')
self.dtb.Sync(auto_resize=True)
# Updating existing properties should be OK, since the device-tree size
# does not change
@ -344,12 +345,17 @@ class TestProp(unittest.TestCase):
self.node.SetInt('one', 1)
self.node.SetInt('two', 2)
self.node.SetInt('three', 3)
self.dtb.Sync(auto_resize=False)
# This should fail since it would need to increase the device-tree size
self.node.AddZeroProp('four')
with self.assertRaises(libfdt.FdtException) as e:
self.node.SetInt('four', 4)
self.dtb.Sync(auto_resize=False)
self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
def testAddNode(self):
self.fdt.pack()
class TestFdtUtil(unittest.TestCase):
"""Tests for the fdt_util module