blob: 55b70e9876409117d9569abcb2151459d4b1b6d8 [file] [log] [blame]
Simon Glassf7ba5f02019-10-31 07:42:54 -06001#!/usr/bin/env python3
Simon Glass2ba98752018-07-06 10:27:24 -06002# SPDX-License-Identifier: GPL-2.0+
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 Glassa004f292019-07-20 12:23:49 -060010import shutil
Simon Glass2ba98752018-07-06 10:27:24 -060011import sys
Simon Glassa004f292019-07-20 12:23:49 -060012import tempfile
Simon Glass2ba98752018-07-06 10:27:24 -060013import unittest
14
15# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glassb4fa9492020-04-17 18:09:05 -060017sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass2ba98752018-07-06 10:27:24 -060018
Simon Glassff139b62021-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 Glassbf776672020-04-17 18:09:04 -060025from dtoc import fdt
26from dtoc import fdt_util
Simon Glassd866e622021-11-23 11:03:39 -070027from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
Simon Glass5ea9dcc2020-11-08 20:36:17 -070028from fdt import Type, BytesToValue
Simon Glass2ba98752018-07-06 10:27:24 -060029import libfdt
Simon Glassbf776672020-04-17 18:09:04 -060030from patman import command
31from patman import test_util
32from patman import tools
Simon Glass2ba98752018-07-06 10:27:24 -060033
Simon Glassf9b88b32018-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 Glass479dd302020-11-08 20:36:20 -070055 return prop, [chr(x) for x in data]
Simon Glassf9b88b32018-07-06 10:27:29 -060056
Simon Glassdff51a52021-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 Glassf9b88b32018-07-06 10:27:29 -060068
Simon Glass2ba98752018-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):
77 tools.PrepareOutputDir(None)
78
79 @classmethod
80 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -060081 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -060082
83 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -070084 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-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 Glasse44bc832019-07-20 12:23:39 -060096
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600100 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600101
Simon Glasse44bc832019-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 Glass2ba98752018-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 Glass2ab6e132019-05-17 22:00:39 -0600109 with open(fname, 'rb') as fd:
Simon Glass2ba98752018-07-06 10:27:24 -0600110 data = fd.read()
111 os.remove(fname)
112 with self.assertRaises(IOError):
Simon Glass2ab6e132019-05-17 22:00:39 -0600113 open(fname, 'rb')
Simon Glass2ba98752018-07-06 10:27:24 -0600114 self.dtb.Flush()
Simon Glass2ab6e132019-05-17 22:00:39 -0600115 with open(fname, 'rb') as fd:
Simon Glass2ba98752018-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 Glass96066242018-07-06 10:27:27 -0600124 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass2ba98752018-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 Glassd866e622021-11-23 11:03:39 -0700131 'int64val', 'intarray', 'intval', 'longbytearray',
Simon Glasseec44c72021-07-28 19:23:11 -0600132 'maybe-empty-int', 'notstring', 'stringarray',
133 'stringval', 'u-boot,dm-pre-reloc'],
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600139 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass2ba98752018-07-06 10:27:24 -0600140 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
141
Simon Glass94a7c602018-07-17 13:25:46 -0600142 def testGetFdt(self):
143 node = self.dtb.GetNode('/spl-test')
144 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass2ba98752018-07-06 10:27:24 -0600145
Simon Glassb5f0daf2019-05-17 22:00:41 -0600146 def testBytesToValue(self):
147 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700148 (Type.STRING, ['this', 'is']))
Simon Glassb5f0daf2019-05-17 22:00:41 -0600149
Simon Glass2ba98752018-07-06 10:27:24 -0600150class TestNode(unittest.TestCase):
151 """Test operation of the Node class"""
152
153 @classmethod
154 def setUpClass(cls):
155 tools.PrepareOutputDir(None)
156
157 @classmethod
158 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -0600159 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600160
161 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700162 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600163 self.node = self.dtb.GetNode('/spl-test')
Simon Glass76677dd2021-03-21 18:24:37 +1300164 self.fdt = self.dtb.GetFdtObj()
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600180 with self.assertRaises(libfdt.FdtException):
181 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600182
Simon Glassf9b88b32018-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 Glass2ba98752018-07-06 10:27:24 -0600189 def testFindNode(self):
Simon Glass1d858882018-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 Glass2ba98752018-07-06 10:27:24 -0600192 self.assertEqual('i2c@0', node.name)
Simon Glass1d858882018-07-17 13:25:41 -0600193 subnode = node.FindNode('pmic@9')
Simon Glass2ba98752018-07-06 10:27:24 -0600194 self.assertEqual('pmic@9', subnode.name)
Simon Glass1d858882018-07-17 13:25:41 -0600195 self.assertEqual(None, node.FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600196
Simon Glassf9b88b32018-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 Glass76677dd2021-03-21 18:24:37 +1300208 self.fdt.del_node(self.node.Offset())
Simon Glassf9b88b32018-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 Glassacd98612021-03-21 18:24:34 +1300220 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glassf9b88b32018-07-06 10:27:29 -0600221 str(e.exception))
222
Simon Glass94a7c602018-07-17 13:25:46 -0600223 def testLookupPhandle(self):
224 """Test looking up a single phandle"""
Simon Glassdff51a52021-02-03 06:00:56 -0700225 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-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 Glass76677dd2021-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 Glassf6176652021-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 Glass76677dd2021-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 Glass5d1bec32021-03-21 18:24:39 +1300275 def testRefreshNameMismatch(self):
276 """Test name mismatch when syncing nodes and properties"""
277 prop = self.node.AddInt('integer-a', 12)
278
279 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
280 self.node._offset = wrong_offset
281 with self.assertRaises(ValueError) as e:
282 self.dtb.Sync()
283 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
284 str(e.exception))
285
286 with self.assertRaises(ValueError) as e:
287 self.node.Refresh(wrong_offset)
288 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
289 str(e.exception))
290
Simon Glass2ba98752018-07-06 10:27:24 -0600291
292class TestProp(unittest.TestCase):
293 """Test operation of the Prop class"""
294
295 @classmethod
296 def setUpClass(cls):
297 tools.PrepareOutputDir(None)
298
299 @classmethod
300 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -0600301 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600302
303 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700304 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600305 self.node = self.dtb.GetNode('/spl-test')
306 self.fdt = self.dtb.GetFdtObj()
307
Simon Glassb9066ff2018-07-06 10:27:30 -0600308 def testMissingNode(self):
309 self.assertEqual(None, self.dtb.GetNode('missing'))
310
Simon Glass2a2d91d2018-07-06 10:27:28 -0600311 def testPhandle(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700312 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass760b7172018-07-06 10:27:31 -0600313 node = dtb.GetNode('/phandle-source2')
314 prop = node.props['clocks']
315 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600316
317 def _ConvertProp(self, prop_name):
318 """Helper function to look up a property in self.node and return it
319
320 Args:
321 Property name to find
322
323 Return fdt.Prop object for this property
324 """
Simon Glass50c59522018-07-26 14:02:13 -0600325 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600326 return fdt.Prop(self.node, -1, prop_name, p)
327
328 def testMakeProp(self):
329 """Test we can convert all the the types that are supported"""
330 prop = self._ConvertProp('boolval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700331 self.assertEqual(Type.BOOL, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600332 self.assertEqual(True, prop.value)
333
334 prop = self._ConvertProp('intval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700335 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600336 self.assertEqual(1, fdt32_to_cpu(prop.value))
337
Simon Glassd866e622021-11-23 11:03:39 -0700338 prop = self._ConvertProp('int64val')
339 self.assertEqual(Type.INT, prop.type)
340 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
341
Simon Glass2a2d91d2018-07-06 10:27:28 -0600342 prop = self._ConvertProp('intarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700343 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600344 val = [fdt32_to_cpu(val) for val in prop.value]
345 self.assertEqual([2, 3, 4], val)
346
347 prop = self._ConvertProp('byteval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700348 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600349 self.assertEqual(5, ord(prop.value))
350
351 prop = self._ConvertProp('longbytearray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700352 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600353 val = [ord(val) for val in prop.value]
354 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
355
356 prop = self._ConvertProp('stringval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700357 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600358 self.assertEqual('message', prop.value)
359
360 prop = self._ConvertProp('stringarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700361 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600362 self.assertEqual(['multi-word', 'message'], prop.value)
363
364 prop = self._ConvertProp('notstring')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700365 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600366 val = [ord(val) for val in prop.value]
367 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
368
Simon Glass2ba98752018-07-06 10:27:24 -0600369 def testGetEmpty(self):
370 """Tests the GetEmpty() function for the various supported types"""
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700371 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
372 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
373 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
374 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass2ba98752018-07-06 10:27:24 -0600375
376 def testGetOffset(self):
377 """Test we can get the offset of a property"""
Simon Glassf9b88b32018-07-06 10:27:29 -0600378 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
379 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600380
381 def testWiden(self):
382 """Test widening of values"""
383 node2 = self.dtb.GetNode('/spl-test2')
Simon Glasse144caf2020-10-03 11:31:27 -0600384 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass2ba98752018-07-06 10:27:24 -0600385 prop = self.node.props['intval']
386
387 # No action
388 prop2 = node2.props['intval']
389 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700390 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600391 self.assertEqual(1, fdt32_to_cpu(prop.value))
392
Simon Glassca044942021-07-28 19:23:10 -0600393 # Convert single value to array
Simon Glass2ba98752018-07-06 10:27:24 -0600394 prop2 = self.node.props['intarray']
395 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700396 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600397 self.assertTrue(isinstance(prop.value, list))
398
399 # A 4-byte array looks like a single integer. When widened by a longer
400 # byte array, it should turn into an array.
401 prop = self.node.props['longbytearray']
402 prop2 = node2.props['longbytearray']
Simon Glasse144caf2020-10-03 11:31:27 -0600403 prop3 = node3.props['longbytearray']
Simon Glass2ba98752018-07-06 10:27:24 -0600404 self.assertFalse(isinstance(prop2.value, list))
405 self.assertEqual(4, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600406 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600407 prop2.Widen(prop)
408 self.assertTrue(isinstance(prop2.value, list))
409 self.assertEqual(9, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600410 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
411 '\0', '\0', '\0', '\0'], prop2.value)
412 prop3.Widen(prop)
413 self.assertTrue(isinstance(prop3.value, list))
414 self.assertEqual(9, len(prop3.value))
415 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
416 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600417
418 # Similarly for a string array
419 prop = self.node.props['stringval']
420 prop2 = node2.props['stringarray']
421 self.assertFalse(isinstance(prop.value, list))
422 self.assertEqual(7, len(prop.value))
423 prop.Widen(prop2)
424 self.assertTrue(isinstance(prop.value, list))
425 self.assertEqual(3, len(prop.value))
426
427 # Enlarging an existing array
428 prop = self.node.props['stringarray']
429 prop2 = node2.props['stringarray']
430 self.assertTrue(isinstance(prop.value, list))
431 self.assertEqual(2, len(prop.value))
432 prop.Widen(prop2)
433 self.assertTrue(isinstance(prop.value, list))
434 self.assertEqual(3, len(prop.value))
435
Simon Glassca044942021-07-28 19:23:10 -0600436 # Widen an array of ints with an int (should do nothing)
437 prop = self.node.props['intarray']
Simon Glasse679f392021-08-02 07:37:54 -0600438 prop2 = node2.props['intval']
Simon Glassca044942021-07-28 19:23:10 -0600439 self.assertEqual(Type.INT, prop.type)
440 self.assertEqual(3, len(prop.value))
441 prop.Widen(prop2)
442 self.assertEqual(Type.INT, prop.type)
443 self.assertEqual(3, len(prop.value))
444
Simon Glasseec44c72021-07-28 19:23:11 -0600445 # Widen an empty bool to an int
446 prop = self.node.props['maybe-empty-int']
447 prop3 = node3.props['maybe-empty-int']
448 self.assertEqual(Type.BOOL, prop.type)
449 self.assertEqual(True, prop.value)
450 self.assertEqual(Type.INT, prop3.type)
451 self.assertFalse(isinstance(prop.value, list))
452 self.assertEqual(4, len(prop3.value))
453 prop.Widen(prop3)
454 self.assertEqual(Type.INT, prop.type)
455 self.assertTrue(isinstance(prop.value, list))
456 self.assertEqual(1, len(prop.value))
457
Simon Glass116adec2018-07-06 10:27:38 -0600458 def testAdd(self):
459 """Test adding properties"""
460 self.fdt.pack()
461 # This function should automatically expand the device tree
462 self.node.AddZeroProp('one')
463 self.node.AddZeroProp('two')
464 self.node.AddZeroProp('three')
Simon Glassfa80c252018-09-14 04:57:13 -0600465 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600466
467 # Updating existing properties should be OK, since the device-tree size
468 # does not change
469 self.fdt.pack()
470 self.node.SetInt('one', 1)
471 self.node.SetInt('two', 2)
472 self.node.SetInt('three', 3)
Simon Glassfa80c252018-09-14 04:57:13 -0600473 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600474
475 # This should fail since it would need to increase the device-tree size
Simon Glassfa80c252018-09-14 04:57:13 -0600476 self.node.AddZeroProp('four')
Simon Glass116adec2018-07-06 10:27:38 -0600477 with self.assertRaises(libfdt.FdtException) as e:
Simon Glassfa80c252018-09-14 04:57:13 -0600478 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600479 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glass64349612018-09-14 04:57:16 -0600480 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600481
Simon Glass64349612018-09-14 04:57:16 -0600482 def testAddMore(self):
483 """Test various other methods for adding and setting properties"""
484 self.node.AddZeroProp('one')
485 self.dtb.Sync(auto_resize=True)
486 data = self.fdt.getprop(self.node.Offset(), 'one')
487 self.assertEqual(0, fdt32_to_cpu(data))
488
489 self.node.SetInt('one', 1)
490 self.dtb.Sync(auto_resize=False)
491 data = self.fdt.getprop(self.node.Offset(), 'one')
492 self.assertEqual(1, fdt32_to_cpu(data))
493
Simon Glass6eb99322021-01-06 21:35:18 -0700494 val = 1234
495 self.node.AddInt('integer', val)
496 self.dtb.Sync(auto_resize=True)
497 data = self.fdt.getprop(self.node.Offset(), 'integer')
498 self.assertEqual(val, fdt32_to_cpu(data))
499
Simon Glass64349612018-09-14 04:57:16 -0600500 val = '123' + chr(0) + '456'
501 self.node.AddString('string', val)
502 self.dtb.Sync(auto_resize=True)
503 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600504 self.assertEqual(tools.ToBytes(val) + b'\0', data)
Simon Glass64349612018-09-14 04:57:16 -0600505
506 self.fdt.pack()
507 self.node.SetString('string', val + 'x')
508 with self.assertRaises(libfdt.FdtException) as e:
509 self.dtb.Sync(auto_resize=False)
510 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
511 self.node.SetString('string', val[:-1])
512
513 prop = self.node.props['string']
Simon Glassf6b64812019-05-17 22:00:36 -0600514 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600515 self.dtb.Sync(auto_resize=False)
516 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600517 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600518
519 self.node.AddEmptyProp('empty', 5)
520 self.dtb.Sync(auto_resize=True)
521 prop = self.node.props['empty']
Simon Glassf6b64812019-05-17 22:00:36 -0600522 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600523 self.dtb.Sync(auto_resize=False)
524 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glassf6b64812019-05-17 22:00:36 -0600525 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600526
Simon Glassf6b64812019-05-17 22:00:36 -0600527 self.node.SetData('empty', b'123')
528 self.assertEqual(b'123', prop.bytes)
Simon Glass64349612018-09-14 04:57:16 -0600529
Simon Glassc0639172020-07-09 18:39:44 -0600530 # Trying adding a lot of data at once
531 self.node.AddData('data', tools.GetBytes(65, 20000))
532 self.dtb.Sync(auto_resize=True)
533
Simon Glass746aee32018-09-14 04:57:17 -0600534 def testFromData(self):
535 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
536 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
537
538 self.node.AddEmptyProp('empty', 5)
539 self.dtb.Sync(auto_resize=True)
540 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
541
Simon Glassd9dad102019-07-20 12:23:37 -0600542 def testMissingSetInt(self):
543 """Test handling of a missing property with SetInt"""
544 with self.assertRaises(ValueError) as e:
545 self.node.SetInt('one', 1)
546 self.assertIn("node '/spl-test': Missing property 'one'",
547 str(e.exception))
548
549 def testMissingSetData(self):
550 """Test handling of a missing property with SetData"""
551 with self.assertRaises(ValueError) as e:
552 self.node.SetData('one', b'data')
553 self.assertIn("node '/spl-test': Missing property 'one'",
554 str(e.exception))
555
556 def testMissingSetString(self):
557 """Test handling of a missing property with SetString"""
558 with self.assertRaises(ValueError) as e:
559 self.node.SetString('one', 1)
560 self.assertIn("node '/spl-test': Missing property 'one'",
561 str(e.exception))
562
Simon Glassf6e02492019-07-20 12:24:08 -0600563 def testGetFilename(self):
564 """Test the dtb filename can be provided"""
565 self.assertEqual(tools.GetOutputFilename('source.dtb'),
566 self.dtb.GetFilename())
567
Simon Glass2ba98752018-07-06 10:27:24 -0600568
Simon Glass2a2d91d2018-07-06 10:27:28 -0600569class TestFdtUtil(unittest.TestCase):
570 """Tests for the fdt_util module
571
572 This module will likely be mostly replaced at some point, once upstream
573 libfdt has better Python support. For now, this provides tests for current
574 functionality.
575 """
576 @classmethod
577 def setUpClass(cls):
578 tools.PrepareOutputDir(None)
579
Simon Glasse0e62752018-10-01 21:12:41 -0600580 @classmethod
581 def tearDownClass(cls):
582 tools.FinaliseOutputDir()
583
Simon Glass2a2d91d2018-07-06 10:27:28 -0600584 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700585 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600586 self.node = self.dtb.GetNode('/spl-test')
587
588 def testGetInt(self):
589 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
590 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
591
592 with self.assertRaises(ValueError) as e:
Simon Glassd866e622021-11-23 11:03:39 -0700593 fdt_util.GetInt(self.node, 'intarray')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600594 self.assertIn("property 'intarray' has list value: expecting a single "
595 'integer', str(e.exception))
596
Simon Glassd866e622021-11-23 11:03:39 -0700597 def testGetInt64(self):
598 self.assertEqual(0x123456789abcdef0,
599 fdt_util.GetInt64(self.node, 'int64val'))
600 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
601
602 with self.assertRaises(ValueError) as e:
603 fdt_util.GetInt64(self.node, 'intarray')
604 self.assertIn(
605 "property 'intarray' should be a list with 2 items for 64-bit values",
606 str(e.exception))
607
Simon Glass2a2d91d2018-07-06 10:27:28 -0600608 def testGetString(self):
609 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
610 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
611 'test'))
612
613 with self.assertRaises(ValueError) as e:
614 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
615 self.assertIn("property 'stringarray' has list value: expecting a "
616 'single string', str(e.exception))
617
Simon Glass1b5a5332021-11-23 21:09:51 -0700618 def testGetStringList(self):
619 self.assertEqual(['message'],
620 fdt_util.GetStringList(self.node, 'stringval'))
621 self.assertEqual(
622 ['multi-word', 'message'],
623 fdt_util.GetStringList(self.node, 'stringarray'))
624 self.assertEqual(['test'],
625 fdt_util.GetStringList(self.node, 'missing', ['test']))
626
Simon Glass2a2d91d2018-07-06 10:27:28 -0600627 def testGetBool(self):
628 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
629 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
630 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
631 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
632
Simon Glass3af8e492018-07-17 13:25:40 -0600633 def testGetByte(self):
634 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
635 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
636
637 with self.assertRaises(ValueError) as e:
638 fdt_util.GetByte(self.node, 'longbytearray')
639 self.assertIn("property 'longbytearray' has list value: expecting a "
640 'single byte', str(e.exception))
641
642 with self.assertRaises(ValueError) as e:
643 fdt_util.GetByte(self.node, 'intval')
644 self.assertIn("property 'intval' has length 4, expecting 1",
645 str(e.exception))
646
Simon Glass40b4d642021-11-23 11:03:40 -0700647 def testGetBytes(self):
648 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
649 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
650 self.assertEqual(
651 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
652
653 with self.assertRaises(ValueError) as e:
654 fdt_util.GetBytes(self.node, 'longbytearray', 7)
655 self.assertIn(
656 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
657 str(e.exception))
658
659 self.assertEqual(
660 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
661 self.assertEqual(
662 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
663
Simon Glass94a7c602018-07-17 13:25:46 -0600664 def testGetPhandleList(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700665 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600666 node = dtb.GetNode('/phandle-source2')
667 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
668 node = dtb.GetNode('/phandle-source')
669 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
670 fdt_util.GetPhandleList(node, 'clocks'))
671 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
672
Simon Glass53af22a2018-07-17 13:25:32 -0600673 def testGetDataType(self):
674 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
675 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
676 str))
677 with self.assertRaises(ValueError) as e:
678 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
679 bool))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600680 def testFdtCellsToCpu(self):
681 val = self.node.props['intarray'].value
682 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
683 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
684
Simon Glassdff51a52021-02-03 06:00:56 -0700685 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600686 node1 = dtb2.GetNode('/test1')
687 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600688 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
689
Simon Glasse66d3182019-05-17 22:00:40 -0600690 node2 = dtb2.GetNode('/test2')
691 val = node2.props['reg'].value
692 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
693 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
694 2))
695 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
696
Simon Glass2a2d91d2018-07-06 10:27:28 -0600697 def testEnsureCompiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600698 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700699 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600700 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
701
Simon Glassa004f292019-07-20 12:23:49 -0600702 def testEnsureCompiledTmpdir(self):
703 """Test providing a temporary directory"""
704 try:
705 old_outdir = tools.outdir
706 tools.outdir= None
707 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glassdff51a52021-02-03 06:00:56 -0700708 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-07-20 12:23:49 -0600709 tmpdir)
710 self.assertEqual(tmpdir, os.path.dirname(dtb))
711 shutil.rmtree(tmpdir)
712 finally:
713 tools.outdir= old_outdir
714
Simon Glass2a2d91d2018-07-06 10:27:28 -0600715
716def RunTestCoverage():
717 """Run the tests and check that we get 100% coverage"""
718 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
719 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
720
721
Simon Glass2ba98752018-07-06 10:27:24 -0600722def RunTests(args):
723 """Run all the test we have for the fdt model
724
725 Args:
726 args: List of positional args provided to fdt. This can hold a test
727 name to execute (as in 'fdt -t testFdt', for example)
728 """
729 result = unittest.TestResult()
730 sys.argv = [sys.argv[0]]
731 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600732 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600733 if test_name:
734 try:
735 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
736 except AttributeError:
737 continue
738 else:
739 suite = unittest.TestLoader().loadTestsFromTestCase(module)
740 suite.run(result)
741
Simon Glass90a81322019-05-17 22:00:31 -0600742 print(result)
Simon Glass2ba98752018-07-06 10:27:24 -0600743 for _, err in result.errors:
Simon Glass90a81322019-05-17 22:00:31 -0600744 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600745 for _, err in result.failures:
Simon Glass90a81322019-05-17 22:00:31 -0600746 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600747
748if __name__ != '__main__':
749 sys.exit(1)
750
751parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600752parser.add_option('-B', '--build-dir', type='string', default='b',
753 help='Directory containing the build output')
Simon Glass11ae93e2018-10-01 21:12:47 -0600754parser.add_option('-P', '--processes', type=int,
755 help='set number of processes to use for running tests')
Simon Glass2ba98752018-07-06 10:27:24 -0600756parser.add_option('-t', '--test', action='store_true', dest='test',
757 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600758parser.add_option('-T', '--test-coverage', action='store_true',
759 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600760(options, args) = parser.parse_args()
761
762# Run our meagre tests
763if options.test:
764 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600765elif options.test_coverage:
766 RunTestCoverage()