dtoc: Keep track of property offsets
authorSimon Glass <sjg@chromium.org>
Fri, 6 Jul 2018 16:27:29 +0000 (10:27 -0600)
committerSimon Glass <sjg@chromium.org>
Mon, 9 Jul 2018 15:11:00 +0000 (09:11 -0600)
At present the Fdt class does not keep track of property offsets if they
change due to removal of properties. Update the code to handle this, and
add a test.

Signed-off-by: Simon Glass <sjg@chromium.org>
tools/dtoc/fdt.py
tools/dtoc/test_fdt.py

index fd016bb8ce10921949f47dcbc585064c4323409d..274c142e7ae55fab88b92896fba6ab92c12adc35 100644 (file)
@@ -49,6 +49,9 @@ class Prop:
             return
         self.type, self.value = self.BytesToValue(bytes)
 
+    def RefreshOffset(self, poffset):
+        self._offset = poffset
+
     def Widen(self, newprop):
         """Figure out which property type is more general
 
@@ -154,6 +157,7 @@ class Prop:
         Returns:
             The offset of the property (struct fdt_property) within the file
         """
+        self._node._fdt.CheckCache()
         return self._node._fdt.GetStructOffset(self._offset)
 
 class Node:
@@ -233,8 +237,23 @@ class Node:
             self._offset = my_offset
         offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
         for subnode in self.subnodes:
+            if subnode.name != fdt_obj.get_name(offset):
+                raise ValueError('Internal error, node name mismatch %s != %s' %
+                                 (subnode.name, fdt_obj.get_name(offset)))
             subnode.Refresh(offset)
             offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
+        if offset != -libfdt.FDT_ERR_NOTFOUND:
+            raise ValueError('Internal error, offset == %d' % offset)
+
+        poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
+        while poffset >= 0:
+            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))
+            prop.RefreshOffset(poffset)
+            poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
 
     def DeleteProp(self, prop_name):
         """Delete a property of a node
@@ -278,6 +297,7 @@ class Fdt:
 
         TODO(sjg@chromium.org): Implement the 'root' parameter
         """
+        self._cached_offsets = True
         self._root = self.Node(self, None, 0, '/', '/')
         self._root.Scan()
 
index d44e4dd84231f3a07bcfbc646f3a1ca7a0c736c3..e57298dbe7cd4d22e83cf42138bb444462c0985e 100755 (executable)
@@ -24,6 +24,30 @@ import libfdt
 import test_util
 import tools
 
+def _GetPropertyValue(dtb, node, prop_name):
+    """Low-level function to get the property value based on its offset
+
+    This looks directly in the device tree at the property's offset to find
+    its value. It is useful as a check that the property is in the correct
+    place.
+
+    Args:
+        node: Node to look in
+        prop_name: Property name to find
+
+    Returns:
+        Tuple:
+            Prop object found
+            Value of property as a string (found using property offset)
+    """
+    prop = node.props[prop_name]
+
+    # Add 12, which is sizeof(struct fdt_property), to get to start of data
+    offset = prop.GetOffset() + 12
+    data = dtb.GetContents()[offset:offset + len(prop.value)]
+    return prop, [chr(x) for x in data]
+
+
 class TestFdt(unittest.TestCase):
     """Tests for the Fdt module
 
@@ -124,6 +148,12 @@ class TestNode(unittest.TestCase):
         with self.assertRaises(libfdt.FdtException):
             self.node.DeleteProp('missing')
 
+    def testDeleteGetOffset(self):
+        """Test that property offset update when properties are deleted"""
+        self.node.DeleteProp('intval')
+        prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
+        self.assertEqual(prop.value, value)
+
     def testFindNode(self):
         """Tests that we can find a node using the _FindNode() functoin"""
         node = self.dtb.GetRoot()._FindNode('i2c@0')
@@ -132,6 +162,32 @@ class TestNode(unittest.TestCase):
         self.assertEqual('pmic@9', subnode.name)
         self.assertEqual(None, node._FindNode('missing'))
 
+    def testRefreshMissingNode(self):
+        """Test refreshing offsets when an extra node is present in dtb"""
+        # Delete it from our tables, not the device tree
+        del self.dtb._root.subnodes[-1]
+        with self.assertRaises(ValueError) as e:
+            self.dtb.Refresh()
+        self.assertIn('Internal error, offset', str(e.exception))
+
+    def testRefreshExtraNode(self):
+        """Test refreshing offsets when an expected node is missing"""
+        # Delete it from the device tre, not our tables
+        self.dtb.GetFdtObj().del_node(self.node.Offset())
+        with self.assertRaises(ValueError) as e:
+            self.dtb.Refresh()
+        self.assertIn('Internal error, node name mismatch '
+                      'spl-test != spl-test2', str(e.exception))
+
+    def testRefreshMissingProp(self):
+        """Test refreshing offsets when an extra property is present in dtb"""
+        # Delete it from our tables, not the device tree
+        del self.node.props['notstring']
+        with self.assertRaises(ValueError) as e:
+            self.dtb.Refresh()
+        self.assertIn("Internal error, property 'notstring' missing, offset ",
+                      str(e.exception))
+
 
 class TestProp(unittest.TestCase):
     """Test operation of the Prop class"""
@@ -210,13 +266,8 @@ class TestProp(unittest.TestCase):
 
     def testGetOffset(self):
         """Test we can get the offset of a property"""
-        prop = self.node.props['longbytearray']
-
-        # Add 12, which is sizeof(struct fdt_property), to get to start of data
-        offset = prop.GetOffset() + 12
-        data = self.dtb.GetContents()[offset:offset + len(prop.value)]
-        bytes = [chr(x) for x in data]
-        self.assertEqual(bytes, prop.value)
+        prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
+        self.assertEqual(prop.value, value)
 
     def testWiden(self):
         """Test widening of values"""