blob: 0b01518f3a5b8421f281c53a5aa86be3326b9a4f [file] [log] [blame]
Simon Glassf7ba5f02019-10-31 07:42:54 -06001#!/usr/bin/env python3
Simon Glass2ba98752018-07-06 10:27:24 -06002# SPDX-License-Identifier: GPL-2.0+
Simon Glass5d1637a2022-07-30 20:57:10 -06003
4"""
5Tests for the Fdt module
6Copyright (c) 2018 Google, Inc
7Written by Simon Glass <sjg@chromium.org>
8"""
Simon Glass2ba98752018-07-06 10:27:24 -06009
Simon Glass7640b162022-07-30 20:57:09 -060010from argparse import ArgumentParser
Simon Glass2ba98752018-07-06 10:27:24 -060011import os
Simon Glassa004f292019-07-20 12:23:49 -060012import shutil
Simon Glass2ba98752018-07-06 10:27:24 -060013import sys
Simon Glassa004f292019-07-20 12:23:49 -060014import tempfile
Simon Glass2ba98752018-07-06 10:27:24 -060015import unittest
16
17# Bring in the patman libraries
18our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glassb4fa9492020-04-17 18:09:05 -060019sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass2ba98752018-07-06 10:27:24 -060020
Simon Glassff139b62021-11-23 11:03:38 -070021# Bring in the libfdt module
22sys.path.insert(2, 'scripts/dtc/pylibfdt')
23sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
24sys.path.insert(2, os.path.join(our_path,
25 '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
26
Simon Glass5d1637a2022-07-30 20:57:10 -060027#pylint: disable=wrong-import-position
Simon Glassbf776672020-04-17 18:09:04 -060028from dtoc import fdt
29from dtoc import fdt_util
Simon Glassd866e622021-11-23 11:03:39 -070030from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
Simon Glass8a455fc2022-02-11 13:23:20 -070031from dtoc.fdt import Type, BytesToValue
Simon Glass2ba98752018-07-06 10:27:24 -060032import libfdt
Simon Glass4583c002023-02-23 18:18:04 -070033from u_boot_pylib import test_util
34from u_boot_pylib import tools
Simon Glass8df8b6d2023-07-22 21:43:54 -060035from u_boot_pylib import tout
Simon Glass2ba98752018-07-06 10:27:24 -060036
Simon Glass5d1637a2022-07-30 20:57:10 -060037#pylint: disable=protected-access
38
39def _get_property_value(dtb, node, prop_name):
Simon Glassf9b88b32018-07-06 10:27:29 -060040 """Low-level function to get the property value based on its offset
41
42 This looks directly in the device tree at the property's offset to find
43 its value. It is useful as a check that the property is in the correct
44 place.
45
46 Args:
47 node: Node to look in
48 prop_name: Property name to find
49
50 Returns:
51 Tuple:
52 Prop object found
53 Value of property as a string (found using property offset)
54 """
55 prop = node.props[prop_name]
56
57 # Add 12, which is sizeof(struct fdt_property), to get to start of data
58 offset = prop.GetOffset() + 12
59 data = dtb.GetContents()[offset:offset + len(prop.value)]
Simon Glass479dd302020-11-08 20:36:20 -070060 return prop, [chr(x) for x in data]
Simon Glassf9b88b32018-07-06 10:27:29 -060061
Simon Glassdff51a52021-02-03 06:00:56 -070062def find_dtb_file(dts_fname):
63 """Locate a test file in the test/ directory
64
65 Args:
66 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
67
68 Returns:
69 str: Path to the test filename
70 """
71 return os.path.join('tools/dtoc/test', dts_fname)
72
Simon Glassf9b88b32018-07-06 10:27:29 -060073
Simon Glass2ba98752018-07-06 10:27:24 -060074class TestFdt(unittest.TestCase):
75 """Tests for the Fdt module
76
77 This includes unit tests for some functions and functional tests for the fdt
78 module.
79 """
80 @classmethod
81 def setUpClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -070082 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -060083
84 @classmethod
85 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -070086 tools.finalise_output_dir()
Simon Glass2ba98752018-07-06 10:27:24 -060087
88 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -070089 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -060090
Simon Glass5d1637a2022-07-30 20:57:10 -060091 def test_fdt(self):
Simon Glass2ba98752018-07-06 10:27:24 -060092 """Test that we can open an Fdt"""
93 self.dtb.Scan()
94 root = self.dtb.GetRoot()
95 self.assertTrue(isinstance(root, fdt.Node))
96
Simon Glass5d1637a2022-07-30 20:57:10 -060097 def test_get_node(self):
Simon Glass2ba98752018-07-06 10:27:24 -060098 """Test the GetNode() method"""
99 node = self.dtb.GetNode('/spl-test')
100 self.assertTrue(isinstance(node, fdt.Node))
Simon Glasse44bc832019-07-20 12:23:39 -0600101
Simon Glass2ba98752018-07-06 10:27:24 -0600102 node = self.dtb.GetNode('/i2c@0/pmic@9')
103 self.assertTrue(isinstance(node, fdt.Node))
104 self.assertEqual('pmic@9', node.name)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600105 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600106
Simon Glasse44bc832019-07-20 12:23:39 -0600107 node = self.dtb.GetNode('/')
108 self.assertTrue(isinstance(node, fdt.Node))
109 self.assertEqual(0, node.Offset())
110
Simon Glass5d1637a2022-07-30 20:57:10 -0600111 def test_flush(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600112 """Check that we can flush the device tree out to its file"""
113 fname = self.dtb._fname
Simon Glass5d1637a2022-07-30 20:57:10 -0600114 with open(fname, 'rb') as inf:
115 inf.read()
Simon Glass2ba98752018-07-06 10:27:24 -0600116 os.remove(fname)
117 with self.assertRaises(IOError):
Simon Glass5d1637a2022-07-30 20:57:10 -0600118 with open(fname, 'rb'):
119 pass
Simon Glass2ba98752018-07-06 10:27:24 -0600120 self.dtb.Flush()
Simon Glass5d1637a2022-07-30 20:57:10 -0600121 with open(fname, 'rb') as inf:
122 inf.read()
Simon Glass2ba98752018-07-06 10:27:24 -0600123
Simon Glass5d1637a2022-07-30 20:57:10 -0600124 def test_pack(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600125 """Test that packing a device tree works"""
126 self.dtb.Pack()
127
Simon Glass5d1637a2022-07-30 20:57:10 -0600128 def test_get_fdt_raw(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600129 """Tetst that we can access the raw device-tree data"""
Simon Glass8a455fc2022-02-11 13:23:20 -0700130 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
Simon Glass2ba98752018-07-06 10:27:24 -0600131
Simon Glass5d1637a2022-07-30 20:57:10 -0600132 def test_get_props(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600133 """Tests obtaining a list of properties"""
134 node = self.dtb.GetNode('/spl-test')
135 props = self.dtb.GetProps(node)
Simon Glasse316fba2023-02-13 08:56:34 -0700136 self.assertEqual(['boolval', 'bootph-all', 'bytearray', 'byteval',
137 'compatible', 'int64val', 'intarray', 'intval',
138 'longbytearray', 'maybe-empty-int', 'notstring',
139 'stringarray', 'stringval', ],
Simon Glass2ba98752018-07-06 10:27:24 -0600140 sorted(props.keys()))
141
Simon Glass5d1637a2022-07-30 20:57:10 -0600142 def test_check_error(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600143 """Tests the ChecKError() function"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600144 with self.assertRaises(ValueError) as exc:
Simon Glass2a2d91d2018-07-06 10:27:28 -0600145 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass5d1637a2022-07-30 20:57:10 -0600146 self.assertIn('FDT_ERR_NOTFOUND: hello', str(exc.exception))
Simon Glass2ba98752018-07-06 10:27:24 -0600147
Simon Glass5d1637a2022-07-30 20:57:10 -0600148 def test_get_fdt(self):
149 """Test getting an Fdt object from a node"""
Simon Glass94a7c602018-07-17 13:25:46 -0600150 node = self.dtb.GetNode('/spl-test')
151 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass2ba98752018-07-06 10:27:24 -0600152
Simon Glass5d1637a2022-07-30 20:57:10 -0600153 def test_bytes_to_value(self):
154 """Test converting a string list into Python"""
Simon Glassb5f0daf2019-05-17 22:00:41 -0600155 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700156 (Type.STRING, ['this', 'is']))
Simon Glassb5f0daf2019-05-17 22:00:41 -0600157
Simon Glass2ba98752018-07-06 10:27:24 -0600158class TestNode(unittest.TestCase):
159 """Test operation of the Node class"""
160
161 @classmethod
162 def setUpClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700163 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -0600164
165 @classmethod
166 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700167 tools.finalise_output_dir()
Simon Glass2ba98752018-07-06 10:27:24 -0600168
169 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700170 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600171 self.node = self.dtb.GetNode('/spl-test')
Simon Glass76677dd2021-03-21 18:24:37 +1300172 self.fdt = self.dtb.GetFdtObj()
Simon Glass2ba98752018-07-06 10:27:24 -0600173
Simon Glass5d1637a2022-07-30 20:57:10 -0600174 def test_offset(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600175 """Tests that we can obtain the offset of a node"""
176 self.assertTrue(self.node.Offset() > 0)
177
Simon Glass5d1637a2022-07-30 20:57:10 -0600178 def test_delete(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600179 """Tests that we can delete a property"""
180 node2 = self.dtb.GetNode('/spl-test2')
181 offset1 = node2.Offset()
182 self.node.DeleteProp('intval')
183 offset2 = node2.Offset()
184 self.assertTrue(offset2 < offset1)
185 self.node.DeleteProp('intarray')
186 offset3 = node2.Offset()
187 self.assertTrue(offset3 < offset2)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600188 with self.assertRaises(libfdt.FdtException):
189 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600190
Simon Glass5d1637a2022-07-30 20:57:10 -0600191 def test_delete_get_offset(self):
Simon Glassf9b88b32018-07-06 10:27:29 -0600192 """Test that property offset update when properties are deleted"""
193 self.node.DeleteProp('intval')
Simon Glass5d1637a2022-07-30 20:57:10 -0600194 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
Simon Glassf9b88b32018-07-06 10:27:29 -0600195 self.assertEqual(prop.value, value)
196
Simon Glass5d1637a2022-07-30 20:57:10 -0600197 def test_find_node(self):
Simon Glass1d858882018-07-17 13:25:41 -0600198 """Tests that we can find a node using the FindNode() functoin"""
199 node = self.dtb.GetRoot().FindNode('i2c@0')
Simon Glass2ba98752018-07-06 10:27:24 -0600200 self.assertEqual('i2c@0', node.name)
Simon Glass1d858882018-07-17 13:25:41 -0600201 subnode = node.FindNode('pmic@9')
Simon Glass2ba98752018-07-06 10:27:24 -0600202 self.assertEqual('pmic@9', subnode.name)
Simon Glass1d858882018-07-17 13:25:41 -0600203 self.assertEqual(None, node.FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600204
Simon Glass5d1637a2022-07-30 20:57:10 -0600205 def test_refresh_missing_node(self):
Simon Glassf9b88b32018-07-06 10:27:29 -0600206 """Test refreshing offsets when an extra node is present in dtb"""
207 # Delete it from our tables, not the device tree
208 del self.dtb._root.subnodes[-1]
Simon Glass5d1637a2022-07-30 20:57:10 -0600209 with self.assertRaises(ValueError) as exc:
Simon Glassf9b88b32018-07-06 10:27:29 -0600210 self.dtb.Refresh()
Simon Glass5d1637a2022-07-30 20:57:10 -0600211 self.assertIn('Internal error, offset', str(exc.exception))
Simon Glassf9b88b32018-07-06 10:27:29 -0600212
Simon Glass5d1637a2022-07-30 20:57:10 -0600213 def test_refresh_extra_node(self):
Simon Glassf9b88b32018-07-06 10:27:29 -0600214 """Test refreshing offsets when an expected node is missing"""
215 # Delete it from the device tre, not our tables
Simon Glass76677dd2021-03-21 18:24:37 +1300216 self.fdt.del_node(self.node.Offset())
Simon Glass5d1637a2022-07-30 20:57:10 -0600217 with self.assertRaises(ValueError) as exc:
Simon Glassf9b88b32018-07-06 10:27:29 -0600218 self.dtb.Refresh()
219 self.assertIn('Internal error, node name mismatch '
Simon Glass5d1637a2022-07-30 20:57:10 -0600220 'spl-test != spl-test2', str(exc.exception))
Simon Glassf9b88b32018-07-06 10:27:29 -0600221
Simon Glass5d1637a2022-07-30 20:57:10 -0600222 def test_refresh_missing_prop(self):
Simon Glassf9b88b32018-07-06 10:27:29 -0600223 """Test refreshing offsets when an extra property is present in dtb"""
224 # Delete it from our tables, not the device tree
225 del self.node.props['notstring']
Simon Glass5d1637a2022-07-30 20:57:10 -0600226 with self.assertRaises(ValueError) as exc:
Simon Glassf9b88b32018-07-06 10:27:29 -0600227 self.dtb.Refresh()
Simon Glassacd98612021-03-21 18:24:34 +1300228 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glass5d1637a2022-07-30 20:57:10 -0600229 str(exc.exception))
Simon Glassf9b88b32018-07-06 10:27:29 -0600230
Simon Glass5d1637a2022-07-30 20:57:10 -0600231 def test_lookup_phandle(self):
Simon Glass94a7c602018-07-17 13:25:46 -0600232 """Test looking up a single phandle"""
Simon Glassdff51a52021-02-03 06:00:56 -0700233 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600234 node = dtb.GetNode('/phandle-source2')
235 prop = node.props['clocks']
236 target = dtb.GetNode('/phandle-target')
237 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
238
Simon Glass5d1637a2022-07-30 20:57:10 -0600239 def test_add_node_space(self):
Simon Glass76677dd2021-03-21 18:24:37 +1300240 """Test adding a single node when out of space"""
241 self.fdt.pack()
242 self.node.AddSubnode('subnode')
Simon Glass5d1637a2022-07-30 20:57:10 -0600243 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glass76677dd2021-03-21 18:24:37 +1300244 self.dtb.Sync(auto_resize=False)
Simon Glass5d1637a2022-07-30 20:57:10 -0600245 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glass76677dd2021-03-21 18:24:37 +1300246
247 self.dtb.Sync(auto_resize=True)
248 offset = self.fdt.path_offset('/spl-test/subnode')
249 self.assertTrue(offset > 0)
250
Simon Glass5d1637a2022-07-30 20:57:10 -0600251 def test_add_nodes(self):
Simon Glass76677dd2021-03-21 18:24:37 +1300252 """Test adding various subnode and properies"""
253 node = self.dtb.GetNode('/i2c@0')
254
Simon Glassf6176652021-03-21 18:24:38 +1300255 # Add one more node next to the pmic one
256 sn1 = node.AddSubnode('node-one')
257 sn1.AddInt('integer-a', 12)
258 sn1.AddInt('integer-b', 23)
259
260 # Sync so that everything is clean
261 self.dtb.Sync(auto_resize=True)
262
263 # Add two subnodes next to pmic and node-one
264 sn2 = node.AddSubnode('node-two')
265 sn2.AddInt('integer-2a', 34)
266 sn2.AddInt('integer-2b', 45)
267
268 sn3 = node.AddSubnode('node-three')
269 sn3.AddInt('integer-3', 123)
270
Simon Glass76677dd2021-03-21 18:24:37 +1300271 # Add a property to the node after i2c@0 to check that this is not
272 # disturbed by adding a subnode to i2c@0
273 orig_node = self.dtb.GetNode('/orig-node')
274 orig_node.AddInt('integer-4', 456)
275
276 # Add a property to the pmic node to check that pmic properties are not
277 # disturbed
278 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
279 pmic.AddInt('integer-5', 567)
280
281 self.dtb.Sync(auto_resize=True)
282
Simon Glass5d1637a2022-07-30 20:57:10 -0600283 def test_add_one_node(self):
Simon Glassdd857ee2022-02-08 11:49:52 -0700284 """Testing deleting and adding a subnode before syncing"""
285 subnode = self.node.AddSubnode('subnode')
286 self.node.AddSubnode('subnode2')
287 self.dtb.Sync(auto_resize=True)
288
289 # Delete a node and add a new one
290 subnode.Delete()
291 self.node.AddSubnode('subnode3')
292 self.dtb.Sync()
293
Simon Glass5d1637a2022-07-30 20:57:10 -0600294 def test_refresh_name_mismatch(self):
Simon Glass5d1bec32021-03-21 18:24:39 +1300295 """Test name mismatch when syncing nodes and properties"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600296 self.node.AddInt('integer-a', 12)
Simon Glass5d1bec32021-03-21 18:24:39 +1300297
298 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
299 self.node._offset = wrong_offset
Simon Glass5d1637a2022-07-30 20:57:10 -0600300 with self.assertRaises(ValueError) as exc:
Simon Glass5d1bec32021-03-21 18:24:39 +1300301 self.dtb.Sync()
302 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
Simon Glass5d1637a2022-07-30 20:57:10 -0600303 str(exc.exception))
Simon Glass5d1bec32021-03-21 18:24:39 +1300304
Simon Glass5d1637a2022-07-30 20:57:10 -0600305 with self.assertRaises(ValueError) as exc:
Simon Glass5d1bec32021-03-21 18:24:39 +1300306 self.node.Refresh(wrong_offset)
307 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
Simon Glass5d1637a2022-07-30 20:57:10 -0600308 str(exc.exception))
Simon Glass5d1bec32021-03-21 18:24:39 +1300309
Simon Glass4df457b2023-07-18 07:24:02 -0600310 def test_copy_node(self):
311 """Test copy_node() function"""
Simon Glass71556462023-07-22 21:43:53 -0600312 def do_copy_checks(dtb, dst, second1_ph_val, expect_none):
Simon Glass4df457b2023-07-18 07:24:02 -0600313 self.assertEqual(
314 ['/dest/base', '/dest/first@0', '/dest/existing'],
315 [n.path for n in dst.subnodes])
316
317 chk = dtb.GetNode('/dest/base')
318 self.assertTrue(chk)
319 self.assertEqual(
320 {'compatible', 'bootph-all', '#address-cells', '#size-cells'},
321 chk.props.keys())
322
323 # Check the first property
324 prop = chk.props['bootph-all']
325 self.assertEqual('bootph-all', prop.name)
326 self.assertEqual(True, prop.value)
327 self.assertEqual(chk.path, prop._node.path)
328
329 # Check the second property
330 prop2 = chk.props['compatible']
331 self.assertEqual('compatible', prop2.name)
332 self.assertEqual('sandbox,i2c', prop2.value)
333 self.assertEqual(chk.path, prop2._node.path)
334
335 base = chk.FindNode('base')
336 self.assertTrue(chk)
337
338 first = dtb.GetNode('/dest/base/first@0')
339 self.assertTrue(first)
340 over = dtb.GetNode('/dest/base/over')
341 self.assertTrue(over)
342
Simon Glass589c2d92023-07-22 21:43:55 -0600343 # Make sure that the phandle for 'over' is copied
344 self.assertIn('phandle', over.props.keys())
Simon Glass4df457b2023-07-18 07:24:02 -0600345
346 second = dtb.GetNode('/dest/base/second')
347 self.assertTrue(second)
348 self.assertEqual([over.name, first.name, second.name],
349 [n.name for n in chk.subnodes])
350 self.assertEqual(chk, over.parent)
351 self.assertEqual(
Simon Glass589c2d92023-07-22 21:43:55 -0600352 {'bootph-all', 'compatible', 'reg', 'low-power', 'phandle'},
Simon Glass4df457b2023-07-18 07:24:02 -0600353 over.props.keys())
354
355 if expect_none:
356 self.assertIsNone(prop._offset)
357 self.assertIsNone(prop2._offset)
358 self.assertIsNone(over._offset)
359 else:
360 self.assertTrue(prop._offset)
361 self.assertTrue(prop2._offset)
362 self.assertTrue(over._offset)
363
364 # Now check ordering of the subnodes
365 self.assertEqual(
366 ['second1', 'second2', 'second3', 'second4'],
367 [n.name for n in second.subnodes])
368
Simon Glass71556462023-07-22 21:43:53 -0600369 # Check the 'second_1_bad' phandle is not copied over
370 second1 = second.FindNode('second1')
371 self.assertTrue(second1)
372 sph = second1.props.get('phandle')
373 self.assertTrue(sph)
374 self.assertEqual(second1_ph_val, sph.bytes)
375
376
Simon Glass4df457b2023-07-18 07:24:02 -0600377 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
378 tmpl = dtb.GetNode('/base')
379 dst = dtb.GetNode('/dest')
Simon Glass71556462023-07-22 21:43:53 -0600380 second1_ph_val = (dtb.GetNode('/dest/base/second/second1').
381 props['phandle'].bytes)
Simon Glass4df457b2023-07-18 07:24:02 -0600382 dst.copy_node(tmpl)
383
Simon Glass71556462023-07-22 21:43:53 -0600384 do_copy_checks(dtb, dst, second1_ph_val, expect_none=True)
Simon Glass4df457b2023-07-18 07:24:02 -0600385
386 dtb.Sync(auto_resize=True)
387
Simon Glass589c2d92023-07-22 21:43:55 -0600388 # Now check the resulting FDT. It should have duplicate phandles since
389 # 'over' has been copied to 'dest/base/over' but still exists in its old
390 # place
Simon Glass4df457b2023-07-18 07:24:02 -0600391 new_dtb = fdt.Fdt.FromData(dtb.GetContents())
Simon Glass589c2d92023-07-22 21:43:55 -0600392 with self.assertRaises(ValueError) as exc:
393 new_dtb.Scan()
394 self.assertIn(
395 'Duplicate phandle 1 in nodes /dest/base/over and /base/over',
396 str(exc.exception))
397
398 # Remove the source nodes for the copy
399 new_dtb.GetNode('/base').Delete()
400
401 # Now it should scan OK
Simon Glass4df457b2023-07-18 07:24:02 -0600402 new_dtb.Scan()
Simon Glass589c2d92023-07-22 21:43:55 -0600403
Simon Glass4df457b2023-07-18 07:24:02 -0600404 dst = new_dtb.GetNode('/dest')
Simon Glass71556462023-07-22 21:43:53 -0600405 do_copy_checks(new_dtb, dst, second1_ph_val, expect_none=False)
Simon Glass4df457b2023-07-18 07:24:02 -0600406
Simon Glass55e12782023-07-18 07:24:03 -0600407 def test_copy_subnodes_from_phandles(self):
408 """Test copy_node() function"""
409 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
410
411 orig = dtb.GetNode('/')
412 node_list = fdt_util.GetPhandleList(orig, 'copy-list')
413
414 dst = dtb.GetNode('/dest')
415 dst.copy_subnodes_from_phandles(node_list)
416
417 pmic = dtb.GetNode('/dest/over')
418 self.assertTrue(pmic)
419
420 subn = dtb.GetNode('/dest/first@0')
421 self.assertTrue(subn)
422 self.assertEqual({'a-prop', 'b-prop', 'reg'}, subn.props.keys())
423
424 self.assertEqual(
425 ['/dest/earlier', '/dest/later', '/dest/over', '/dest/first@0',
426 '/dest/second', '/dest/existing', '/dest/base'],
427 [n.path for n in dst.subnodes])
428
429 # Make sure that the phandle for 'over' is not copied
430 over = dst.FindNode('over')
Simon Glass8df8b6d2023-07-22 21:43:54 -0600431 tout.debug(f'keys: {over.props.keys()}')
Simon Glass55e12782023-07-18 07:24:03 -0600432 self.assertNotIn('phandle', over.props.keys())
433
434 # Check the merged properties, first the base ones in '/dest'
435 expect = {'bootph-all', 'compatible', 'stringarray', 'longbytearray',
436 'maybe-empty-int'}
437
438 # Properties from 'base'
439 expect.update({'#address-cells', '#size-cells'})
440
441 # Properties from 'another'
442 expect.add('new-prop')
443
444 self.assertEqual(expect, set(dst.props.keys()))
445
Simon Glass2ba98752018-07-06 10:27:24 -0600446
447class TestProp(unittest.TestCase):
448 """Test operation of the Prop class"""
449
450 @classmethod
451 def setUpClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700452 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -0600453
454 @classmethod
455 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700456 tools.finalise_output_dir()
Simon Glass2ba98752018-07-06 10:27:24 -0600457
458 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700459 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600460 self.node = self.dtb.GetNode('/spl-test')
461 self.fdt = self.dtb.GetFdtObj()
462
Simon Glass5d1637a2022-07-30 20:57:10 -0600463 def test_missing_node(self):
464 """Test GetNode() when the node is missing"""
Simon Glassb9066ff2018-07-06 10:27:30 -0600465 self.assertEqual(None, self.dtb.GetNode('missing'))
466
Simon Glass5d1637a2022-07-30 20:57:10 -0600467 def test_phandle(self):
468 """Test GetNode() on a phandle"""
Simon Glassdff51a52021-02-03 06:00:56 -0700469 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass760b7172018-07-06 10:27:31 -0600470 node = dtb.GetNode('/phandle-source2')
471 prop = node.props['clocks']
472 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600473
Simon Glass5d1637a2022-07-30 20:57:10 -0600474 def _convert_prop(self, prop_name):
Simon Glass2a2d91d2018-07-06 10:27:28 -0600475 """Helper function to look up a property in self.node and return it
476
477 Args:
Simon Glass5d1637a2022-07-30 20:57:10 -0600478 str: Property name to find
Simon Glass2a2d91d2018-07-06 10:27:28 -0600479
Simon Glass5d1637a2022-07-30 20:57:10 -0600480 Returns:
481 fdt.Prop: object for this property
Simon Glass2a2d91d2018-07-06 10:27:28 -0600482 """
Simon Glass5d1637a2022-07-30 20:57:10 -0600483 prop = self.fdt.getprop(self.node.Offset(), prop_name)
484 return fdt.Prop(self.node, -1, prop_name, prop)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600485
Simon Glass5d1637a2022-07-30 20:57:10 -0600486 def test_make_prop(self):
Simon Glass2a2d91d2018-07-06 10:27:28 -0600487 """Test we can convert all the the types that are supported"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600488 prop = self._convert_prop('boolval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700489 self.assertEqual(Type.BOOL, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600490 self.assertEqual(True, prop.value)
491
Simon Glass5d1637a2022-07-30 20:57:10 -0600492 prop = self._convert_prop('intval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700493 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600494 self.assertEqual(1, fdt32_to_cpu(prop.value))
495
Simon Glass5d1637a2022-07-30 20:57:10 -0600496 prop = self._convert_prop('int64val')
Simon Glassd866e622021-11-23 11:03:39 -0700497 self.assertEqual(Type.INT, prop.type)
498 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
499
Simon Glass5d1637a2022-07-30 20:57:10 -0600500 prop = self._convert_prop('intarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700501 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600502 val = [fdt32_to_cpu(val) for val in prop.value]
503 self.assertEqual([2, 3, 4], val)
504
Simon Glass5d1637a2022-07-30 20:57:10 -0600505 prop = self._convert_prop('byteval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700506 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600507 self.assertEqual(5, ord(prop.value))
508
Simon Glass5d1637a2022-07-30 20:57:10 -0600509 prop = self._convert_prop('longbytearray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700510 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600511 val = [ord(val) for val in prop.value]
512 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
513
Simon Glass5d1637a2022-07-30 20:57:10 -0600514 prop = self._convert_prop('stringval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700515 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600516 self.assertEqual('message', prop.value)
517
Simon Glass5d1637a2022-07-30 20:57:10 -0600518 prop = self._convert_prop('stringarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700519 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600520 self.assertEqual(['multi-word', 'message'], prop.value)
521
Simon Glass5d1637a2022-07-30 20:57:10 -0600522 prop = self._convert_prop('notstring')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700523 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600524 val = [ord(val) for val in prop.value]
525 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
526
Simon Glass5d1637a2022-07-30 20:57:10 -0600527 def test_get_empty(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600528 """Tests the GetEmpty() function for the various supported types"""
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700529 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
530 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
Simon Glassc1aa66e2022-01-29 14:14:04 -0700531 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700532 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass2ba98752018-07-06 10:27:24 -0600533
Simon Glass5d1637a2022-07-30 20:57:10 -0600534 def test_get_offset(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600535 """Test we can get the offset of a property"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600536 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
Simon Glassf9b88b32018-07-06 10:27:29 -0600537 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600538
Simon Glass5d1637a2022-07-30 20:57:10 -0600539 def test_widen(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600540 """Test widening of values"""
541 node2 = self.dtb.GetNode('/spl-test2')
Simon Glasse144caf2020-10-03 11:31:27 -0600542 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass2ba98752018-07-06 10:27:24 -0600543 prop = self.node.props['intval']
544
545 # No action
546 prop2 = node2.props['intval']
547 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700548 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600549 self.assertEqual(1, fdt32_to_cpu(prop.value))
550
Simon Glassca044942021-07-28 19:23:10 -0600551 # Convert single value to array
Simon Glass2ba98752018-07-06 10:27:24 -0600552 prop2 = self.node.props['intarray']
553 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700554 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600555 self.assertTrue(isinstance(prop.value, list))
556
557 # A 4-byte array looks like a single integer. When widened by a longer
558 # byte array, it should turn into an array.
559 prop = self.node.props['longbytearray']
560 prop2 = node2.props['longbytearray']
Simon Glasse144caf2020-10-03 11:31:27 -0600561 prop3 = node3.props['longbytearray']
Simon Glass2ba98752018-07-06 10:27:24 -0600562 self.assertFalse(isinstance(prop2.value, list))
563 self.assertEqual(4, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600564 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600565 prop2.Widen(prop)
566 self.assertTrue(isinstance(prop2.value, list))
567 self.assertEqual(9, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600568 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
569 '\0', '\0', '\0', '\0'], prop2.value)
570 prop3.Widen(prop)
571 self.assertTrue(isinstance(prop3.value, list))
572 self.assertEqual(9, len(prop3.value))
573 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
574 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600575
Simon Glass5d1637a2022-07-30 20:57:10 -0600576 def test_widen_more(self):
577 """More tests of widening values"""
578 node2 = self.dtb.GetNode('/spl-test2')
579 node3 = self.dtb.GetNode('/spl-test3')
580 prop = self.node.props['intval']
581
582 # Test widening a single string into a string array
Simon Glass2ba98752018-07-06 10:27:24 -0600583 prop = self.node.props['stringval']
584 prop2 = node2.props['stringarray']
585 self.assertFalse(isinstance(prop.value, list))
586 self.assertEqual(7, len(prop.value))
587 prop.Widen(prop2)
588 self.assertTrue(isinstance(prop.value, list))
589 self.assertEqual(3, len(prop.value))
590
591 # Enlarging an existing array
592 prop = self.node.props['stringarray']
593 prop2 = node2.props['stringarray']
594 self.assertTrue(isinstance(prop.value, list))
595 self.assertEqual(2, len(prop.value))
596 prop.Widen(prop2)
597 self.assertTrue(isinstance(prop.value, list))
598 self.assertEqual(3, len(prop.value))
599
Simon Glassca044942021-07-28 19:23:10 -0600600 # Widen an array of ints with an int (should do nothing)
601 prop = self.node.props['intarray']
Simon Glasse679f392021-08-02 07:37:54 -0600602 prop2 = node2.props['intval']
Simon Glassca044942021-07-28 19:23:10 -0600603 self.assertEqual(Type.INT, prop.type)
604 self.assertEqual(3, len(prop.value))
605 prop.Widen(prop2)
606 self.assertEqual(Type.INT, prop.type)
607 self.assertEqual(3, len(prop.value))
608
Simon Glasseec44c72021-07-28 19:23:11 -0600609 # Widen an empty bool to an int
610 prop = self.node.props['maybe-empty-int']
611 prop3 = node3.props['maybe-empty-int']
612 self.assertEqual(Type.BOOL, prop.type)
613 self.assertEqual(True, prop.value)
614 self.assertEqual(Type.INT, prop3.type)
615 self.assertFalse(isinstance(prop.value, list))
616 self.assertEqual(4, len(prop3.value))
617 prop.Widen(prop3)
618 self.assertEqual(Type.INT, prop.type)
619 self.assertTrue(isinstance(prop.value, list))
620 self.assertEqual(1, len(prop.value))
621
Simon Glass5d1637a2022-07-30 20:57:10 -0600622 def test_add(self):
Simon Glass116adec2018-07-06 10:27:38 -0600623 """Test adding properties"""
624 self.fdt.pack()
625 # This function should automatically expand the device tree
626 self.node.AddZeroProp('one')
627 self.node.AddZeroProp('two')
628 self.node.AddZeroProp('three')
Simon Glassfa80c252018-09-14 04:57:13 -0600629 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600630
631 # Updating existing properties should be OK, since the device-tree size
632 # does not change
633 self.fdt.pack()
634 self.node.SetInt('one', 1)
635 self.node.SetInt('two', 2)
636 self.node.SetInt('three', 3)
Simon Glassfa80c252018-09-14 04:57:13 -0600637 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600638
639 # This should fail since it would need to increase the device-tree size
Simon Glassfa80c252018-09-14 04:57:13 -0600640 self.node.AddZeroProp('four')
Simon Glass5d1637a2022-07-30 20:57:10 -0600641 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glassfa80c252018-09-14 04:57:13 -0600642 self.dtb.Sync(auto_resize=False)
Simon Glass5d1637a2022-07-30 20:57:10 -0600643 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glass64349612018-09-14 04:57:16 -0600644 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600645
Simon Glass5d1637a2022-07-30 20:57:10 -0600646 def test_add_more(self):
Simon Glass64349612018-09-14 04:57:16 -0600647 """Test various other methods for adding and setting properties"""
648 self.node.AddZeroProp('one')
649 self.dtb.Sync(auto_resize=True)
650 data = self.fdt.getprop(self.node.Offset(), 'one')
651 self.assertEqual(0, fdt32_to_cpu(data))
652
653 self.node.SetInt('one', 1)
654 self.dtb.Sync(auto_resize=False)
655 data = self.fdt.getprop(self.node.Offset(), 'one')
656 self.assertEqual(1, fdt32_to_cpu(data))
657
Simon Glass6eb99322021-01-06 21:35:18 -0700658 val = 1234
659 self.node.AddInt('integer', val)
660 self.dtb.Sync(auto_resize=True)
661 data = self.fdt.getprop(self.node.Offset(), 'integer')
662 self.assertEqual(val, fdt32_to_cpu(data))
663
Simon Glass64349612018-09-14 04:57:16 -0600664 val = '123' + chr(0) + '456'
665 self.node.AddString('string', val)
666 self.dtb.Sync(auto_resize=True)
667 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700668 self.assertEqual(tools.to_bytes(val) + b'\0', data)
Simon Glass64349612018-09-14 04:57:16 -0600669
670 self.fdt.pack()
671 self.node.SetString('string', val + 'x')
Simon Glass5d1637a2022-07-30 20:57:10 -0600672 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glass64349612018-09-14 04:57:16 -0600673 self.dtb.Sync(auto_resize=False)
Simon Glass5d1637a2022-07-30 20:57:10 -0600674 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glass64349612018-09-14 04:57:16 -0600675 self.node.SetString('string', val[:-1])
676
677 prop = self.node.props['string']
Simon Glassc1aa66e2022-01-29 14:14:04 -0700678 prop.SetData(tools.to_bytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600679 self.dtb.Sync(auto_resize=False)
680 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700681 self.assertEqual(tools.to_bytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600682
683 self.node.AddEmptyProp('empty', 5)
684 self.dtb.Sync(auto_resize=True)
685 prop = self.node.props['empty']
Simon Glassc1aa66e2022-01-29 14:14:04 -0700686 prop.SetData(tools.to_bytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600687 self.dtb.Sync(auto_resize=False)
688 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700689 self.assertEqual(tools.to_bytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600690
Simon Glassf6b64812019-05-17 22:00:36 -0600691 self.node.SetData('empty', b'123')
692 self.assertEqual(b'123', prop.bytes)
Simon Glass64349612018-09-14 04:57:16 -0600693
Simon Glassc0639172020-07-09 18:39:44 -0600694 # Trying adding a lot of data at once
Simon Glassc1aa66e2022-01-29 14:14:04 -0700695 self.node.AddData('data', tools.get_bytes(65, 20000))
Simon Glassc0639172020-07-09 18:39:44 -0600696 self.dtb.Sync(auto_resize=True)
697
Simon Glassbc116022022-02-08 11:49:50 -0700698 def test_string_list(self):
699 """Test adding string-list property to a node"""
700 val = ['123', '456']
701 self.node.AddStringList('stringlist', val)
702 self.dtb.Sync(auto_resize=True)
703 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
704 self.assertEqual(b'123\x00456\0', data)
705
Simon Glass0ded4d42022-03-05 20:18:56 -0700706 val = []
707 self.node.AddStringList('stringlist', val)
708 self.dtb.Sync(auto_resize=True)
709 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
710 self.assertEqual(b'', data)
711
Simon Glassa30c39f2022-02-08 11:49:51 -0700712 def test_delete_node(self):
713 """Test deleting a node"""
714 old_offset = self.fdt.path_offset('/spl-test')
715 self.assertGreater(old_offset, 0)
716 self.node.Delete()
717 self.dtb.Sync()
718 new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
719 self.assertEqual(-libfdt.NOTFOUND, new_offset)
720
Simon Glass5d1637a2022-07-30 20:57:10 -0600721 def test_from_data(self):
722 """Test creating an FDT from data"""
Simon Glass746aee32018-09-14 04:57:17 -0600723 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
724 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
725
726 self.node.AddEmptyProp('empty', 5)
727 self.dtb.Sync(auto_resize=True)
728 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
729
Simon Glass5d1637a2022-07-30 20:57:10 -0600730 def test_missing_set_int(self):
Simon Glassd9dad102019-07-20 12:23:37 -0600731 """Test handling of a missing property with SetInt"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600732 with self.assertRaises(ValueError) as exc:
Simon Glassd9dad102019-07-20 12:23:37 -0600733 self.node.SetInt('one', 1)
734 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass5d1637a2022-07-30 20:57:10 -0600735 str(exc.exception))
Simon Glassd9dad102019-07-20 12:23:37 -0600736
Simon Glass5d1637a2022-07-30 20:57:10 -0600737 def test_missing_set_data(self):
Simon Glassd9dad102019-07-20 12:23:37 -0600738 """Test handling of a missing property with SetData"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600739 with self.assertRaises(ValueError) as exc:
Simon Glassd9dad102019-07-20 12:23:37 -0600740 self.node.SetData('one', b'data')
741 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass5d1637a2022-07-30 20:57:10 -0600742 str(exc.exception))
Simon Glassd9dad102019-07-20 12:23:37 -0600743
Simon Glass5d1637a2022-07-30 20:57:10 -0600744 def test_missing_set_string(self):
Simon Glassd9dad102019-07-20 12:23:37 -0600745 """Test handling of a missing property with SetString"""
Simon Glass5d1637a2022-07-30 20:57:10 -0600746 with self.assertRaises(ValueError) as exc:
Simon Glassd9dad102019-07-20 12:23:37 -0600747 self.node.SetString('one', 1)
748 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass5d1637a2022-07-30 20:57:10 -0600749 str(exc.exception))
Simon Glassd9dad102019-07-20 12:23:37 -0600750
Simon Glass5d1637a2022-07-30 20:57:10 -0600751 def test_get_filename(self):
Simon Glassf6e02492019-07-20 12:24:08 -0600752 """Test the dtb filename can be provided"""
Simon Glassc1aa66e2022-01-29 14:14:04 -0700753 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glassf6e02492019-07-20 12:24:08 -0600754 self.dtb.GetFilename())
755
Simon Glass2ba98752018-07-06 10:27:24 -0600756
Simon Glass2a2d91d2018-07-06 10:27:28 -0600757class TestFdtUtil(unittest.TestCase):
758 """Tests for the fdt_util module
759
760 This module will likely be mostly replaced at some point, once upstream
761 libfdt has better Python support. For now, this provides tests for current
762 functionality.
763 """
764 @classmethod
765 def setUpClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700766 tools.prepare_output_dir(None)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600767
Simon Glasse0e62752018-10-01 21:12:41 -0600768 @classmethod
769 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700770 tools.finalise_output_dir()
Simon Glasse0e62752018-10-01 21:12:41 -0600771
Simon Glass2a2d91d2018-07-06 10:27:28 -0600772 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700773 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600774 self.node = self.dtb.GetNode('/spl-test')
775
Simon Glass5d1637a2022-07-30 20:57:10 -0600776 def test_get_int(self):
777 """Test getting an int from a node"""
Simon Glass2a2d91d2018-07-06 10:27:28 -0600778 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
779 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
780
Simon Glass5d1637a2022-07-30 20:57:10 -0600781 with self.assertRaises(ValueError) as exc:
Simon Glassd866e622021-11-23 11:03:39 -0700782 fdt_util.GetInt(self.node, 'intarray')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600783 self.assertIn("property 'intarray' has list value: expecting a single "
Simon Glass5d1637a2022-07-30 20:57:10 -0600784 'integer', str(exc.exception))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600785
Simon Glass5d1637a2022-07-30 20:57:10 -0600786 def test_get_int64(self):
787 """Test getting a 64-bit int from a node"""
Simon Glassd866e622021-11-23 11:03:39 -0700788 self.assertEqual(0x123456789abcdef0,
789 fdt_util.GetInt64(self.node, 'int64val'))
790 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
791
Simon Glass5d1637a2022-07-30 20:57:10 -0600792 with self.assertRaises(ValueError) as exc:
Simon Glassd866e622021-11-23 11:03:39 -0700793 fdt_util.GetInt64(self.node, 'intarray')
794 self.assertIn(
795 "property 'intarray' should be a list with 2 items for 64-bit values",
Simon Glass5d1637a2022-07-30 20:57:10 -0600796 str(exc.exception))
Simon Glassd866e622021-11-23 11:03:39 -0700797
Simon Glass5d1637a2022-07-30 20:57:10 -0600798 def test_get_string(self):
799 """Test getting a string from a node"""
Simon Glass2a2d91d2018-07-06 10:27:28 -0600800 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
801 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
802 'test'))
Simon Glass0ded4d42022-03-05 20:18:56 -0700803 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600804
Simon Glass5d1637a2022-07-30 20:57:10 -0600805 with self.assertRaises(ValueError) as exc:
Simon Glass2a2d91d2018-07-06 10:27:28 -0600806 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
807 self.assertIn("property 'stringarray' has list value: expecting a "
Simon Glass5d1637a2022-07-30 20:57:10 -0600808 'single string', str(exc.exception))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600809
Simon Glass5d1637a2022-07-30 20:57:10 -0600810 def test_get_string_list(self):
811 """Test getting a string list from a node"""
Simon Glass1b5a5332021-11-23 21:09:51 -0700812 self.assertEqual(['message'],
813 fdt_util.GetStringList(self.node, 'stringval'))
814 self.assertEqual(
815 ['multi-word', 'message'],
816 fdt_util.GetStringList(self.node, 'stringarray'))
817 self.assertEqual(['test'],
818 fdt_util.GetStringList(self.node, 'missing', ['test']))
Simon Glass0ded4d42022-03-05 20:18:56 -0700819 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
Simon Glass1b5a5332021-11-23 21:09:51 -0700820
Simon Glass5d1637a2022-07-30 20:57:10 -0600821 def test_get_args(self):
822 """Test getting arguments from a node"""
Simon Glass7e4b66a2022-02-08 11:49:53 -0700823 node = self.dtb.GetNode('/orig-node')
824 self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
825 self.assertEqual(
826 ['multi-word', 'message'],
827 fdt_util.GetArgs(self.node, 'stringarray'))
828 self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
Simon Glass61012532022-03-05 20:18:52 -0700829 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
Simon Glass7e4b66a2022-02-08 11:49:53 -0700830 fdt_util.GetArgs(node, 'args'))
Simon Glass61012532022-03-05 20:18:52 -0700831 self.assertEqual(['a space', 'there'],
832 fdt_util.GetArgs(node, 'args2'))
833 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
834 fdt_util.GetArgs(node, 'args3'))
Simon Glass7e4b66a2022-02-08 11:49:53 -0700835 with self.assertRaises(ValueError) as exc:
836 fdt_util.GetArgs(self.node, 'missing')
837 self.assertIn(
838 "Node '/spl-test': Expected property 'missing'",
839 str(exc.exception))
840
Simon Glass5d1637a2022-07-30 20:57:10 -0600841 def test_get_bool(self):
842 """Test getting a bool from a node"""
Simon Glass2a2d91d2018-07-06 10:27:28 -0600843 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
844 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
845 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
846 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
847
Simon Glass5d1637a2022-07-30 20:57:10 -0600848 def test_get_byte(self):
849 """Test getting a byte from a node"""
Simon Glass3af8e492018-07-17 13:25:40 -0600850 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
851 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
852
Simon Glass5d1637a2022-07-30 20:57:10 -0600853 with self.assertRaises(ValueError) as exc:
Simon Glass3af8e492018-07-17 13:25:40 -0600854 fdt_util.GetByte(self.node, 'longbytearray')
855 self.assertIn("property 'longbytearray' has list value: expecting a "
Simon Glass5d1637a2022-07-30 20:57:10 -0600856 'single byte', str(exc.exception))
Simon Glass3af8e492018-07-17 13:25:40 -0600857
Simon Glass5d1637a2022-07-30 20:57:10 -0600858 with self.assertRaises(ValueError) as exc:
Simon Glass3af8e492018-07-17 13:25:40 -0600859 fdt_util.GetByte(self.node, 'intval')
860 self.assertIn("property 'intval' has length 4, expecting 1",
Simon Glass5d1637a2022-07-30 20:57:10 -0600861 str(exc.exception))
Simon Glass3af8e492018-07-17 13:25:40 -0600862
Simon Glass5d1637a2022-07-30 20:57:10 -0600863 def test_get_bytes(self):
864 """Test getting multiple bytes from a node"""
Simon Glass40b4d642021-11-23 11:03:40 -0700865 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
866 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
867 self.assertEqual(
868 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
869
Simon Glass5d1637a2022-07-30 20:57:10 -0600870 with self.assertRaises(ValueError) as exc:
Simon Glass40b4d642021-11-23 11:03:40 -0700871 fdt_util.GetBytes(self.node, 'longbytearray', 7)
872 self.assertIn(
873 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
Simon Glass5d1637a2022-07-30 20:57:10 -0600874 str(exc.exception))
Simon Glass40b4d642021-11-23 11:03:40 -0700875
876 self.assertEqual(
877 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
878 self.assertEqual(
879 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
880
Simon Glass5d1637a2022-07-30 20:57:10 -0600881 def test_get_phandle_list(self):
882 """Test getting a list of phandles from a node"""
Simon Glassdff51a52021-02-03 06:00:56 -0700883 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600884 node = dtb.GetNode('/phandle-source2')
885 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
886 node = dtb.GetNode('/phandle-source')
887 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
888 fdt_util.GetPhandleList(node, 'clocks'))
889 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
890
Simon Glass5d1637a2022-07-30 20:57:10 -0600891 def test_get_data_type(self):
892 """Test getting a value of a particular type from a node"""
Simon Glass53af22a2018-07-17 13:25:32 -0600893 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
894 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
895 str))
Simon Glass5d1637a2022-07-30 20:57:10 -0600896 with self.assertRaises(ValueError):
Simon Glass53af22a2018-07-17 13:25:32 -0600897 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
898 bool))
Simon Glass5d1637a2022-07-30 20:57:10 -0600899 def test_fdt_cells_to_cpu(self):
900 """Test getting cells with the correct endianness"""
Simon Glass2a2d91d2018-07-06 10:27:28 -0600901 val = self.node.props['intarray'].value
902 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
903 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
904
Simon Glassdff51a52021-02-03 06:00:56 -0700905 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600906 node1 = dtb2.GetNode('/test1')
907 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600908 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
909
Simon Glasse66d3182019-05-17 22:00:40 -0600910 node2 = dtb2.GetNode('/test2')
911 val = node2.props['reg'].value
912 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
913 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
914 2))
915 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
916
Simon Glass5d1637a2022-07-30 20:57:10 -0600917 def test_ensure_compiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600918 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700919 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600920 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
921
Simon Glass5d1637a2022-07-30 20:57:10 -0600922 def test_ensure_compiled_tmpdir(self):
Simon Glassa004f292019-07-20 12:23:49 -0600923 """Test providing a temporary directory"""
Heinrich Schuchardtd0d0e772023-04-20 20:03:43 +0200924 old_outdir = tools.outdir
Simon Glassa004f292019-07-20 12:23:49 -0600925 try:
Simon Glassa004f292019-07-20 12:23:49 -0600926 tools.outdir= None
927 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glassdff51a52021-02-03 06:00:56 -0700928 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-07-20 12:23:49 -0600929 tmpdir)
930 self.assertEqual(tmpdir, os.path.dirname(dtb))
931 shutil.rmtree(tmpdir)
932 finally:
Heinrich Schuchardtd0d0e772023-04-20 20:03:43 +0200933 tools.outdir = old_outdir
Simon Glassa004f292019-07-20 12:23:49 -0600934
Simon Glass8f5afe22023-01-11 16:10:18 -0700935 def test_get_phandle_name_offset(self):
936 val = fdt_util.GetPhandleNameOffset(self.node, 'missing')
937 self.assertIsNone(val)
938
939 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
940 node = dtb.GetNode('/phandle-source')
941 node, name, offset = fdt_util.GetPhandleNameOffset(node,
942 'phandle-name-offset')
943 self.assertEqual('phandle3-target', node.name)
944 self.assertEqual('fred', name)
945 self.assertEqual(123, offset)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600946
Simon Glass25980792022-07-30 20:57:05 -0600947def run_test_coverage(build_dir):
948 """Run the tests and check that we get 100% coverage
949
950 Args:
951 build_dir (str): Directory containing the build output
952 """
Simon Glass5e2ab402022-01-29 14:14:14 -0700953 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass4583c002023-02-23 18:18:04 -0700954 ['tools/patman/*.py', 'tools/u_boot_pylib/*', '*test_fdt.py'],
955 build_dir)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600956
957
Simon Glass7640b162022-07-30 20:57:09 -0600958def run_tests(names, processes):
Simon Glass2ba98752018-07-06 10:27:24 -0600959 """Run all the test we have for the fdt model
960
961 Args:
Simon Glass7640b162022-07-30 20:57:09 -0600962 names (list of str): List of test names provided. Only the first is used
Simon Glassb26dd962022-07-30 20:57:06 -0600963 processes (int): Number of processes to use (None means as many as there
964 are CPUs on the system. This must be set to 1 when running under
965 the python3-coverage tool
Simon Glass42ae3632022-03-18 18:01:50 -0600966
967 Returns:
Simon Glassb26dd962022-07-30 20:57:06 -0600968 int: Return code, 0 on success
Simon Glass2ba98752018-07-06 10:27:24 -0600969 """
Simon Glass7640b162022-07-30 20:57:09 -0600970 test_name = names[0] if names else None
Alper Nebi Yasakd8318fe2022-04-02 20:06:06 +0300971 result = test_util.run_test_suites(
Simon Glassad744222022-07-30 20:57:07 -0600972 'test_fdt', False, False, False, processes, test_name, None,
Simon Glass42ae3632022-03-18 18:01:50 -0600973 [TestFdt, TestNode, TestProp, TestFdtUtil])
Simon Glass2ba98752018-07-06 10:27:24 -0600974
Alper Nebi Yasakd8318fe2022-04-02 20:06:06 +0300975 return (0 if result.wasSuccessful() else 1)
976
Simon Glass2ba98752018-07-06 10:27:24 -0600977
Simon Glassa8ad9aa2022-07-30 20:57:08 -0600978def main():
979 """Main program for this tool"""
Simon Glass7640b162022-07-30 20:57:09 -0600980 parser = ArgumentParser()
981 parser.add_argument('-B', '--build-dir', type=str, default='b',
982 help='Directory containing the build output')
983 parser.add_argument('-P', '--processes', type=int,
984 help='set number of processes to use for running tests')
985 parser.add_argument('-t', '--test', action='store_true', dest='test',
986 default=False, help='run tests')
987 parser.add_argument('-T', '--test-coverage', action='store_true',
988 default=False,
989 help='run tests and check for 100% coverage')
990 parser.add_argument('name', nargs='*')
991 args = parser.parse_args()
Simon Glass2ba98752018-07-06 10:27:24 -0600992
Simon Glassa8ad9aa2022-07-30 20:57:08 -0600993 # Run our meagre tests
Simon Glass7640b162022-07-30 20:57:09 -0600994 if args.test:
995 ret_code = run_tests(args.name, args.processes)
Simon Glassa8ad9aa2022-07-30 20:57:08 -0600996 return ret_code
Simon Glass7640b162022-07-30 20:57:09 -0600997 if args.test_coverage:
998 run_test_coverage(args.build_dir)
Simon Glassa8ad9aa2022-07-30 20:57:08 -0600999 return 0
Simon Glass2ba98752018-07-06 10:27:24 -06001000
Simon Glassa8ad9aa2022-07-30 20:57:08 -06001001if __name__ == '__main__':
1002 sys.exit(main())