blob: 576d65b97e8e1b296fd3e1c9c4efbbbebf75a963 [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 Glassc9a032c2020-11-08 20:36:17 -070028from 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
122 def testGetFdt(self):
123 """Tetst that we can access the raw device-tree data"""
Simon Glass792d2392018-07-06 10:27:27 -0600124 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
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 Glassb9b5cb32022-02-08 11:49:51 -0700553 def test_delete_node(self):
554 """Test deleting a node"""
555 old_offset = self.fdt.path_offset('/spl-test')
556 self.assertGreater(old_offset, 0)
557 self.node.Delete()
558 self.dtb.Sync()
559 new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
560 self.assertEqual(-libfdt.NOTFOUND, new_offset)
561
Simon Glassb8a49292018-09-14 04:57:17 -0600562 def testFromData(self):
563 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
564 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
565
566 self.node.AddEmptyProp('empty', 5)
567 self.dtb.Sync(auto_resize=True)
568 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
569
Simon Glassa683a5f2019-07-20 12:23:37 -0600570 def testMissingSetInt(self):
571 """Test handling of a missing property with SetInt"""
572 with self.assertRaises(ValueError) as e:
573 self.node.SetInt('one', 1)
574 self.assertIn("node '/spl-test': Missing property 'one'",
575 str(e.exception))
576
577 def testMissingSetData(self):
578 """Test handling of a missing property with SetData"""
579 with self.assertRaises(ValueError) as e:
580 self.node.SetData('one', b'data')
581 self.assertIn("node '/spl-test': Missing property 'one'",
582 str(e.exception))
583
584 def testMissingSetString(self):
585 """Test handling of a missing property with SetString"""
586 with self.assertRaises(ValueError) as e:
587 self.node.SetString('one', 1)
588 self.assertIn("node '/spl-test': Missing property 'one'",
589 str(e.exception))
590
Simon Glass74f5feb2019-07-20 12:24:08 -0600591 def testGetFilename(self):
592 """Test the dtb filename can be provided"""
Simon Glass80025522022-01-29 14:14:04 -0700593 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glass74f5feb2019-07-20 12:24:08 -0600594 self.dtb.GetFilename())
595
Simon Glass0ed50752018-07-06 10:27:24 -0600596
Simon Glass9c526332018-07-06 10:27:28 -0600597class TestFdtUtil(unittest.TestCase):
598 """Tests for the fdt_util module
599
600 This module will likely be mostly replaced at some point, once upstream
601 libfdt has better Python support. For now, this provides tests for current
602 functionality.
603 """
604 @classmethod
605 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700606 tools.prepare_output_dir(None)
Simon Glass9c526332018-07-06 10:27:28 -0600607
Simon Glass752e7552018-10-01 21:12:41 -0600608 @classmethod
609 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -0700610 tools.finalise_output_dir()
Simon Glass752e7552018-10-01 21:12:41 -0600611
Simon Glass9c526332018-07-06 10:27:28 -0600612 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700613 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600614 self.node = self.dtb.GetNode('/spl-test')
615
616 def testGetInt(self):
617 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
618 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
619
620 with self.assertRaises(ValueError) as e:
Simon Glass3b55e3f2021-11-23 11:03:39 -0700621 fdt_util.GetInt(self.node, 'intarray')
Simon Glass9c526332018-07-06 10:27:28 -0600622 self.assertIn("property 'intarray' has list value: expecting a single "
623 'integer', str(e.exception))
624
Simon Glass3b55e3f2021-11-23 11:03:39 -0700625 def testGetInt64(self):
626 self.assertEqual(0x123456789abcdef0,
627 fdt_util.GetInt64(self.node, 'int64val'))
628 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
629
630 with self.assertRaises(ValueError) as e:
631 fdt_util.GetInt64(self.node, 'intarray')
632 self.assertIn(
633 "property 'intarray' should be a list with 2 items for 64-bit values",
634 str(e.exception))
635
Simon Glass9c526332018-07-06 10:27:28 -0600636 def testGetString(self):
637 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
638 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
639 'test'))
640
641 with self.assertRaises(ValueError) as e:
642 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
643 self.assertIn("property 'stringarray' has list value: expecting a "
644 'single string', str(e.exception))
645
Simon Glassb2e88612021-11-23 21:09:51 -0700646 def testGetStringList(self):
647 self.assertEqual(['message'],
648 fdt_util.GetStringList(self.node, 'stringval'))
649 self.assertEqual(
650 ['multi-word', 'message'],
651 fdt_util.GetStringList(self.node, 'stringarray'))
652 self.assertEqual(['test'],
653 fdt_util.GetStringList(self.node, 'missing', ['test']))
654
Simon Glass738a54d2022-02-08 11:49:53 -0700655 def testGetArgs(self):
656 node = self.dtb.GetNode('/orig-node')
657 self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
658 self.assertEqual(
659 ['multi-word', 'message'],
660 fdt_util.GetArgs(self.node, 'stringarray'))
661 self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
662 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
663 fdt_util.GetArgs(node, 'args'))
664 with self.assertRaises(ValueError) as exc:
665 fdt_util.GetArgs(self.node, 'missing')
666 self.assertIn(
667 "Node '/spl-test': Expected property 'missing'",
668 str(exc.exception))
669
Simon Glass9c526332018-07-06 10:27:28 -0600670 def testGetBool(self):
671 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
672 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
673 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
674 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
675
Simon Glass53f53992018-07-17 13:25:40 -0600676 def testGetByte(self):
677 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
678 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
679
680 with self.assertRaises(ValueError) as e:
681 fdt_util.GetByte(self.node, 'longbytearray')
682 self.assertIn("property 'longbytearray' has list value: expecting a "
683 'single byte', str(e.exception))
684
685 with self.assertRaises(ValueError) as e:
686 fdt_util.GetByte(self.node, 'intval')
687 self.assertIn("property 'intval' has length 4, expecting 1",
688 str(e.exception))
689
Simon Glass0e055bf2021-11-23 11:03:40 -0700690 def testGetBytes(self):
691 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
692 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
693 self.assertEqual(
694 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
695
696 with self.assertRaises(ValueError) as e:
697 fdt_util.GetBytes(self.node, 'longbytearray', 7)
698 self.assertIn(
699 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
700 str(e.exception))
701
702 self.assertEqual(
703 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
704 self.assertEqual(
705 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
706
Simon Glasse2d65282018-07-17 13:25:46 -0600707 def testGetPhandleList(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700708 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600709 node = dtb.GetNode('/phandle-source2')
710 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
711 node = dtb.GetNode('/phandle-source')
712 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
713 fdt_util.GetPhandleList(node, 'clocks'))
714 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
715
Simon Glass91710b32018-07-17 13:25:32 -0600716 def testGetDataType(self):
717 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
718 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
719 str))
720 with self.assertRaises(ValueError) as e:
721 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
722 bool))
Simon Glass9c526332018-07-06 10:27:28 -0600723 def testFdtCellsToCpu(self):
724 val = self.node.props['intarray'].value
725 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
726 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
727
Simon Glass4f4b2402021-02-03 06:00:56 -0700728 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glassb0a34a42019-05-17 22:00:40 -0600729 node1 = dtb2.GetNode('/test1')
730 val = node1.props['reg'].value
Simon Glass9c526332018-07-06 10:27:28 -0600731 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
732
Simon Glassb0a34a42019-05-17 22:00:40 -0600733 node2 = dtb2.GetNode('/test2')
734 val = node2.props['reg'].value
735 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
736 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
737 2))
738 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
739
Simon Glass9c526332018-07-06 10:27:28 -0600740 def testEnsureCompiled(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600741 """Test a degenerate case of this function (file already compiled)"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700742 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600743 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
744
Simon Glassb8d2daa2019-07-20 12:23:49 -0600745 def testEnsureCompiledTmpdir(self):
746 """Test providing a temporary directory"""
747 try:
748 old_outdir = tools.outdir
749 tools.outdir= None
750 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glass4f4b2402021-02-03 06:00:56 -0700751 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassb8d2daa2019-07-20 12:23:49 -0600752 tmpdir)
753 self.assertEqual(tmpdir, os.path.dirname(dtb))
754 shutil.rmtree(tmpdir)
755 finally:
756 tools.outdir= old_outdir
757
Simon Glass9c526332018-07-06 10:27:28 -0600758
759def RunTestCoverage():
760 """Run the tests and check that we get 100% coverage"""
Simon Glass1b53d902022-01-29 14:14:14 -0700761 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass9c526332018-07-06 10:27:28 -0600762 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
763
764
Simon Glass0ed50752018-07-06 10:27:24 -0600765def RunTests(args):
766 """Run all the test we have for the fdt model
767
768 Args:
769 args: List of positional args provided to fdt. This can hold a test
770 name to execute (as in 'fdt -t testFdt', for example)
771 """
772 result = unittest.TestResult()
773 sys.argv = [sys.argv[0]]
774 test_name = args and args[0] or None
Simon Glass9c526332018-07-06 10:27:28 -0600775 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass0ed50752018-07-06 10:27:24 -0600776 if test_name:
777 try:
778 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
779 except AttributeError:
780 continue
781 else:
782 suite = unittest.TestLoader().loadTestsFromTestCase(module)
783 suite.run(result)
784
Simon Glass61b88e52019-05-17 22:00:31 -0600785 print(result)
Simon Glass0ed50752018-07-06 10:27:24 -0600786 for _, err in result.errors:
Simon Glass61b88e52019-05-17 22:00:31 -0600787 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600788 for _, err in result.failures:
Simon Glass61b88e52019-05-17 22:00:31 -0600789 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600790
791if __name__ != '__main__':
792 sys.exit(1)
793
794parser = OptionParser()
Simon Glass9c526332018-07-06 10:27:28 -0600795parser.add_option('-B', '--build-dir', type='string', default='b',
796 help='Directory containing the build output')
Simon Glass7057d022018-10-01 21:12:47 -0600797parser.add_option('-P', '--processes', type=int,
798 help='set number of processes to use for running tests')
Simon Glass0ed50752018-07-06 10:27:24 -0600799parser.add_option('-t', '--test', action='store_true', dest='test',
800 default=False, help='run tests')
Simon Glass9c526332018-07-06 10:27:28 -0600801parser.add_option('-T', '--test-coverage', action='store_true',
802 default=False, help='run tests and check for 100% coverage')
Simon Glass0ed50752018-07-06 10:27:24 -0600803(options, args) = parser.parse_args()
804
805# Run our meagre tests
806if options.test:
807 RunTests(args)
Simon Glass9c526332018-07-06 10:27:28 -0600808elif options.test_coverage:
809 RunTestCoverage()