blob: ea707f2f87e726b1ed897c1142b90ab6accdf474 [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 Glassa30c39f2022-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 Glass746aee32018-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 Glassd9dad102019-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 Glassf6e02492019-07-20 12:24:08 -0600591 def testGetFilename(self):
592 """Test the dtb filename can be provided"""
Simon Glassc1aa66e2022-01-29 14:14:04 -0700593 self.assertEqual(tools.get_output_filename('source.dtb'),
Simon Glassf6e02492019-07-20 12:24:08 -0600594 self.dtb.GetFilename())
595
Simon Glass2ba98752018-07-06 10:27:24 -0600596
Simon Glass2a2d91d2018-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 Glassc1aa66e2022-01-29 14:14:04 -0700606 tools.prepare_output_dir(None)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600607
Simon Glasse0e62752018-10-01 21:12:41 -0600608 @classmethod
609 def tearDownClass(cls):
Simon Glassc1aa66e2022-01-29 14:14:04 -0700610 tools.finalise_output_dir()
Simon Glasse0e62752018-10-01 21:12:41 -0600611
Simon Glass2a2d91d2018-07-06 10:27:28 -0600612 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700613 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-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 Glassd866e622021-11-23 11:03:39 -0700621 fdt_util.GetInt(self.node, 'intarray')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600622 self.assertIn("property 'intarray' has list value: expecting a single "
623 'integer', str(e.exception))
624
Simon Glassd866e622021-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 Glass2a2d91d2018-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 Glass1b5a5332021-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 Glass7e4b66a2022-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'))
Simon Glass61012532022-03-05 20:18:52 -0700662 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
Simon Glass7e4b66a2022-02-08 11:49:53 -0700663 fdt_util.GetArgs(node, 'args'))
Simon Glass61012532022-03-05 20:18:52 -0700664 self.assertEqual(['a space', 'there'],
665 fdt_util.GetArgs(node, 'args2'))
666 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
667 fdt_util.GetArgs(node, 'args3'))
Simon Glass7e4b66a2022-02-08 11:49:53 -0700668 with self.assertRaises(ValueError) as exc:
669 fdt_util.GetArgs(self.node, 'missing')
670 self.assertIn(
671 "Node '/spl-test': Expected property 'missing'",
672 str(exc.exception))
673
Simon Glass2a2d91d2018-07-06 10:27:28 -0600674 def testGetBool(self):
675 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
676 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
677 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
678 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
679
Simon Glass3af8e492018-07-17 13:25:40 -0600680 def testGetByte(self):
681 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
682 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
683
684 with self.assertRaises(ValueError) as e:
685 fdt_util.GetByte(self.node, 'longbytearray')
686 self.assertIn("property 'longbytearray' has list value: expecting a "
687 'single byte', str(e.exception))
688
689 with self.assertRaises(ValueError) as e:
690 fdt_util.GetByte(self.node, 'intval')
691 self.assertIn("property 'intval' has length 4, expecting 1",
692 str(e.exception))
693
Simon Glass40b4d642021-11-23 11:03:40 -0700694 def testGetBytes(self):
695 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
696 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
697 self.assertEqual(
698 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
699
700 with self.assertRaises(ValueError) as e:
701 fdt_util.GetBytes(self.node, 'longbytearray', 7)
702 self.assertIn(
703 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
704 str(e.exception))
705
706 self.assertEqual(
707 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
708 self.assertEqual(
709 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
710
Simon Glass94a7c602018-07-17 13:25:46 -0600711 def testGetPhandleList(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700712 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600713 node = dtb.GetNode('/phandle-source2')
714 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
715 node = dtb.GetNode('/phandle-source')
716 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
717 fdt_util.GetPhandleList(node, 'clocks'))
718 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
719
Simon Glass53af22a2018-07-17 13:25:32 -0600720 def testGetDataType(self):
721 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
722 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
723 str))
724 with self.assertRaises(ValueError) as e:
725 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
726 bool))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600727 def testFdtCellsToCpu(self):
728 val = self.node.props['intarray'].value
729 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
730 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
731
Simon Glassdff51a52021-02-03 06:00:56 -0700732 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600733 node1 = dtb2.GetNode('/test1')
734 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600735 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
736
Simon Glasse66d3182019-05-17 22:00:40 -0600737 node2 = dtb2.GetNode('/test2')
738 val = node2.props['reg'].value
739 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
740 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
741 2))
742 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
743
Simon Glass2a2d91d2018-07-06 10:27:28 -0600744 def testEnsureCompiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600745 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700746 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600747 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
748
Simon Glassa004f292019-07-20 12:23:49 -0600749 def testEnsureCompiledTmpdir(self):
750 """Test providing a temporary directory"""
751 try:
752 old_outdir = tools.outdir
753 tools.outdir= None
754 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glassdff51a52021-02-03 06:00:56 -0700755 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-07-20 12:23:49 -0600756 tmpdir)
757 self.assertEqual(tmpdir, os.path.dirname(dtb))
758 shutil.rmtree(tmpdir)
759 finally:
760 tools.outdir= old_outdir
761
Simon Glass2a2d91d2018-07-06 10:27:28 -0600762
763def RunTestCoverage():
764 """Run the tests and check that we get 100% coverage"""
Simon Glass5e2ab402022-01-29 14:14:14 -0700765 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
Simon Glass2a2d91d2018-07-06 10:27:28 -0600766 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
767
768
Simon Glass2ba98752018-07-06 10:27:24 -0600769def RunTests(args):
770 """Run all the test we have for the fdt model
771
772 Args:
773 args: List of positional args provided to fdt. This can hold a test
774 name to execute (as in 'fdt -t testFdt', for example)
775 """
776 result = unittest.TestResult()
777 sys.argv = [sys.argv[0]]
778 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600779 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600780 if test_name:
781 try:
782 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
783 except AttributeError:
784 continue
785 else:
786 suite = unittest.TestLoader().loadTestsFromTestCase(module)
787 suite.run(result)
788
Simon Glass90a81322019-05-17 22:00:31 -0600789 print(result)
Simon Glass2ba98752018-07-06 10:27:24 -0600790 for _, err in result.errors:
Simon Glass90a81322019-05-17 22:00:31 -0600791 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600792 for _, err in result.failures:
Simon Glass90a81322019-05-17 22:00:31 -0600793 print(err)
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:
811 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600812elif options.test_coverage:
813 RunTestCoverage()