From fa80c25c09a6c59be4df9c42b4a02538d8a07382 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 14 Sep 2018 04:57:13 -0600 Subject: [PATCH] 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 --- tools/binman/control.py | 2 ++ tools/dtoc/fdt.py | 91 ++++++++++++++++++++++++++++++++++++++++++++----- tools/dtoc/test_fdt.py | 8 ++++- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/tools/binman/control.py b/tools/binman/control.py index 8c48008..ded1b71 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -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() diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index 55baa38..76cd66f 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -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 diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index e88d19f..4a67f89 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -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