blob: 3859af8d032b011535ae8f30b69f2aa3f36d0998 [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 Glass8a455fc2022-02-11 13:23:20 -070028from dtoc.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):
Simon Glassc1aa66e2022-01-29 14:14:04 -070077 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -060078
79 @classmethod
80 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -070081 tools.finalise_output_dir()
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
Simon Glass8a455fc2022-02-11 13:23:20 -0700122 def testGetFdtRaw(self):
Simon Glass2ba98752018-07-06 10:27:24 -0600123 """Tetst that we can access the raw device-tree data"""
Simon Glass8a455fc2022-02-11 13:23:20 -0700124 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
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):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700155 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -0600156
157 @classmethod
158 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700159 tools.finalise_output_dir()
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 Glassdd857ee2022-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 Glass5d1bec32021-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 Glass2ba98752018-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 Glassc1aa66e2022-01-29 14:14:04 -0700308 tools.prepare_output_dir(None)
Simon Glass2ba98752018-07-06 10:27:24 -0600309
310 @classmethod
311 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700312 tools.finalise_output_dir()
Simon Glass2ba98752018-07-06 10:27:24 -0600313
314 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700315 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600316 self.node = self.dtb.GetNode('/spl-test')
317 self.fdt = self.dtb.GetFdtObj()
318
Simon Glassb9066ff2018-07-06 10:27:30 -0600319 def testMissingNode(self):
320 self.assertEqual(None, self.dtb.GetNode('missing'))
321
Simon Glass2a2d91d2018-07-06 10:27:28 -0600322 def testPhandle(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700323 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass760b7172018-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 Glass2a2d91d2018-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 Glass50c59522018-07-26 14:02:13 -0600336 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700342 self.assertEqual(Type.BOOL, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600343 self.assertEqual(True, prop.value)
344
345 prop = self._ConvertProp('intval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700346 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600347 self.assertEqual(1, fdt32_to_cpu(prop.value))
348
Simon Glassd866e622021-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 Glass2a2d91d2018-07-06 10:27:28 -0600353 prop = self._ConvertProp('intarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700354 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700359 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600360 self.assertEqual(5, ord(prop.value))
361
362 prop = self._ConvertProp('longbytearray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700363 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700368 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600369 self.assertEqual('message', prop.value)
370
371 prop = self._ConvertProp('stringarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700372 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600373 self.assertEqual(['multi-word', 'message'], prop.value)
374
375 prop = self._ConvertProp('notstring')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700376 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-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 Glass2ba98752018-07-06 10:27:24 -0600380 def testGetEmpty(self):
381 """Tests the GetEmpty() function for the various supported types"""
Simon Glass5ea9dcc2020-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 Glassc1aa66e2022-01-29 14:14:04 -0700384 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700385 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass2ba98752018-07-06 10:27:24 -0600386
387 def testGetOffset(self):
388 """Test we can get the offset of a property"""
Simon Glassf9b88b32018-07-06 10:27:29 -0600389 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
390 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600391
392 def testWiden(self):
393 """Test widening of values"""
394 node2 = self.dtb.GetNode('/spl-test2')
Simon Glasse144caf2020-10-03 11:31:27 -0600395 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass2ba98752018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700401 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600402 self.assertEqual(1, fdt32_to_cpu(prop.value))
403
Simon Glassca044942021-07-28 19:23:10 -0600404 # Convert single value to array
Simon Glass2ba98752018-07-06 10:27:24 -0600405 prop2 = self.node.props['intarray']
406 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700407 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-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 Glasse144caf2020-10-03 11:31:27 -0600414 prop3 = node3.props['longbytearray']
Simon Glass2ba98752018-07-06 10:27:24 -0600415 self.assertFalse(isinstance(prop2.value, list))
416 self.assertEqual(4, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600417 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600418 prop2.Widen(prop)
419 self.assertTrue(isinstance(prop2.value, list))
420 self.assertEqual(9, len(prop2.value))
Simon Glasse144caf2020-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 Glass2ba98752018-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 Glassca044942021-07-28 19:23:10 -0600447 # Widen an array of ints with an int (should do nothing)
448 prop = self.node.props['intarray']
Simon Glasse679f392021-08-02 07:37:54 -0600449 prop2 = node2.props['intval']
Simon Glassca044942021-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 Glasseec44c72021-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 Glass116adec2018-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 Glassfa80c252018-09-14 04:57:13 -0600476 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-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 Glassfa80c252018-09-14 04:57:13 -0600484 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600485
486 # This should fail since it would need to increase the device-tree size
Simon Glassfa80c252018-09-14 04:57:13 -0600487 self.node.AddZeroProp('four')
Simon Glass116adec2018-07-06 10:27:38 -0600488 with self.assertRaises(libfdt.FdtException) as e:
Simon Glassfa80c252018-09-14 04:57:13 -0600489 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600490 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glass64349612018-09-14 04:57:16 -0600491 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600492
Simon Glass64349612018-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 Glass6eb99322021-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 Glass64349612018-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 Glassc1aa66e2022-01-29 14:14:04 -0700515 self.assertEqual(tools.to_bytes(val) + b'\0', data)
Simon Glass64349612018-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 Glassc1aa66e2022-01-29 14:14:04 -0700525 prop.SetData(tools.to_bytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600526 self.dtb.Sync(auto_resize=False)
527 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700528 self.assertEqual(tools.to_bytes(val), data)
Simon Glass64349612018-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 Glassc1aa66e2022-01-29 14:14:04 -0700533 prop.SetData(tools.to_bytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600534 self.dtb.Sync(auto_resize=False)
535 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700536 self.assertEqual(tools.to_bytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600537
Simon Glassf6b64812019-05-17 22:00:36 -0600538 self.node.SetData('empty', b'123')
539 self.assertEqual(b'123', prop.bytes)
Simon Glass64349612018-09-14 04:57:16 -0600540
Simon Glassc0639172020-07-09 18:39:44 -0600541 # Trying adding a lot of data at once
Simon Glassc1aa66e2022-01-29 14:14:04 -0700542 self.node.AddData('data', tools.get_bytes(65, 20000))
Simon Glassc0639172020-07-09 18:39:44 -0600543 self.dtb.Sync(auto_resize=True)
544
Simon Glassbc116022022-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 Glass0ded4d42022-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 Glassa30c39f2022-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 Glass746aee32018-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 Glassd9dad102019-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 Glassf6e02492019-07-20 12:24:08 -0600597 def testGetFilename(self):
598 """Test the dtb filename can be provided"""
Simon Glassc1aa66e2022-01-29 14:14:04 -0700599 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glassf6e02492019-07-20 12:24:08 -0600600 self.dtb.GetFilename())
601
Simon Glass2ba98752018-07-06 10:27:24 -0600602
Simon Glass2a2d91d2018-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 Glassc1aa66e2022-01-29 14:14:04 -0700612 tools.prepare_output_dir(None)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600613
Simon Glasse0e62752018-10-01 21:12:41 -0600614 @classmethod
615 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700616 tools.finalise_output_dir()
Simon Glasse0e62752018-10-01 21:12:41 -0600617
Simon Glass2a2d91d2018-07-06 10:27:28 -0600618 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700619 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-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 Glassd866e622021-11-23 11:03:39 -0700627 fdt_util.GetInt(self.node, 'intarray')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600628 self.assertIn("property 'intarray' has list value: expecting a single "
629 'integer', str(e.exception))
630
Simon Glassd866e622021-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 Glass2a2d91d2018-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 Glass0ded4d42022-03-05 20:18:56 -0700646 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
Simon Glass2a2d91d2018-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 Glass1b5a5332021-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 Glass0ded4d42022-03-05 20:18:56 -0700661 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
Simon Glass1b5a5332021-11-23 21:09:51 -0700662
Simon Glass7e4b66a2022-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 Glass61012532022-03-05 20:18:52 -0700670 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
Simon Glass7e4b66a2022-02-08 11:49:53 -0700671 fdt_util.GetArgs(node, 'args'))
Simon Glass61012532022-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 Glass7e4b66a2022-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 Glass2a2d91d2018-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 Glass3af8e492018-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 Glass40b4d642021-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 Glass94a7c602018-07-17 13:25:46 -0600719 def testGetPhandleList(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700720 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-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 Glass53af22a2018-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 Glass2a2d91d2018-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 Glassdff51a52021-02-03 06:00:56 -0700740 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600741 node1 = dtb2.GetNode('/test1')
742 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600743 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
744
Simon Glasse66d3182019-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 Glass2a2d91d2018-07-06 10:27:28 -0600752 def testEnsureCompiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600753 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700754 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600755 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
756
Simon Glassa004f292019-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 Glassdff51a52021-02-03 06:00:56 -0700763 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-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 Glass2a2d91d2018-07-06 10:27:28 -0600770
771def RunTestCoverage():
772 """Run the tests and check that we get 100% coverage"""
Simon Glass5e2ab402022-01-29 14:14:14 -0700773 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass2a2d91d2018-07-06 10:27:28 -0600774 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
775
776
Simon Glass2ba98752018-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)
Simon Glass42ae3632022-03-18 18:01:50 -0600783
784 Returns:
785 Return code, 0 on success
Simon Glass2ba98752018-07-06 10:27:24 -0600786 """
787 result = unittest.TestResult()
Simon Glass2ba98752018-07-06 10:27:24 -0600788 test_name = args and args[0] or None
Simon Glass42ae3632022-03-18 18:01:50 -0600789 test_util.run_test_suites(
790 result, False, False, False, None, test_name, None,
791 [TestFdt, TestNode, TestProp, TestFdtUtil])
Simon Glass2ba98752018-07-06 10:27:24 -0600792
Simon Glass42ae3632022-03-18 18:01:50 -0600793 return test_util.report_result('fdt', test_name, result)
Simon Glass2ba98752018-07-06 10:27:24 -0600794
795if __name__ != '__main__':
796 sys.exit(1)
797
798parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600799parser.add_option('-B', '--build-dir', type='string', default='b',
800 help='Directory containing the build output')
Simon Glass11ae93e2018-10-01 21:12:47 -0600801parser.add_option('-P', '--processes', type=int,
802 help='set number of processes to use for running tests')
Simon Glass2ba98752018-07-06 10:27:24 -0600803parser.add_option('-t', '--test', action='store_true', dest='test',
804 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600805parser.add_option('-T', '--test-coverage', action='store_true',
806 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600807(options, args) = parser.parse_args()
808
809# Run our meagre tests
810if options.test:
Simon Glass42ae3632022-03-18 18:01:50 -0600811 ret_code = RunTests(args)
812 sys.exit(ret_code)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600813elif options.test_coverage:
814 RunTestCoverage()