blob: 0b01518f3a5b8421f281c53a5aa86be3326b9a4f [file] [log] [blame]
Simon Glassfc017822019-10-31 07:42:54 -06001#!/usr/bin/env python3
Simon Glass0ed50752018-07-06 10:27:24 -06002# SPDX-License-Identifier: GPL-2.0+
Simon Glass04afdfe2022-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 Glass0ed50752018-07-06 10:27:24 -06009
Simon Glass882f6322022-07-30 20:57:09 -060010from argparse import ArgumentParser
Simon Glass0ed50752018-07-06 10:27:24 -060011import os
Simon Glassb8d2daa2019-07-20 12:23:49 -060012import shutil
Simon Glass0ed50752018-07-06 10:27:24 -060013import sys
Simon Glassb8d2daa2019-07-20 12:23:49 -060014import tempfile
Simon Glass0ed50752018-07-06 10:27:24 -060015import unittest
16
17# Bring in the patman libraries
18our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glass42143162020-04-17 18:09:05 -060019sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass0ed50752018-07-06 10:27:24 -060020
Simon Glass7e10f8a2021-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 Glass04afdfe2022-07-30 20:57:10 -060027#pylint: disable=wrong-import-position
Simon Glassa997ea52020-04-17 18:09:04 -060028from dtoc import fdt
29from dtoc import fdt_util
Simon Glass3b55e3f2021-11-23 11:03:39 -070030from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
Simon Glass2b160072022-02-11 13:23:20 -070031from dtoc.fdt import Type, BytesToValue
Simon Glass0ed50752018-07-06 10:27:24 -060032import libfdt
Simon Glass131444f2023-02-23 18:18:04 -070033from u_boot_pylib import test_util
34from u_boot_pylib import tools
Simon Glass638567d2023-07-22 21:43:54 -060035from u_boot_pylib import tout
Simon Glass0ed50752018-07-06 10:27:24 -060036
Simon Glass04afdfe2022-07-30 20:57:10 -060037#pylint: disable=protected-access
38
39def _get_property_value(dtb, node, prop_name):
Simon Glass4df8a0c2018-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 Glass632b84c2020-11-08 20:36:20 -070060 return prop, [chr(x) for x in data]
Simon Glass4df8a0c2018-07-06 10:27:29 -060061
Simon Glass4f4b2402021-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 Glass4df8a0c2018-07-06 10:27:29 -060073
Simon Glass0ed50752018-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 Glass80025522022-01-29 14:14:04 -070082 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -060083
84 @classmethod
85 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -070086 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -060087
88 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -070089 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -060090
Simon Glass04afdfe2022-07-30 20:57:10 -060091 def test_fdt(self):
Simon Glass0ed50752018-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 Glass04afdfe2022-07-30 20:57:10 -060097 def test_get_node(self):
Simon Glass0ed50752018-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 Glass3b9a8292019-07-20 12:23:39 -0600101
Simon Glass0ed50752018-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 Glass9c526332018-07-06 10:27:28 -0600105 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600106
Simon Glass3b9a8292019-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 Glass04afdfe2022-07-30 20:57:10 -0600111 def test_flush(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600112 """Check that we can flush the device tree out to its file"""
113 fname = self.dtb._fname
Simon Glass04afdfe2022-07-30 20:57:10 -0600114 with open(fname, 'rb') as inf:
115 inf.read()
Simon Glass0ed50752018-07-06 10:27:24 -0600116 os.remove(fname)
117 with self.assertRaises(IOError):
Simon Glass04afdfe2022-07-30 20:57:10 -0600118 with open(fname, 'rb'):
119 pass
Simon Glass0ed50752018-07-06 10:27:24 -0600120 self.dtb.Flush()
Simon Glass04afdfe2022-07-30 20:57:10 -0600121 with open(fname, 'rb') as inf:
122 inf.read()
Simon Glass0ed50752018-07-06 10:27:24 -0600123
Simon Glass04afdfe2022-07-30 20:57:10 -0600124 def test_pack(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600125 """Test that packing a device tree works"""
126 self.dtb.Pack()
127
Simon Glass04afdfe2022-07-30 20:57:10 -0600128 def test_get_fdt_raw(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600129 """Tetst that we can access the raw device-tree data"""
Simon Glass2b160072022-02-11 13:23:20 -0700130 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
Simon Glass0ed50752018-07-06 10:27:24 -0600131
Simon Glass04afdfe2022-07-30 20:57:10 -0600132 def test_get_props(self):
Simon Glass0ed50752018-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 Glassfc1aa352023-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 Glass0ed50752018-07-06 10:27:24 -0600140 sorted(props.keys()))
141
Simon Glass04afdfe2022-07-30 20:57:10 -0600142 def test_check_error(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600143 """Tests the ChecKError() function"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600144 with self.assertRaises(ValueError) as exc:
Simon Glass9c526332018-07-06 10:27:28 -0600145 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass04afdfe2022-07-30 20:57:10 -0600146 self.assertIn('FDT_ERR_NOTFOUND: hello', str(exc.exception))
Simon Glass0ed50752018-07-06 10:27:24 -0600147
Simon Glass04afdfe2022-07-30 20:57:10 -0600148 def test_get_fdt(self):
149 """Test getting an Fdt object from a node"""
Simon Glasse2d65282018-07-17 13:25:46 -0600150 node = self.dtb.GetNode('/spl-test')
151 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass0ed50752018-07-06 10:27:24 -0600152
Simon Glass04afdfe2022-07-30 20:57:10 -0600153 def test_bytes_to_value(self):
154 """Test converting a string list into Python"""
Simon Glass5872f0c2019-05-17 22:00:41 -0600155 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glassc9a032c2020-11-08 20:36:17 -0700156 (Type.STRING, ['this', 'is']))
Simon Glass5872f0c2019-05-17 22:00:41 -0600157
Simon Glass0ed50752018-07-06 10:27:24 -0600158class TestNode(unittest.TestCase):
159 """Test operation of the Node class"""
160
161 @classmethod
162 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700163 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -0600164
165 @classmethod
166 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700167 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -0600168
169 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700170 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600171 self.node = self.dtb.GetNode('/spl-test')
Simon Glass244548f2021-03-21 18:24:37 +1300172 self.fdt = self.dtb.GetFdtObj()
Simon Glass0ed50752018-07-06 10:27:24 -0600173
Simon Glass04afdfe2022-07-30 20:57:10 -0600174 def test_offset(self):
Simon Glass0ed50752018-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 Glass04afdfe2022-07-30 20:57:10 -0600178 def test_delete(self):
Simon Glass0ed50752018-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 Glass9c526332018-07-06 10:27:28 -0600188 with self.assertRaises(libfdt.FdtException):
189 self.node.DeleteProp('missing')
Simon Glass0ed50752018-07-06 10:27:24 -0600190
Simon Glass04afdfe2022-07-30 20:57:10 -0600191 def test_delete_get_offset(self):
Simon Glass4df8a0c2018-07-06 10:27:29 -0600192 """Test that property offset update when properties are deleted"""
193 self.node.DeleteProp('intval')
Simon Glass04afdfe2022-07-30 20:57:10 -0600194 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
Simon Glass4df8a0c2018-07-06 10:27:29 -0600195 self.assertEqual(prop.value, value)
196
Simon Glass04afdfe2022-07-30 20:57:10 -0600197 def test_find_node(self):
Simon Glassaa1a5d72018-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 Glass0ed50752018-07-06 10:27:24 -0600200 self.assertEqual('i2c@0', node.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600201 subnode = node.FindNode('pmic@9')
Simon Glass0ed50752018-07-06 10:27:24 -0600202 self.assertEqual('pmic@9', subnode.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600203 self.assertEqual(None, node.FindNode('missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600204
Simon Glass04afdfe2022-07-30 20:57:10 -0600205 def test_refresh_missing_node(self):
Simon Glass4df8a0c2018-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 Glass04afdfe2022-07-30 20:57:10 -0600209 with self.assertRaises(ValueError) as exc:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600210 self.dtb.Refresh()
Simon Glass04afdfe2022-07-30 20:57:10 -0600211 self.assertIn('Internal error, offset', str(exc.exception))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600212
Simon Glass04afdfe2022-07-30 20:57:10 -0600213 def test_refresh_extra_node(self):
Simon Glass4df8a0c2018-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 Glass244548f2021-03-21 18:24:37 +1300216 self.fdt.del_node(self.node.Offset())
Simon Glass04afdfe2022-07-30 20:57:10 -0600217 with self.assertRaises(ValueError) as exc:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600218 self.dtb.Refresh()
219 self.assertIn('Internal error, node name mismatch '
Simon Glass04afdfe2022-07-30 20:57:10 -0600220 'spl-test != spl-test2', str(exc.exception))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600221
Simon Glass04afdfe2022-07-30 20:57:10 -0600222 def test_refresh_missing_prop(self):
Simon Glass4df8a0c2018-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 Glass04afdfe2022-07-30 20:57:10 -0600226 with self.assertRaises(ValueError) as exc:
Simon Glass4df8a0c2018-07-06 10:27:29 -0600227 self.dtb.Refresh()
Simon Glass93f18a12021-03-21 18:24:34 +1300228 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glass04afdfe2022-07-30 20:57:10 -0600229 str(exc.exception))
Simon Glass4df8a0c2018-07-06 10:27:29 -0600230
Simon Glass04afdfe2022-07-30 20:57:10 -0600231 def test_lookup_phandle(self):
Simon Glasse2d65282018-07-17 13:25:46 -0600232 """Test looking up a single phandle"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700233 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-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 Glass04afdfe2022-07-30 20:57:10 -0600239 def test_add_node_space(self):
Simon Glass244548f2021-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 Glass04afdfe2022-07-30 20:57:10 -0600243 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glass244548f2021-03-21 18:24:37 +1300244 self.dtb.Sync(auto_resize=False)
Simon Glass04afdfe2022-07-30 20:57:10 -0600245 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glass244548f2021-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 Glass04afdfe2022-07-30 20:57:10 -0600251 def test_add_nodes(self):
Simon Glass244548f2021-03-21 18:24:37 +1300252 """Test adding various subnode and properies"""
253 node = self.dtb.GetNode('/i2c@0')
254
Simon Glass3be798a2021-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 Glass244548f2021-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 Glass04afdfe2022-07-30 20:57:10 -0600283 def test_add_one_node(self):
Simon Glass00238612022-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 Glass04afdfe2022-07-30 20:57:10 -0600294 def test_refresh_name_mismatch(self):
Simon Glassd8bee462021-03-21 18:24:39 +1300295 """Test name mismatch when syncing nodes and properties"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600296 self.node.AddInt('integer-a', 12)
Simon Glassd8bee462021-03-21 18:24:39 +1300297
298 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
299 self.node._offset = wrong_offset
Simon Glass04afdfe2022-07-30 20:57:10 -0600300 with self.assertRaises(ValueError) as exc:
Simon Glassd8bee462021-03-21 18:24:39 +1300301 self.dtb.Sync()
302 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
Simon Glass04afdfe2022-07-30 20:57:10 -0600303 str(exc.exception))
Simon Glassd8bee462021-03-21 18:24:39 +1300304
Simon Glass04afdfe2022-07-30 20:57:10 -0600305 with self.assertRaises(ValueError) as exc:
Simon Glassd8bee462021-03-21 18:24:39 +1300306 self.node.Refresh(wrong_offset)
307 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
Simon Glass04afdfe2022-07-30 20:57:10 -0600308 str(exc.exception))
Simon Glassd8bee462021-03-21 18:24:39 +1300309
Simon Glassadf8e052023-07-18 07:24:02 -0600310 def test_copy_node(self):
311 """Test copy_node() function"""
Simon Glass02e505e2023-07-22 21:43:53 -0600312 def do_copy_checks(dtb, dst, second1_ph_val, expect_none):
Simon Glassadf8e052023-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 Glass48ec1ff2023-07-22 21:43:55 -0600343 # Make sure that the phandle for 'over' is copied
344 self.assertIn('phandle', over.props.keys())
Simon Glassadf8e052023-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 Glass48ec1ff2023-07-22 21:43:55 -0600352 {'bootph-all', 'compatible', 'reg', 'low-power', 'phandle'},
Simon Glassadf8e052023-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 Glass02e505e2023-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 Glassadf8e052023-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 Glass02e505e2023-07-22 21:43:53 -0600380 second1_ph_val = (dtb.GetNode('/dest/base/second/second1').
381 props['phandle'].bytes)
Simon Glassadf8e052023-07-18 07:24:02 -0600382 dst.copy_node(tmpl)
383
Simon Glass02e505e2023-07-22 21:43:53 -0600384 do_copy_checks(dtb, dst, second1_ph_val, expect_none=True)
Simon Glassadf8e052023-07-18 07:24:02 -0600385
386 dtb.Sync(auto_resize=True)
387
Simon Glass48ec1ff2023-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 Glassadf8e052023-07-18 07:24:02 -0600391 new_dtb = fdt.Fdt.FromData(dtb.GetContents())
Simon Glass48ec1ff2023-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 Glassadf8e052023-07-18 07:24:02 -0600402 new_dtb.Scan()
Simon Glass48ec1ff2023-07-22 21:43:55 -0600403
Simon Glassadf8e052023-07-18 07:24:02 -0600404 dst = new_dtb.GetNode('/dest')
Simon Glass02e505e2023-07-22 21:43:53 -0600405 do_copy_checks(new_dtb, dst, second1_ph_val, expect_none=False)
Simon Glassadf8e052023-07-18 07:24:02 -0600406
Simon Glasscd971192023-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 Glass638567d2023-07-22 21:43:54 -0600431 tout.debug(f'keys: {over.props.keys()}')
Simon Glasscd971192023-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 Glass0ed50752018-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 Glass80025522022-01-29 14:14:04 -0700452 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -0600453
454 @classmethod
455 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700456 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -0600457
458 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700459 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600460 self.node = self.dtb.GetNode('/spl-test')
461 self.fdt = self.dtb.GetFdtObj()
462
Simon Glass04afdfe2022-07-30 20:57:10 -0600463 def test_missing_node(self):
464 """Test GetNode() when the node is missing"""
Simon Glassc5eddc82018-07-06 10:27:30 -0600465 self.assertEqual(None, self.dtb.GetNode('missing'))
466
Simon Glass04afdfe2022-07-30 20:57:10 -0600467 def test_phandle(self):
468 """Test GetNode() on a phandle"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700469 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass609e2b12018-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 Glass9c526332018-07-06 10:27:28 -0600473
Simon Glass04afdfe2022-07-30 20:57:10 -0600474 def _convert_prop(self, prop_name):
Simon Glass9c526332018-07-06 10:27:28 -0600475 """Helper function to look up a property in self.node and return it
476
477 Args:
Simon Glass04afdfe2022-07-30 20:57:10 -0600478 str: Property name to find
Simon Glass9c526332018-07-06 10:27:28 -0600479
Simon Glass04afdfe2022-07-30 20:57:10 -0600480 Returns:
481 fdt.Prop: object for this property
Simon Glass9c526332018-07-06 10:27:28 -0600482 """
Simon Glass04afdfe2022-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 Glass9c526332018-07-06 10:27:28 -0600485
Simon Glass04afdfe2022-07-30 20:57:10 -0600486 def test_make_prop(self):
Simon Glass9c526332018-07-06 10:27:28 -0600487 """Test we can convert all the the types that are supported"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600488 prop = self._convert_prop('boolval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700489 self.assertEqual(Type.BOOL, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600490 self.assertEqual(True, prop.value)
491
Simon Glass04afdfe2022-07-30 20:57:10 -0600492 prop = self._convert_prop('intval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700493 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600494 self.assertEqual(1, fdt32_to_cpu(prop.value))
495
Simon Glass04afdfe2022-07-30 20:57:10 -0600496 prop = self._convert_prop('int64val')
Simon Glass3b55e3f2021-11-23 11:03:39 -0700497 self.assertEqual(Type.INT, prop.type)
498 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
499
Simon Glass04afdfe2022-07-30 20:57:10 -0600500 prop = self._convert_prop('intarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700501 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600505 prop = self._convert_prop('byteval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700506 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600507 self.assertEqual(5, ord(prop.value))
508
Simon Glass04afdfe2022-07-30 20:57:10 -0600509 prop = self._convert_prop('longbytearray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700510 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600514 prop = self._convert_prop('stringval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700515 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600516 self.assertEqual('message', prop.value)
517
Simon Glass04afdfe2022-07-30 20:57:10 -0600518 prop = self._convert_prop('stringarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700519 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600520 self.assertEqual(['multi-word', 'message'], prop.value)
521
Simon Glass04afdfe2022-07-30 20:57:10 -0600522 prop = self._convert_prop('notstring')
Simon Glassc9a032c2020-11-08 20:36:17 -0700523 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600527 def test_get_empty(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600528 """Tests the GetEmpty() function for the various supported types"""
Simon Glassc9a032c2020-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 Glass80025522022-01-29 14:14:04 -0700531 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
Simon Glassc9a032c2020-11-08 20:36:17 -0700532 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass0ed50752018-07-06 10:27:24 -0600533
Simon Glass04afdfe2022-07-30 20:57:10 -0600534 def test_get_offset(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600535 """Test we can get the offset of a property"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600536 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
Simon Glass4df8a0c2018-07-06 10:27:29 -0600537 self.assertEqual(prop.value, value)
Simon Glass0ed50752018-07-06 10:27:24 -0600538
Simon Glass04afdfe2022-07-30 20:57:10 -0600539 def test_widen(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600540 """Test widening of values"""
541 node2 = self.dtb.GetNode('/spl-test2')
Simon Glass8034e4d2020-10-03 11:31:27 -0600542 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass0ed50752018-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 Glassc9a032c2020-11-08 20:36:17 -0700548 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600549 self.assertEqual(1, fdt32_to_cpu(prop.value))
550
Simon Glassa7d66982021-07-28 19:23:10 -0600551 # Convert single value to array
Simon Glass0ed50752018-07-06 10:27:24 -0600552 prop2 = self.node.props['intarray']
553 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700554 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-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 Glass8034e4d2020-10-03 11:31:27 -0600561 prop3 = node3.props['longbytearray']
Simon Glass0ed50752018-07-06 10:27:24 -0600562 self.assertFalse(isinstance(prop2.value, list))
563 self.assertEqual(4, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600564 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600565 prop2.Widen(prop)
566 self.assertTrue(isinstance(prop2.value, list))
567 self.assertEqual(9, len(prop2.value))
Simon Glass8034e4d2020-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 Glass0ed50752018-07-06 10:27:24 -0600575
Simon Glass04afdfe2022-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 Glass0ed50752018-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 Glassa7d66982021-07-28 19:23:10 -0600600 # Widen an array of ints with an int (should do nothing)
601 prop = self.node.props['intarray']
Simon Glassf42f26e2021-08-02 07:37:54 -0600602 prop2 = node2.props['intval']
Simon Glassa7d66982021-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 Glass43118322021-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 Glass04afdfe2022-07-30 20:57:10 -0600622 def test_add(self):
Simon Glasse80c5562018-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 Glasseddd7292018-09-14 04:57:13 -0600629 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-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 Glasseddd7292018-09-14 04:57:13 -0600637 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600638
639 # This should fail since it would need to increase the device-tree size
Simon Glasseddd7292018-09-14 04:57:13 -0600640 self.node.AddZeroProp('four')
Simon Glass04afdfe2022-07-30 20:57:10 -0600641 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glasseddd7292018-09-14 04:57:13 -0600642 self.dtb.Sync(auto_resize=False)
Simon Glass04afdfe2022-07-30 20:57:10 -0600643 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glassccd25262018-09-14 04:57:16 -0600644 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600645
Simon Glass04afdfe2022-07-30 20:57:10 -0600646 def test_add_more(self):
Simon Glassccd25262018-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 Glassa2af7302021-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 Glassccd25262018-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 Glass80025522022-01-29 14:14:04 -0700668 self.assertEqual(tools.to_bytes(val) + b'\0', data)
Simon Glassccd25262018-09-14 04:57:16 -0600669
670 self.fdt.pack()
671 self.node.SetString('string', val + 'x')
Simon Glass04afdfe2022-07-30 20:57:10 -0600672 with self.assertRaises(libfdt.FdtException) as exc:
Simon Glassccd25262018-09-14 04:57:16 -0600673 self.dtb.Sync(auto_resize=False)
Simon Glass04afdfe2022-07-30 20:57:10 -0600674 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
Simon Glassccd25262018-09-14 04:57:16 -0600675 self.node.SetString('string', val[:-1])
676
677 prop = self.node.props['string']
Simon Glass80025522022-01-29 14:14:04 -0700678 prop.SetData(tools.to_bytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600679 self.dtb.Sync(auto_resize=False)
680 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass80025522022-01-29 14:14:04 -0700681 self.assertEqual(tools.to_bytes(val), data)
Simon Glassccd25262018-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 Glass80025522022-01-29 14:14:04 -0700686 prop.SetData(tools.to_bytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600687 self.dtb.Sync(auto_resize=False)
688 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glass80025522022-01-29 14:14:04 -0700689 self.assertEqual(tools.to_bytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600690
Simon Glass1cd40082019-05-17 22:00:36 -0600691 self.node.SetData('empty', b'123')
692 self.assertEqual(b'123', prop.bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600693
Simon Glassf67c99c2020-07-09 18:39:44 -0600694 # Trying adding a lot of data at once
Simon Glass80025522022-01-29 14:14:04 -0700695 self.node.AddData('data', tools.get_bytes(65, 20000))
Simon Glassf67c99c2020-07-09 18:39:44 -0600696 self.dtb.Sync(auto_resize=True)
697
Simon Glass452be422022-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 Glass120fa002022-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 Glassb9b5cb32022-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 Glass04afdfe2022-07-30 20:57:10 -0600721 def test_from_data(self):
722 """Test creating an FDT from data"""
Simon Glassb8a49292018-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 Glass04afdfe2022-07-30 20:57:10 -0600730 def test_missing_set_int(self):
Simon Glassa683a5f2019-07-20 12:23:37 -0600731 """Test handling of a missing property with SetInt"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600732 with self.assertRaises(ValueError) as exc:
Simon Glassa683a5f2019-07-20 12:23:37 -0600733 self.node.SetInt('one', 1)
734 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass04afdfe2022-07-30 20:57:10 -0600735 str(exc.exception))
Simon Glassa683a5f2019-07-20 12:23:37 -0600736
Simon Glass04afdfe2022-07-30 20:57:10 -0600737 def test_missing_set_data(self):
Simon Glassa683a5f2019-07-20 12:23:37 -0600738 """Test handling of a missing property with SetData"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600739 with self.assertRaises(ValueError) as exc:
Simon Glassa683a5f2019-07-20 12:23:37 -0600740 self.node.SetData('one', b'data')
741 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass04afdfe2022-07-30 20:57:10 -0600742 str(exc.exception))
Simon Glassa683a5f2019-07-20 12:23:37 -0600743
Simon Glass04afdfe2022-07-30 20:57:10 -0600744 def test_missing_set_string(self):
Simon Glassa683a5f2019-07-20 12:23:37 -0600745 """Test handling of a missing property with SetString"""
Simon Glass04afdfe2022-07-30 20:57:10 -0600746 with self.assertRaises(ValueError) as exc:
Simon Glassa683a5f2019-07-20 12:23:37 -0600747 self.node.SetString('one', 1)
748 self.assertIn("node '/spl-test': Missing property 'one'",
Simon Glass04afdfe2022-07-30 20:57:10 -0600749 str(exc.exception))
Simon Glassa683a5f2019-07-20 12:23:37 -0600750
Simon Glass04afdfe2022-07-30 20:57:10 -0600751 def test_get_filename(self):
Simon Glass74f5feb2019-07-20 12:24:08 -0600752 """Test the dtb filename can be provided"""
Simon Glass80025522022-01-29 14:14:04 -0700753 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glass74f5feb2019-07-20 12:24:08 -0600754 self.dtb.GetFilename())
755
Simon Glass0ed50752018-07-06 10:27:24 -0600756
Simon Glass9c526332018-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 Glass80025522022-01-29 14:14:04 -0700766 tools.prepare_output_dir(None)
Simon Glass9c526332018-07-06 10:27:28 -0600767
Simon Glass752e7552018-10-01 21:12:41 -0600768 @classmethod
769 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700770 tools.finalise_output_dir()
Simon Glass752e7552018-10-01 21:12:41 -0600771
Simon Glass9c526332018-07-06 10:27:28 -0600772 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700773 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600774 self.node = self.dtb.GetNode('/spl-test')
775
Simon Glass04afdfe2022-07-30 20:57:10 -0600776 def test_get_int(self):
777 """Test getting an int from a node"""
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600781 with self.assertRaises(ValueError) as exc:
Simon Glass3b55e3f2021-11-23 11:03:39 -0700782 fdt_util.GetInt(self.node, 'intarray')
Simon Glass9c526332018-07-06 10:27:28 -0600783 self.assertIn("property 'intarray' has list value: expecting a single "
Simon Glass04afdfe2022-07-30 20:57:10 -0600784 'integer', str(exc.exception))
Simon Glass9c526332018-07-06 10:27:28 -0600785
Simon Glass04afdfe2022-07-30 20:57:10 -0600786 def test_get_int64(self):
787 """Test getting a 64-bit int from a node"""
Simon Glass3b55e3f2021-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 Glass04afdfe2022-07-30 20:57:10 -0600792 with self.assertRaises(ValueError) as exc:
Simon Glass3b55e3f2021-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 Glass04afdfe2022-07-30 20:57:10 -0600796 str(exc.exception))
Simon Glass3b55e3f2021-11-23 11:03:39 -0700797
Simon Glass04afdfe2022-07-30 20:57:10 -0600798 def test_get_string(self):
799 """Test getting a string from a node"""
Simon Glass9c526332018-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 Glass120fa002022-03-05 20:18:56 -0700803 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
Simon Glass9c526332018-07-06 10:27:28 -0600804
Simon Glass04afdfe2022-07-30 20:57:10 -0600805 with self.assertRaises(ValueError) as exc:
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600808 'single string', str(exc.exception))
Simon Glass9c526332018-07-06 10:27:28 -0600809
Simon Glass04afdfe2022-07-30 20:57:10 -0600810 def test_get_string_list(self):
811 """Test getting a string list from a node"""
Simon Glassb2e88612021-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 Glass120fa002022-03-05 20:18:56 -0700819 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
Simon Glassb2e88612021-11-23 21:09:51 -0700820
Simon Glass04afdfe2022-07-30 20:57:10 -0600821 def test_get_args(self):
822 """Test getting arguments from a node"""
Simon Glass738a54d2022-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 Glassc6b3cdc2022-03-05 20:18:52 -0700829 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
Simon Glass738a54d2022-02-08 11:49:53 -0700830 fdt_util.GetArgs(node, 'args'))
Simon Glassc6b3cdc2022-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 Glass738a54d2022-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 Glass04afdfe2022-07-30 20:57:10 -0600841 def test_get_bool(self):
842 """Test getting a bool from a node"""
Simon Glass9c526332018-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 Glass04afdfe2022-07-30 20:57:10 -0600848 def test_get_byte(self):
849 """Test getting a byte from a node"""
Simon Glass53f53992018-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 Glass04afdfe2022-07-30 20:57:10 -0600853 with self.assertRaises(ValueError) as exc:
Simon Glass53f53992018-07-17 13:25:40 -0600854 fdt_util.GetByte(self.node, 'longbytearray')
855 self.assertIn("property 'longbytearray' has list value: expecting a "
Simon Glass04afdfe2022-07-30 20:57:10 -0600856 'single byte', str(exc.exception))
Simon Glass53f53992018-07-17 13:25:40 -0600857
Simon Glass04afdfe2022-07-30 20:57:10 -0600858 with self.assertRaises(ValueError) as exc:
Simon Glass53f53992018-07-17 13:25:40 -0600859 fdt_util.GetByte(self.node, 'intval')
860 self.assertIn("property 'intval' has length 4, expecting 1",
Simon Glass04afdfe2022-07-30 20:57:10 -0600861 str(exc.exception))
Simon Glass53f53992018-07-17 13:25:40 -0600862
Simon Glass04afdfe2022-07-30 20:57:10 -0600863 def test_get_bytes(self):
864 """Test getting multiple bytes from a node"""
Simon Glass0e055bf2021-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 Glass04afdfe2022-07-30 20:57:10 -0600870 with self.assertRaises(ValueError) as exc:
Simon Glass0e055bf2021-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 Glass04afdfe2022-07-30 20:57:10 -0600874 str(exc.exception))
Simon Glass0e055bf2021-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 Glass04afdfe2022-07-30 20:57:10 -0600881 def test_get_phandle_list(self):
882 """Test getting a list of phandles from a node"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700883 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-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 Glass04afdfe2022-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 Glass91710b32018-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 Glass04afdfe2022-07-30 20:57:10 -0600896 with self.assertRaises(ValueError):
Simon Glass91710b32018-07-17 13:25:32 -0600897 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
898 bool))
Simon Glass04afdfe2022-07-30 20:57:10 -0600899 def test_fdt_cells_to_cpu(self):
900 """Test getting cells with the correct endianness"""
Simon Glass9c526332018-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 Glass4f4b2402021-02-03 06:00:56 -0700905 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glassb0a34a42019-05-17 22:00:40 -0600906 node1 = dtb2.GetNode('/test1')
907 val = node1.props['reg'].value
Simon Glass9c526332018-07-06 10:27:28 -0600908 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
909
Simon Glassb0a34a42019-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 Glass04afdfe2022-07-30 20:57:10 -0600917 def test_ensure_compiled(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600918 """Test a degenerate case of this function (file already compiled)"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700919 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600920 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
921
Simon Glass04afdfe2022-07-30 20:57:10 -0600922 def test_ensure_compiled_tmpdir(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600923 """Test providing a temporary directory"""
Heinrich Schuchardte72dcd42023-04-20 20:03:43 +0200924 old_outdir = tools.outdir
Simon Glassb8d2daa2019-07-20 12:23:49 -0600925 try:
Simon Glassb8d2daa2019-07-20 12:23:49 -0600926 tools.outdir= None
927 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glass4f4b2402021-02-03 06:00:56 -0700928 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassb8d2daa2019-07-20 12:23:49 -0600929 tmpdir)
930 self.assertEqual(tmpdir, os.path.dirname(dtb))
931 shutil.rmtree(tmpdir)
932 finally:
Heinrich Schuchardte72dcd42023-04-20 20:03:43 +0200933 tools.outdir = old_outdir
Simon Glassb8d2daa2019-07-20 12:23:49 -0600934
Simon Glasse8cea0e2023-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 Glass9c526332018-07-06 10:27:28 -0600946
Simon Glass9852bef2022-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 Glass1b53d902022-01-29 14:14:14 -0700953 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass131444f2023-02-23 18:18:04 -0700954 ['tools/patman/*.py', 'tools/u_boot_pylib/*', '*test_fdt.py'],
955 build_dir)
Simon Glass9c526332018-07-06 10:27:28 -0600956
957
Simon Glass882f6322022-07-30 20:57:09 -0600958def run_tests(names, processes):
Simon Glass0ed50752018-07-06 10:27:24 -0600959 """Run all the test we have for the fdt model
960
961 Args:
Simon Glass882f6322022-07-30 20:57:09 -0600962 names (list of str): List of test names provided. Only the first is used
Simon Glass8726f662022-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 Glass77164ee2022-03-18 18:01:50 -0600966
967 Returns:
Simon Glass8726f662022-07-30 20:57:06 -0600968 int: Return code, 0 on success
Simon Glass0ed50752018-07-06 10:27:24 -0600969 """
Simon Glass882f6322022-07-30 20:57:09 -0600970 test_name = names[0] if names else None
Alper Nebi Yasakca1c5882022-04-02 20:06:06 +0300971 result = test_util.run_test_suites(
Simon Glassb6ec10c2022-07-30 20:57:07 -0600972 'test_fdt', False, False, False, processes, test_name, None,
Simon Glass77164ee2022-03-18 18:01:50 -0600973 [TestFdt, TestNode, TestProp, TestFdtUtil])
Simon Glass0ed50752018-07-06 10:27:24 -0600974
Alper Nebi Yasakca1c5882022-04-02 20:06:06 +0300975 return (0 if result.wasSuccessful() else 1)
976
Simon Glass0ed50752018-07-06 10:27:24 -0600977
Simon Glass9954cda2022-07-30 20:57:08 -0600978def main():
979 """Main program for this tool"""
Simon Glass882f6322022-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 Glass0ed50752018-07-06 10:27:24 -0600992
Simon Glass9954cda2022-07-30 20:57:08 -0600993 # Run our meagre tests
Simon Glass882f6322022-07-30 20:57:09 -0600994 if args.test:
995 ret_code = run_tests(args.name, args.processes)
Simon Glass9954cda2022-07-30 20:57:08 -0600996 return ret_code
Simon Glass882f6322022-07-30 20:57:09 -0600997 if args.test_coverage:
998 run_test_coverage(args.build_dir)
Simon Glass9954cda2022-07-30 20:57:08 -0600999 return 0
Simon Glass0ed50752018-07-06 10:27:24 -06001000
Simon Glass9954cda2022-07-30 20:57:08 -06001001if __name__ == '__main__':
1002 sys.exit(main())