blob: 914ed6aed59f30d2a8ea8541fb6ed019e46fc2bf [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+
3# Copyright (c) 2018 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6
7from optparse import OptionParser
8import glob
9import os
Simon Glassb8d2daa2019-07-20 12:23:49 -060010import shutil
Simon Glass0ed50752018-07-06 10:27:24 -060011import sys
Simon Glassb8d2daa2019-07-20 12:23:49 -060012import tempfile
Simon Glass0ed50752018-07-06 10:27:24 -060013import unittest
14
15# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glass42143162020-04-17 18:09:05 -060017sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass0ed50752018-07-06 10:27:24 -060018
Simon Glass7e10f8a2021-11-23 11:03:38 -070019# Bring in the libfdt module
20sys.path.insert(2, 'scripts/dtc/pylibfdt')
21sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
22sys.path.insert(2, os.path.join(our_path,
23 '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
24
Simon Glassa997ea52020-04-17 18:09:04 -060025from dtoc import fdt
26from dtoc import fdt_util
Simon Glass3b55e3f2021-11-23 11:03:39 -070027from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
Simon Glass2b160072022-02-11 13:23:20 -070028from dtoc.fdt import Type, BytesToValue
Simon Glass0ed50752018-07-06 10:27:24 -060029import libfdt
Simon Glassa997ea52020-04-17 18:09:04 -060030from patman import command
31from patman import test_util
32from patman import tools
Simon Glass0ed50752018-07-06 10:27:24 -060033
Simon Glass4df8a0c2018-07-06 10:27:29 -060034def _GetPropertyValue(dtb, node, prop_name):
35 """Low-level function to get the property value based on its offset
36
37 This looks directly in the device tree at the property's offset to find
38 its value. It is useful as a check that the property is in the correct
39 place.
40
41 Args:
42 node: Node to look in
43 prop_name: Property name to find
44
45 Returns:
46 Tuple:
47 Prop object found
48 Value of property as a string (found using property offset)
49 """
50 prop = node.props[prop_name]
51
52 # Add 12, which is sizeof(struct fdt_property), to get to start of data
53 offset = prop.GetOffset() + 12
54 data = dtb.GetContents()[offset:offset + len(prop.value)]
Simon Glass632b84c2020-11-08 20:36:20 -070055 return prop, [chr(x) for x in data]
Simon Glass4df8a0c2018-07-06 10:27:29 -060056
Simon Glass4f4b2402021-02-03 06:00:56 -070057def find_dtb_file(dts_fname):
58 """Locate a test file in the test/ directory
59
60 Args:
61 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
62
63 Returns:
64 str: Path to the test filename
65 """
66 return os.path.join('tools/dtoc/test', dts_fname)
67
Simon Glass4df8a0c2018-07-06 10:27:29 -060068
Simon Glass0ed50752018-07-06 10:27:24 -060069class TestFdt(unittest.TestCase):
70 """Tests for the Fdt module
71
72 This includes unit tests for some functions and functional tests for the fdt
73 module.
74 """
75 @classmethod
76 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -070077 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -060078
79 @classmethod
80 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -070081 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -060082
83 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -070084 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -060085
86 def testFdt(self):
87 """Test that we can open an Fdt"""
88 self.dtb.Scan()
89 root = self.dtb.GetRoot()
90 self.assertTrue(isinstance(root, fdt.Node))
91
92 def testGetNode(self):
93 """Test the GetNode() method"""
94 node = self.dtb.GetNode('/spl-test')
95 self.assertTrue(isinstance(node, fdt.Node))
Simon Glass3b9a8292019-07-20 12:23:39 -060096
Simon Glass0ed50752018-07-06 10:27:24 -060097 node = self.dtb.GetNode('/i2c@0/pmic@9')
98 self.assertTrue(isinstance(node, fdt.Node))
99 self.assertEqual('pmic@9', node.name)
Simon Glass9c526332018-07-06 10:27:28 -0600100 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600101
Simon Glass3b9a8292019-07-20 12:23:39 -0600102 node = self.dtb.GetNode('/')
103 self.assertTrue(isinstance(node, fdt.Node))
104 self.assertEqual(0, node.Offset())
105
Simon Glass0ed50752018-07-06 10:27:24 -0600106 def testFlush(self):
107 """Check that we can flush the device tree out to its file"""
108 fname = self.dtb._fname
Simon Glass80d54ee2019-05-17 22:00:39 -0600109 with open(fname, 'rb') as fd:
Simon Glass0ed50752018-07-06 10:27:24 -0600110 data = fd.read()
111 os.remove(fname)
112 with self.assertRaises(IOError):
Simon Glass80d54ee2019-05-17 22:00:39 -0600113 open(fname, 'rb')
Simon Glass0ed50752018-07-06 10:27:24 -0600114 self.dtb.Flush()
Simon Glass80d54ee2019-05-17 22:00:39 -0600115 with open(fname, 'rb') as fd:
Simon Glass0ed50752018-07-06 10:27:24 -0600116 data = fd.read()
117
118 def testPack(self):
119 """Test that packing a device tree works"""
120 self.dtb.Pack()
121
Simon Glass2b160072022-02-11 13:23:20 -0700122 def testGetFdtRaw(self):
Simon Glass0ed50752018-07-06 10:27:24 -0600123 """Tetst that we can access the raw device-tree data"""
Simon Glass2b160072022-02-11 13:23:20 -0700124 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
Simon Glass0ed50752018-07-06 10:27:24 -0600125
126 def testGetProps(self):
127 """Tests obtaining a list of properties"""
128 node = self.dtb.GetNode('/spl-test')
129 props = self.dtb.GetProps(node)
130 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
Simon Glass3b55e3f2021-11-23 11:03:39 -0700131 'int64val', 'intarray', 'intval', 'longbytearray',
Simon Glass43118322021-07-28 19:23:11 -0600132 'maybe-empty-int', 'notstring', 'stringarray',
133 'stringval', 'u-boot,dm-pre-reloc'],
Simon Glass0ed50752018-07-06 10:27:24 -0600134 sorted(props.keys()))
135
136 def testCheckError(self):
137 """Tests the ChecKError() function"""
138 with self.assertRaises(ValueError) as e:
Simon Glass9c526332018-07-06 10:27:28 -0600139 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass0ed50752018-07-06 10:27:24 -0600140 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
141
Simon Glasse2d65282018-07-17 13:25:46 -0600142 def testGetFdt(self):
143 node = self.dtb.GetNode('/spl-test')
144 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass0ed50752018-07-06 10:27:24 -0600145
Simon Glass5872f0c2019-05-17 22:00:41 -0600146 def testBytesToValue(self):
147 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glassc9a032c2020-11-08 20:36:17 -0700148 (Type.STRING, ['this', 'is']))
Simon Glass5872f0c2019-05-17 22:00:41 -0600149
Simon Glass0ed50752018-07-06 10:27:24 -0600150class TestNode(unittest.TestCase):
151 """Test operation of the Node class"""
152
153 @classmethod
154 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700155 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -0600156
157 @classmethod
158 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700159 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -0600160
161 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700162 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600163 self.node = self.dtb.GetNode('/spl-test')
Simon Glass244548f2021-03-21 18:24:37 +1300164 self.fdt = self.dtb.GetFdtObj()
Simon Glass0ed50752018-07-06 10:27:24 -0600165
166 def testOffset(self):
167 """Tests that we can obtain the offset of a node"""
168 self.assertTrue(self.node.Offset() > 0)
169
170 def testDelete(self):
171 """Tests that we can delete a property"""
172 node2 = self.dtb.GetNode('/spl-test2')
173 offset1 = node2.Offset()
174 self.node.DeleteProp('intval')
175 offset2 = node2.Offset()
176 self.assertTrue(offset2 < offset1)
177 self.node.DeleteProp('intarray')
178 offset3 = node2.Offset()
179 self.assertTrue(offset3 < offset2)
Simon Glass9c526332018-07-06 10:27:28 -0600180 with self.assertRaises(libfdt.FdtException):
181 self.node.DeleteProp('missing')
Simon Glass0ed50752018-07-06 10:27:24 -0600182
Simon Glass4df8a0c2018-07-06 10:27:29 -0600183 def testDeleteGetOffset(self):
184 """Test that property offset update when properties are deleted"""
185 self.node.DeleteProp('intval')
186 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
187 self.assertEqual(prop.value, value)
188
Simon Glass0ed50752018-07-06 10:27:24 -0600189 def testFindNode(self):
Simon Glassaa1a5d72018-07-17 13:25:41 -0600190 """Tests that we can find a node using the FindNode() functoin"""
191 node = self.dtb.GetRoot().FindNode('i2c@0')
Simon Glass0ed50752018-07-06 10:27:24 -0600192 self.assertEqual('i2c@0', node.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600193 subnode = node.FindNode('pmic@9')
Simon Glass0ed50752018-07-06 10:27:24 -0600194 self.assertEqual('pmic@9', subnode.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600195 self.assertEqual(None, node.FindNode('missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600196
Simon Glass4df8a0c2018-07-06 10:27:29 -0600197 def testRefreshMissingNode(self):
198 """Test refreshing offsets when an extra node is present in dtb"""
199 # Delete it from our tables, not the device tree
200 del self.dtb._root.subnodes[-1]
201 with self.assertRaises(ValueError) as e:
202 self.dtb.Refresh()
203 self.assertIn('Internal error, offset', str(e.exception))
204
205 def testRefreshExtraNode(self):
206 """Test refreshing offsets when an expected node is missing"""
207 # Delete it from the device tre, not our tables
Simon Glass244548f2021-03-21 18:24:37 +1300208 self.fdt.del_node(self.node.Offset())
Simon Glass4df8a0c2018-07-06 10:27:29 -0600209 with self.assertRaises(ValueError) as e:
210 self.dtb.Refresh()
211 self.assertIn('Internal error, node name mismatch '
212 'spl-test != spl-test2', str(e.exception))
213
214 def testRefreshMissingProp(self):
215 """Test refreshing offsets when an extra property is present in dtb"""
216 # Delete it from our tables, not the device tree
217 del self.node.props['notstring']
218 with self.assertRaises(ValueError) as e:
219 self.dtb.Refresh()
Simon Glass93f18a12021-03-21 18:24:34 +1300220 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glass4df8a0c2018-07-06 10:27:29 -0600221 str(e.exception))
222
Simon Glasse2d65282018-07-17 13:25:46 -0600223 def testLookupPhandle(self):
224 """Test looking up a single phandle"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700225 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600226 node = dtb.GetNode('/phandle-source2')
227 prop = node.props['clocks']
228 target = dtb.GetNode('/phandle-target')
229 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
230
Simon Glass244548f2021-03-21 18:24:37 +1300231 def testAddNodeSpace(self):
232 """Test adding a single node when out of space"""
233 self.fdt.pack()
234 self.node.AddSubnode('subnode')
235 with self.assertRaises(libfdt.FdtException) as e:
236 self.dtb.Sync(auto_resize=False)
237 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
238
239 self.dtb.Sync(auto_resize=True)
240 offset = self.fdt.path_offset('/spl-test/subnode')
241 self.assertTrue(offset > 0)
242
243 def testAddNodes(self):
244 """Test adding various subnode and properies"""
245 node = self.dtb.GetNode('/i2c@0')
246
Simon Glass3be798a2021-03-21 18:24:38 +1300247 # Add one more node next to the pmic one
248 sn1 = node.AddSubnode('node-one')
249 sn1.AddInt('integer-a', 12)
250 sn1.AddInt('integer-b', 23)
251
252 # Sync so that everything is clean
253 self.dtb.Sync(auto_resize=True)
254
255 # Add two subnodes next to pmic and node-one
256 sn2 = node.AddSubnode('node-two')
257 sn2.AddInt('integer-2a', 34)
258 sn2.AddInt('integer-2b', 45)
259
260 sn3 = node.AddSubnode('node-three')
261 sn3.AddInt('integer-3', 123)
262
Simon Glass244548f2021-03-21 18:24:37 +1300263 # Add a property to the node after i2c@0 to check that this is not
264 # disturbed by adding a subnode to i2c@0
265 orig_node = self.dtb.GetNode('/orig-node')
266 orig_node.AddInt('integer-4', 456)
267
268 # Add a property to the pmic node to check that pmic properties are not
269 # disturbed
270 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
271 pmic.AddInt('integer-5', 567)
272
273 self.dtb.Sync(auto_resize=True)
274
Simon Glass00238612022-02-08 11:49:52 -0700275 def testAddOneNode(self):
276 """Testing deleting and adding a subnode before syncing"""
277 subnode = self.node.AddSubnode('subnode')
278 self.node.AddSubnode('subnode2')
279 self.dtb.Sync(auto_resize=True)
280
281 # Delete a node and add a new one
282 subnode.Delete()
283 self.node.AddSubnode('subnode3')
284 self.dtb.Sync()
285
Simon Glassd8bee462021-03-21 18:24:39 +1300286 def testRefreshNameMismatch(self):
287 """Test name mismatch when syncing nodes and properties"""
288 prop = self.node.AddInt('integer-a', 12)
289
290 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
291 self.node._offset = wrong_offset
292 with self.assertRaises(ValueError) as e:
293 self.dtb.Sync()
294 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
295 str(e.exception))
296
297 with self.assertRaises(ValueError) as e:
298 self.node.Refresh(wrong_offset)
299 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
300 str(e.exception))
301
Simon Glass0ed50752018-07-06 10:27:24 -0600302
303class TestProp(unittest.TestCase):
304 """Test operation of the Prop class"""
305
306 @classmethod
307 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700308 tools.prepare_output_dir(None)
Simon Glass0ed50752018-07-06 10:27:24 -0600309
310 @classmethod
311 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700312 tools.finalise_output_dir()
Simon Glass0ed50752018-07-06 10:27:24 -0600313
314 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700315 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600316 self.node = self.dtb.GetNode('/spl-test')
317 self.fdt = self.dtb.GetFdtObj()
318
Simon Glassc5eddc82018-07-06 10:27:30 -0600319 def testMissingNode(self):
320 self.assertEqual(None, self.dtb.GetNode('missing'))
321
Simon Glass9c526332018-07-06 10:27:28 -0600322 def testPhandle(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700323 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass609e2b12018-07-06 10:27:31 -0600324 node = dtb.GetNode('/phandle-source2')
325 prop = node.props['clocks']
326 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass9c526332018-07-06 10:27:28 -0600327
328 def _ConvertProp(self, prop_name):
329 """Helper function to look up a property in self.node and return it
330
331 Args:
332 Property name to find
333
334 Return fdt.Prop object for this property
335 """
Simon Glassb474c762018-07-26 14:02:13 -0600336 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass9c526332018-07-06 10:27:28 -0600337 return fdt.Prop(self.node, -1, prop_name, p)
338
339 def testMakeProp(self):
340 """Test we can convert all the the types that are supported"""
341 prop = self._ConvertProp('boolval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700342 self.assertEqual(Type.BOOL, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600343 self.assertEqual(True, prop.value)
344
345 prop = self._ConvertProp('intval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700346 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600347 self.assertEqual(1, fdt32_to_cpu(prop.value))
348
Simon Glass3b55e3f2021-11-23 11:03:39 -0700349 prop = self._ConvertProp('int64val')
350 self.assertEqual(Type.INT, prop.type)
351 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
352
Simon Glass9c526332018-07-06 10:27:28 -0600353 prop = self._ConvertProp('intarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700354 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600355 val = [fdt32_to_cpu(val) for val in prop.value]
356 self.assertEqual([2, 3, 4], val)
357
358 prop = self._ConvertProp('byteval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700359 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600360 self.assertEqual(5, ord(prop.value))
361
362 prop = self._ConvertProp('longbytearray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700363 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600364 val = [ord(val) for val in prop.value]
365 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
366
367 prop = self._ConvertProp('stringval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700368 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600369 self.assertEqual('message', prop.value)
370
371 prop = self._ConvertProp('stringarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700372 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600373 self.assertEqual(['multi-word', 'message'], prop.value)
374
375 prop = self._ConvertProp('notstring')
Simon Glassc9a032c2020-11-08 20:36:17 -0700376 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600377 val = [ord(val) for val in prop.value]
378 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
379
Simon Glass0ed50752018-07-06 10:27:24 -0600380 def testGetEmpty(self):
381 """Tests the GetEmpty() function for the various supported types"""
Simon Glassc9a032c2020-11-08 20:36:17 -0700382 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
383 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
Simon Glass80025522022-01-29 14:14:04 -0700384 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
Simon Glassc9a032c2020-11-08 20:36:17 -0700385 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass0ed50752018-07-06 10:27:24 -0600386
387 def testGetOffset(self):
388 """Test we can get the offset of a property"""
Simon Glass4df8a0c2018-07-06 10:27:29 -0600389 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
390 self.assertEqual(prop.value, value)
Simon Glass0ed50752018-07-06 10:27:24 -0600391
392 def testWiden(self):
393 """Test widening of values"""
394 node2 = self.dtb.GetNode('/spl-test2')
Simon Glass8034e4d2020-10-03 11:31:27 -0600395 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass0ed50752018-07-06 10:27:24 -0600396 prop = self.node.props['intval']
397
398 # No action
399 prop2 = node2.props['intval']
400 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700401 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600402 self.assertEqual(1, fdt32_to_cpu(prop.value))
403
Simon Glassa7d66982021-07-28 19:23:10 -0600404 # Convert single value to array
Simon Glass0ed50752018-07-06 10:27:24 -0600405 prop2 = self.node.props['intarray']
406 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700407 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600408 self.assertTrue(isinstance(prop.value, list))
409
410 # A 4-byte array looks like a single integer. When widened by a longer
411 # byte array, it should turn into an array.
412 prop = self.node.props['longbytearray']
413 prop2 = node2.props['longbytearray']
Simon Glass8034e4d2020-10-03 11:31:27 -0600414 prop3 = node3.props['longbytearray']
Simon Glass0ed50752018-07-06 10:27:24 -0600415 self.assertFalse(isinstance(prop2.value, list))
416 self.assertEqual(4, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600417 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600418 prop2.Widen(prop)
419 self.assertTrue(isinstance(prop2.value, list))
420 self.assertEqual(9, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600421 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
422 '\0', '\0', '\0', '\0'], prop2.value)
423 prop3.Widen(prop)
424 self.assertTrue(isinstance(prop3.value, list))
425 self.assertEqual(9, len(prop3.value))
426 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
427 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600428
429 # Similarly for a string array
430 prop = self.node.props['stringval']
431 prop2 = node2.props['stringarray']
432 self.assertFalse(isinstance(prop.value, list))
433 self.assertEqual(7, len(prop.value))
434 prop.Widen(prop2)
435 self.assertTrue(isinstance(prop.value, list))
436 self.assertEqual(3, len(prop.value))
437
438 # Enlarging an existing array
439 prop = self.node.props['stringarray']
440 prop2 = node2.props['stringarray']
441 self.assertTrue(isinstance(prop.value, list))
442 self.assertEqual(2, len(prop.value))
443 prop.Widen(prop2)
444 self.assertTrue(isinstance(prop.value, list))
445 self.assertEqual(3, len(prop.value))
446
Simon Glassa7d66982021-07-28 19:23:10 -0600447 # Widen an array of ints with an int (should do nothing)
448 prop = self.node.props['intarray']
Simon Glassf42f26e2021-08-02 07:37:54 -0600449 prop2 = node2.props['intval']
Simon Glassa7d66982021-07-28 19:23:10 -0600450 self.assertEqual(Type.INT, prop.type)
451 self.assertEqual(3, len(prop.value))
452 prop.Widen(prop2)
453 self.assertEqual(Type.INT, prop.type)
454 self.assertEqual(3, len(prop.value))
455
Simon Glass43118322021-07-28 19:23:11 -0600456 # Widen an empty bool to an int
457 prop = self.node.props['maybe-empty-int']
458 prop3 = node3.props['maybe-empty-int']
459 self.assertEqual(Type.BOOL, prop.type)
460 self.assertEqual(True, prop.value)
461 self.assertEqual(Type.INT, prop3.type)
462 self.assertFalse(isinstance(prop.value, list))
463 self.assertEqual(4, len(prop3.value))
464 prop.Widen(prop3)
465 self.assertEqual(Type.INT, prop.type)
466 self.assertTrue(isinstance(prop.value, list))
467 self.assertEqual(1, len(prop.value))
468
Simon Glasse80c5562018-07-06 10:27:38 -0600469 def testAdd(self):
470 """Test adding properties"""
471 self.fdt.pack()
472 # This function should automatically expand the device tree
473 self.node.AddZeroProp('one')
474 self.node.AddZeroProp('two')
475 self.node.AddZeroProp('three')
Simon Glasseddd7292018-09-14 04:57:13 -0600476 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600477
478 # Updating existing properties should be OK, since the device-tree size
479 # does not change
480 self.fdt.pack()
481 self.node.SetInt('one', 1)
482 self.node.SetInt('two', 2)
483 self.node.SetInt('three', 3)
Simon Glasseddd7292018-09-14 04:57:13 -0600484 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600485
486 # This should fail since it would need to increase the device-tree size
Simon Glasseddd7292018-09-14 04:57:13 -0600487 self.node.AddZeroProp('four')
Simon Glasse80c5562018-07-06 10:27:38 -0600488 with self.assertRaises(libfdt.FdtException) as e:
Simon Glasseddd7292018-09-14 04:57:13 -0600489 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600490 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glassccd25262018-09-14 04:57:16 -0600491 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600492
Simon Glassccd25262018-09-14 04:57:16 -0600493 def testAddMore(self):
494 """Test various other methods for adding and setting properties"""
495 self.node.AddZeroProp('one')
496 self.dtb.Sync(auto_resize=True)
497 data = self.fdt.getprop(self.node.Offset(), 'one')
498 self.assertEqual(0, fdt32_to_cpu(data))
499
500 self.node.SetInt('one', 1)
501 self.dtb.Sync(auto_resize=False)
502 data = self.fdt.getprop(self.node.Offset(), 'one')
503 self.assertEqual(1, fdt32_to_cpu(data))
504
Simon Glassa2af7302021-01-06 21:35:18 -0700505 val = 1234
506 self.node.AddInt('integer', val)
507 self.dtb.Sync(auto_resize=True)
508 data = self.fdt.getprop(self.node.Offset(), 'integer')
509 self.assertEqual(val, fdt32_to_cpu(data))
510
Simon Glassccd25262018-09-14 04:57:16 -0600511 val = '123' + chr(0) + '456'
512 self.node.AddString('string', val)
513 self.dtb.Sync(auto_resize=True)
514 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass80025522022-01-29 14:14:04 -0700515 self.assertEqual(tools.to_bytes(val) + b'\0', data)
Simon Glassccd25262018-09-14 04:57:16 -0600516
517 self.fdt.pack()
518 self.node.SetString('string', val + 'x')
519 with self.assertRaises(libfdt.FdtException) as e:
520 self.dtb.Sync(auto_resize=False)
521 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
522 self.node.SetString('string', val[:-1])
523
524 prop = self.node.props['string']
Simon Glass80025522022-01-29 14:14:04 -0700525 prop.SetData(tools.to_bytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600526 self.dtb.Sync(auto_resize=False)
527 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass80025522022-01-29 14:14:04 -0700528 self.assertEqual(tools.to_bytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600529
530 self.node.AddEmptyProp('empty', 5)
531 self.dtb.Sync(auto_resize=True)
532 prop = self.node.props['empty']
Simon Glass80025522022-01-29 14:14:04 -0700533 prop.SetData(tools.to_bytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600534 self.dtb.Sync(auto_resize=False)
535 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glass80025522022-01-29 14:14:04 -0700536 self.assertEqual(tools.to_bytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600537
Simon Glass1cd40082019-05-17 22:00:36 -0600538 self.node.SetData('empty', b'123')
539 self.assertEqual(b'123', prop.bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600540
Simon Glassf67c99c2020-07-09 18:39:44 -0600541 # Trying adding a lot of data at once
Simon Glass80025522022-01-29 14:14:04 -0700542 self.node.AddData('data', tools.get_bytes(65, 20000))
Simon Glassf67c99c2020-07-09 18:39:44 -0600543 self.dtb.Sync(auto_resize=True)
544
Simon Glass452be422022-02-08 11:49:50 -0700545 def test_string_list(self):
546 """Test adding string-list property to a node"""
547 val = ['123', '456']
548 self.node.AddStringList('stringlist', val)
549 self.dtb.Sync(auto_resize=True)
550 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
551 self.assertEqual(b'123\x00456\0', data)
552
Simon Glass120fa002022-03-05 20:18:56 -0700553 val = []
554 self.node.AddStringList('stringlist', val)
555 self.dtb.Sync(auto_resize=True)
556 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
557 self.assertEqual(b'', data)
558
Simon Glassb9b5cb32022-02-08 11:49:51 -0700559 def test_delete_node(self):
560 """Test deleting a node"""
561 old_offset = self.fdt.path_offset('/spl-test')
562 self.assertGreater(old_offset, 0)
563 self.node.Delete()
564 self.dtb.Sync()
565 new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
566 self.assertEqual(-libfdt.NOTFOUND, new_offset)
567
Simon Glassb8a49292018-09-14 04:57:17 -0600568 def testFromData(self):
569 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
570 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
571
572 self.node.AddEmptyProp('empty', 5)
573 self.dtb.Sync(auto_resize=True)
574 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
575
Simon Glassa683a5f2019-07-20 12:23:37 -0600576 def testMissingSetInt(self):
577 """Test handling of a missing property with SetInt"""
578 with self.assertRaises(ValueError) as e:
579 self.node.SetInt('one', 1)
580 self.assertIn("node '/spl-test': Missing property 'one'",
581 str(e.exception))
582
583 def testMissingSetData(self):
584 """Test handling of a missing property with SetData"""
585 with self.assertRaises(ValueError) as e:
586 self.node.SetData('one', b'data')
587 self.assertIn("node '/spl-test': Missing property 'one'",
588 str(e.exception))
589
590 def testMissingSetString(self):
591 """Test handling of a missing property with SetString"""
592 with self.assertRaises(ValueError) as e:
593 self.node.SetString('one', 1)
594 self.assertIn("node '/spl-test': Missing property 'one'",
595 str(e.exception))
596
Simon Glass74f5feb2019-07-20 12:24:08 -0600597 def testGetFilename(self):
598 """Test the dtb filename can be provided"""
Simon Glass80025522022-01-29 14:14:04 -0700599 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glass74f5feb2019-07-20 12:24:08 -0600600 self.dtb.GetFilename())
601
Simon Glass0ed50752018-07-06 10:27:24 -0600602
Simon Glass9c526332018-07-06 10:27:28 -0600603class TestFdtUtil(unittest.TestCase):
604 """Tests for the fdt_util module
605
606 This module will likely be mostly replaced at some point, once upstream
607 libfdt has better Python support. For now, this provides tests for current
608 functionality.
609 """
610 @classmethod
611 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700612 tools.prepare_output_dir(None)
Simon Glass9c526332018-07-06 10:27:28 -0600613
Simon Glass752e7552018-10-01 21:12:41 -0600614 @classmethod
615 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700616 tools.finalise_output_dir()
Simon Glass752e7552018-10-01 21:12:41 -0600617
Simon Glass9c526332018-07-06 10:27:28 -0600618 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700619 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600620 self.node = self.dtb.GetNode('/spl-test')
621
622 def testGetInt(self):
623 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
624 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
625
626 with self.assertRaises(ValueError) as e:
Simon Glass3b55e3f2021-11-23 11:03:39 -0700627 fdt_util.GetInt(self.node, 'intarray')
Simon Glass9c526332018-07-06 10:27:28 -0600628 self.assertIn("property 'intarray' has list value: expecting a single "
629 'integer', str(e.exception))
630
Simon Glass3b55e3f2021-11-23 11:03:39 -0700631 def testGetInt64(self):
632 self.assertEqual(0x123456789abcdef0,
633 fdt_util.GetInt64(self.node, 'int64val'))
634 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
635
636 with self.assertRaises(ValueError) as e:
637 fdt_util.GetInt64(self.node, 'intarray')
638 self.assertIn(
639 "property 'intarray' should be a list with 2 items for 64-bit values",
640 str(e.exception))
641
Simon Glass9c526332018-07-06 10:27:28 -0600642 def testGetString(self):
643 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
644 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
645 'test'))
Simon Glass120fa002022-03-05 20:18:56 -0700646 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
Simon Glass9c526332018-07-06 10:27:28 -0600647
648 with self.assertRaises(ValueError) as e:
649 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
650 self.assertIn("property 'stringarray' has list value: expecting a "
651 'single string', str(e.exception))
652
Simon Glassb2e88612021-11-23 21:09:51 -0700653 def testGetStringList(self):
654 self.assertEqual(['message'],
655 fdt_util.GetStringList(self.node, 'stringval'))
656 self.assertEqual(
657 ['multi-word', 'message'],
658 fdt_util.GetStringList(self.node, 'stringarray'))
659 self.assertEqual(['test'],
660 fdt_util.GetStringList(self.node, 'missing', ['test']))
Simon Glass120fa002022-03-05 20:18:56 -0700661 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
Simon Glassb2e88612021-11-23 21:09:51 -0700662
Simon Glass738a54d2022-02-08 11:49:53 -0700663 def testGetArgs(self):
664 node = self.dtb.GetNode('/orig-node')
665 self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
666 self.assertEqual(
667 ['multi-word', 'message'],
668 fdt_util.GetArgs(self.node, 'stringarray'))
669 self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
Simon Glassc6b3cdc2022-03-05 20:18:52 -0700670 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
Simon Glass738a54d2022-02-08 11:49:53 -0700671 fdt_util.GetArgs(node, 'args'))
Simon Glassc6b3cdc2022-03-05 20:18:52 -0700672 self.assertEqual(['a space', 'there'],
673 fdt_util.GetArgs(node, 'args2'))
674 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
675 fdt_util.GetArgs(node, 'args3'))
Simon Glass738a54d2022-02-08 11:49:53 -0700676 with self.assertRaises(ValueError) as exc:
677 fdt_util.GetArgs(self.node, 'missing')
678 self.assertIn(
679 "Node '/spl-test': Expected property 'missing'",
680 str(exc.exception))
681
Simon Glass9c526332018-07-06 10:27:28 -0600682 def testGetBool(self):
683 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
684 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
685 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
686 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
687
Simon Glass53f53992018-07-17 13:25:40 -0600688 def testGetByte(self):
689 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
690 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
691
692 with self.assertRaises(ValueError) as e:
693 fdt_util.GetByte(self.node, 'longbytearray')
694 self.assertIn("property 'longbytearray' has list value: expecting a "
695 'single byte', str(e.exception))
696
697 with self.assertRaises(ValueError) as e:
698 fdt_util.GetByte(self.node, 'intval')
699 self.assertIn("property 'intval' has length 4, expecting 1",
700 str(e.exception))
701
Simon Glass0e055bf2021-11-23 11:03:40 -0700702 def testGetBytes(self):
703 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
704 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
705 self.assertEqual(
706 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
707
708 with self.assertRaises(ValueError) as e:
709 fdt_util.GetBytes(self.node, 'longbytearray', 7)
710 self.assertIn(
711 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
712 str(e.exception))
713
714 self.assertEqual(
715 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
716 self.assertEqual(
717 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
718
Simon Glasse2d65282018-07-17 13:25:46 -0600719 def testGetPhandleList(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700720 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600721 node = dtb.GetNode('/phandle-source2')
722 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
723 node = dtb.GetNode('/phandle-source')
724 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
725 fdt_util.GetPhandleList(node, 'clocks'))
726 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
727
Simon Glass91710b32018-07-17 13:25:32 -0600728 def testGetDataType(self):
729 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
730 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
731 str))
732 with self.assertRaises(ValueError) as e:
733 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
734 bool))
Simon Glass9c526332018-07-06 10:27:28 -0600735 def testFdtCellsToCpu(self):
736 val = self.node.props['intarray'].value
737 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
738 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
739
Simon Glass4f4b2402021-02-03 06:00:56 -0700740 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glassb0a34a42019-05-17 22:00:40 -0600741 node1 = dtb2.GetNode('/test1')
742 val = node1.props['reg'].value
Simon Glass9c526332018-07-06 10:27:28 -0600743 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
744
Simon Glassb0a34a42019-05-17 22:00:40 -0600745 node2 = dtb2.GetNode('/test2')
746 val = node2.props['reg'].value
747 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
748 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
749 2))
750 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
751
Simon Glass9c526332018-07-06 10:27:28 -0600752 def testEnsureCompiled(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600753 """Test a degenerate case of this function (file already compiled)"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700754 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600755 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
756
Simon Glassb8d2daa2019-07-20 12:23:49 -0600757 def testEnsureCompiledTmpdir(self):
758 """Test providing a temporary directory"""
759 try:
760 old_outdir = tools.outdir
761 tools.outdir= None
762 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glass4f4b2402021-02-03 06:00:56 -0700763 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassb8d2daa2019-07-20 12:23:49 -0600764 tmpdir)
765 self.assertEqual(tmpdir, os.path.dirname(dtb))
766 shutil.rmtree(tmpdir)
767 finally:
768 tools.outdir= old_outdir
769
Simon Glass9c526332018-07-06 10:27:28 -0600770
771def RunTestCoverage():
772 """Run the tests and check that we get 100% coverage"""
Simon Glass1b53d902022-01-29 14:14:14 -0700773 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass9c526332018-07-06 10:27:28 -0600774 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
775
776
Simon Glass0ed50752018-07-06 10:27:24 -0600777def RunTests(args):
778 """Run all the test we have for the fdt model
779
780 Args:
781 args: List of positional args provided to fdt. This can hold a test
782 name to execute (as in 'fdt -t testFdt', for example)
783 """
784 result = unittest.TestResult()
785 sys.argv = [sys.argv[0]]
786 test_name = args and args[0] or None
Simon Glass9c526332018-07-06 10:27:28 -0600787 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass0ed50752018-07-06 10:27:24 -0600788 if test_name:
789 try:
790 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
791 except AttributeError:
792 continue
793 else:
794 suite = unittest.TestLoader().loadTestsFromTestCase(module)
795 suite.run(result)
796
Simon Glass61b88e52019-05-17 22:00:31 -0600797 print(result)
Simon Glass0ed50752018-07-06 10:27:24 -0600798 for _, err in result.errors:
Simon Glass61b88e52019-05-17 22:00:31 -0600799 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600800 for _, err in result.failures:
Simon Glass61b88e52019-05-17 22:00:31 -0600801 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600802
803if __name__ != '__main__':
804 sys.exit(1)
805
806parser = OptionParser()
Simon Glass9c526332018-07-06 10:27:28 -0600807parser.add_option('-B', '--build-dir', type='string', default='b',
808 help='Directory containing the build output')
Simon Glass7057d022018-10-01 21:12:47 -0600809parser.add_option('-P', '--processes', type=int,
810 help='set number of processes to use for running tests')
Simon Glass0ed50752018-07-06 10:27:24 -0600811parser.add_option('-t', '--test', action='store_true', dest='test',
812 default=False, help='run tests')
Simon Glass9c526332018-07-06 10:27:28 -0600813parser.add_option('-T', '--test-coverage', action='store_true',
814 default=False, help='run tests and check for 100% coverage')
Simon Glass0ed50752018-07-06 10:27:24 -0600815(options, args) = parser.parse_args()
816
817# Run our meagre tests
818if options.test:
819 RunTests(args)
Simon Glass9c526332018-07-06 10:27:28 -0600820elif options.test_coverage:
821 RunTestCoverage()