test_fdt.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. #!/usr/bin/env python3
  2. # SPDX-License-Identifier: GPL-2.0+
  3. """
  4. Tests for the Fdt module
  5. Copyright (c) 2018 Google, Inc
  6. Written by Simon Glass <sjg@chromium.org>
  7. """
  8. from argparse import ArgumentParser
  9. import os
  10. import shutil
  11. import sys
  12. import tempfile
  13. import unittest
  14. # Bring in the patman libraries
  15. our_path = os.path.dirname(os.path.realpath(__file__))
  16. sys.path.insert(1, os.path.join(our_path, '..'))
  17. # Bring in the libfdt module
  18. sys.path.insert(2, 'scripts/dtc/pylibfdt')
  19. sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
  20. sys.path.insert(2, os.path.join(our_path,
  21. '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
  22. #pylint: disable=wrong-import-position
  23. from dtoc import fdt
  24. from dtoc import fdt_util
  25. from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
  26. from dtoc.fdt import Type, BytesToValue
  27. import libfdt
  28. from u_boot_pylib import test_util
  29. from u_boot_pylib import tools
  30. from u_boot_pylib import tout
  31. #pylint: disable=protected-access
  32. def _get_property_value(dtb, node, prop_name):
  33. """Low-level function to get the property value based on its offset
  34. This looks directly in the device tree at the property's offset to find
  35. its value. It is useful as a check that the property is in the correct
  36. place.
  37. Args:
  38. node: Node to look in
  39. prop_name: Property name to find
  40. Returns:
  41. Tuple:
  42. Prop object found
  43. Value of property as a string (found using property offset)
  44. """
  45. prop = node.props[prop_name]
  46. # Add 12, which is sizeof(struct fdt_property), to get to start of data
  47. offset = prop.GetOffset() + 12
  48. data = dtb.GetContents()[offset:offset + len(prop.value)]
  49. return prop, [chr(x) for x in data]
  50. def find_dtb_file(dts_fname):
  51. """Locate a test file in the test/ directory
  52. Args:
  53. dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
  54. Returns:
  55. str: Path to the test filename
  56. """
  57. return os.path.join('tools/dtoc/test', dts_fname)
  58. class TestFdt(unittest.TestCase):
  59. """Tests for the Fdt module
  60. This includes unit tests for some functions and functional tests for the fdt
  61. module.
  62. """
  63. @classmethod
  64. def setUpClass(cls):
  65. tools.prepare_output_dir(None)
  66. @classmethod
  67. def tearDownClass(cls):
  68. tools.finalise_output_dir()
  69. def setUp(self):
  70. self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
  71. def test_fdt(self):
  72. """Test that we can open an Fdt"""
  73. self.dtb.Scan()
  74. root = self.dtb.GetRoot()
  75. self.assertTrue(isinstance(root, fdt.Node))
  76. def test_get_node(self):
  77. """Test the GetNode() method"""
  78. node = self.dtb.GetNode('/spl-test')
  79. self.assertTrue(isinstance(node, fdt.Node))
  80. node = self.dtb.GetNode('/i2c@0/pmic@9')
  81. self.assertTrue(isinstance(node, fdt.Node))
  82. self.assertEqual('pmic@9', node.name)
  83. self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
  84. node = self.dtb.GetNode('/')
  85. self.assertTrue(isinstance(node, fdt.Node))
  86. self.assertEqual(0, node.Offset())
  87. def test_flush(self):
  88. """Check that we can flush the device tree out to its file"""
  89. fname = self.dtb._fname
  90. with open(fname, 'rb') as inf:
  91. inf.read()
  92. os.remove(fname)
  93. with self.assertRaises(IOError):
  94. with open(fname, 'rb'):
  95. pass
  96. self.dtb.Flush()
  97. with open(fname, 'rb') as inf:
  98. inf.read()
  99. def test_pack(self):
  100. """Test that packing a device tree works"""
  101. self.dtb.Pack()
  102. def test_get_fdt_raw(self):
  103. """Tetst that we can access the raw device-tree data"""
  104. self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
  105. def test_get_props(self):
  106. """Tests obtaining a list of properties"""
  107. node = self.dtb.GetNode('/spl-test')
  108. props = self.dtb.GetProps(node)
  109. self.assertEqual(['boolval', 'bootph-all', 'bytearray', 'byteval',
  110. 'compatible', 'int64val', 'intarray', 'intval',
  111. 'longbytearray', 'maybe-empty-int', 'notstring',
  112. 'stringarray', 'stringval', ],
  113. sorted(props.keys()))
  114. def test_check_error(self):
  115. """Tests the ChecKError() function"""
  116. with self.assertRaises(ValueError) as exc:
  117. fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
  118. self.assertIn('FDT_ERR_NOTFOUND: hello', str(exc.exception))
  119. def test_get_fdt(self):
  120. """Test getting an Fdt object from a node"""
  121. node = self.dtb.GetNode('/spl-test')
  122. self.assertEqual(self.dtb, node.GetFdt())
  123. def test_bytes_to_value(self):
  124. """Test converting a string list into Python"""
  125. self.assertEqual(BytesToValue(b'this\0is\0'),
  126. (Type.STRING, ['this', 'is']))
  127. class TestNode(unittest.TestCase):
  128. """Test operation of the Node class"""
  129. @classmethod
  130. def setUpClass(cls):
  131. tools.prepare_output_dir(None)
  132. @classmethod
  133. def tearDownClass(cls):
  134. tools.finalise_output_dir()
  135. def setUp(self):
  136. self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
  137. self.node = self.dtb.GetNode('/spl-test')
  138. self.fdt = self.dtb.GetFdtObj()
  139. def test_offset(self):
  140. """Tests that we can obtain the offset of a node"""
  141. self.assertTrue(self.node.Offset() > 0)
  142. def test_delete(self):
  143. """Tests that we can delete a property"""
  144. node2 = self.dtb.GetNode('/spl-test2')
  145. offset1 = node2.Offset()
  146. self.node.DeleteProp('intval')
  147. offset2 = node2.Offset()
  148. self.assertTrue(offset2 < offset1)
  149. self.node.DeleteProp('intarray')
  150. offset3 = node2.Offset()
  151. self.assertTrue(offset3 < offset2)
  152. with self.assertRaises(libfdt.FdtException):
  153. self.node.DeleteProp('missing')
  154. def test_delete_get_offset(self):
  155. """Test that property offset update when properties are deleted"""
  156. self.node.DeleteProp('intval')
  157. prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
  158. self.assertEqual(prop.value, value)
  159. def test_find_node(self):
  160. """Tests that we can find a node using the FindNode() functoin"""
  161. node = self.dtb.GetRoot().FindNode('i2c@0')
  162. self.assertEqual('i2c@0', node.name)
  163. subnode = node.FindNode('pmic@9')
  164. self.assertEqual('pmic@9', subnode.name)
  165. self.assertEqual(None, node.FindNode('missing'))
  166. def test_refresh_missing_node(self):
  167. """Test refreshing offsets when an extra node is present in dtb"""
  168. # Delete it from our tables, not the device tree
  169. del self.dtb._root.subnodes[-1]
  170. with self.assertRaises(ValueError) as exc:
  171. self.dtb.Refresh()
  172. self.assertIn('Internal error, offset', str(exc.exception))
  173. def test_refresh_extra_node(self):
  174. """Test refreshing offsets when an expected node is missing"""
  175. # Delete it from the device tre, not our tables
  176. self.fdt.del_node(self.node.Offset())
  177. with self.assertRaises(ValueError) as exc:
  178. self.dtb.Refresh()
  179. self.assertIn('Internal error, node name mismatch '
  180. 'spl-test != spl-test2', str(exc.exception))
  181. def test_refresh_missing_prop(self):
  182. """Test refreshing offsets when an extra property is present in dtb"""
  183. # Delete it from our tables, not the device tree
  184. del self.node.props['notstring']
  185. with self.assertRaises(ValueError) as exc:
  186. self.dtb.Refresh()
  187. self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
  188. str(exc.exception))
  189. def test_lookup_phandle(self):
  190. """Test looking up a single phandle"""
  191. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
  192. node = dtb.GetNode('/phandle-source2')
  193. prop = node.props['clocks']
  194. target = dtb.GetNode('/phandle-target')
  195. self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
  196. def test_add_node_space(self):
  197. """Test adding a single node when out of space"""
  198. self.fdt.pack()
  199. self.node.AddSubnode('subnode')
  200. with self.assertRaises(libfdt.FdtException) as exc:
  201. self.dtb.Sync(auto_resize=False)
  202. self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
  203. self.dtb.Sync(auto_resize=True)
  204. offset = self.fdt.path_offset('/spl-test/subnode')
  205. self.assertTrue(offset > 0)
  206. def test_add_nodes(self):
  207. """Test adding various subnode and properies"""
  208. node = self.dtb.GetNode('/i2c@0')
  209. # Add one more node next to the pmic one
  210. sn1 = node.AddSubnode('node-one')
  211. sn1.AddInt('integer-a', 12)
  212. sn1.AddInt('integer-b', 23)
  213. # Sync so that everything is clean
  214. self.dtb.Sync(auto_resize=True)
  215. # Add two subnodes next to pmic and node-one
  216. sn2 = node.AddSubnode('node-two')
  217. sn2.AddInt('integer-2a', 34)
  218. sn2.AddInt('integer-2b', 45)
  219. sn3 = node.AddSubnode('node-three')
  220. sn3.AddInt('integer-3', 123)
  221. # Add a property to the node after i2c@0 to check that this is not
  222. # disturbed by adding a subnode to i2c@0
  223. orig_node = self.dtb.GetNode('/orig-node')
  224. orig_node.AddInt('integer-4', 456)
  225. # Add a property to the pmic node to check that pmic properties are not
  226. # disturbed
  227. pmic = self.dtb.GetNode('/i2c@0/pmic@9')
  228. pmic.AddInt('integer-5', 567)
  229. self.dtb.Sync(auto_resize=True)
  230. def test_add_one_node(self):
  231. """Testing deleting and adding a subnode before syncing"""
  232. subnode = self.node.AddSubnode('subnode')
  233. self.node.AddSubnode('subnode2')
  234. self.dtb.Sync(auto_resize=True)
  235. # Delete a node and add a new one
  236. subnode.Delete()
  237. self.node.AddSubnode('subnode3')
  238. self.dtb.Sync()
  239. def test_refresh_name_mismatch(self):
  240. """Test name mismatch when syncing nodes and properties"""
  241. self.node.AddInt('integer-a', 12)
  242. wrong_offset = self.dtb.GetNode('/i2c@0')._offset
  243. self.node._offset = wrong_offset
  244. with self.assertRaises(ValueError) as exc:
  245. self.dtb.Sync()
  246. self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
  247. str(exc.exception))
  248. with self.assertRaises(ValueError) as exc:
  249. self.node.Refresh(wrong_offset)
  250. self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
  251. str(exc.exception))
  252. def test_copy_node(self):
  253. """Test copy_node() function"""
  254. def do_copy_checks(dtb, dst, second1_ph_val, expect_none):
  255. self.assertEqual(
  256. ['/dest/base', '/dest/first@0', '/dest/existing'],
  257. [n.path for n in dst.subnodes])
  258. chk = dtb.GetNode('/dest/base')
  259. self.assertTrue(chk)
  260. self.assertEqual(
  261. {'compatible', 'bootph-all', '#address-cells', '#size-cells'},
  262. chk.props.keys())
  263. # Check the first property
  264. prop = chk.props['bootph-all']
  265. self.assertEqual('bootph-all', prop.name)
  266. self.assertEqual(True, prop.value)
  267. self.assertEqual(chk.path, prop._node.path)
  268. # Check the second property
  269. prop2 = chk.props['compatible']
  270. self.assertEqual('compatible', prop2.name)
  271. self.assertEqual('sandbox,i2c', prop2.value)
  272. self.assertEqual(chk.path, prop2._node.path)
  273. base = chk.FindNode('base')
  274. self.assertTrue(chk)
  275. first = dtb.GetNode('/dest/base/first@0')
  276. self.assertTrue(first)
  277. over = dtb.GetNode('/dest/base/over')
  278. self.assertTrue(over)
  279. # Make sure that the phandle for 'over' is copied
  280. self.assertIn('phandle', over.props.keys())
  281. second = dtb.GetNode('/dest/base/second')
  282. self.assertTrue(second)
  283. self.assertEqual([over.name, first.name, second.name],
  284. [n.name for n in chk.subnodes])
  285. self.assertEqual(chk, over.parent)
  286. self.assertEqual(
  287. {'bootph-all', 'compatible', 'reg', 'low-power', 'phandle'},
  288. over.props.keys())
  289. if expect_none:
  290. self.assertIsNone(prop._offset)
  291. self.assertIsNone(prop2._offset)
  292. self.assertIsNone(over._offset)
  293. else:
  294. self.assertTrue(prop._offset)
  295. self.assertTrue(prop2._offset)
  296. self.assertTrue(over._offset)
  297. # Now check ordering of the subnodes
  298. self.assertEqual(
  299. ['second1', 'second2', 'second3', 'second4'],
  300. [n.name for n in second.subnodes])
  301. # Check the 'second_1_bad' phandle is not copied over
  302. second1 = second.FindNode('second1')
  303. self.assertTrue(second1)
  304. sph = second1.props.get('phandle')
  305. self.assertTrue(sph)
  306. self.assertEqual(second1_ph_val, sph.bytes)
  307. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
  308. tmpl = dtb.GetNode('/base')
  309. dst = dtb.GetNode('/dest')
  310. second1_ph_val = (dtb.GetNode('/dest/base/second/second1').
  311. props['phandle'].bytes)
  312. dst.copy_node(tmpl)
  313. do_copy_checks(dtb, dst, second1_ph_val, expect_none=True)
  314. dtb.Sync(auto_resize=True)
  315. # Now check the resulting FDT. It should have duplicate phandles since
  316. # 'over' has been copied to 'dest/base/over' but still exists in its old
  317. # place
  318. new_dtb = fdt.Fdt.FromData(dtb.GetContents())
  319. with self.assertRaises(ValueError) as exc:
  320. new_dtb.Scan()
  321. self.assertIn(
  322. 'Duplicate phandle 1 in nodes /dest/base/over and /base/over',
  323. str(exc.exception))
  324. # Remove the source nodes for the copy
  325. new_dtb.GetNode('/base').Delete()
  326. # Now it should scan OK
  327. new_dtb.Scan()
  328. dst = new_dtb.GetNode('/dest')
  329. do_copy_checks(new_dtb, dst, second1_ph_val, expect_none=False)
  330. def test_copy_subnodes_from_phandles(self):
  331. """Test copy_node() function"""
  332. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
  333. orig = dtb.GetNode('/')
  334. node_list = fdt_util.GetPhandleList(orig, 'copy-list')
  335. dst = dtb.GetNode('/dest')
  336. dst.copy_subnodes_from_phandles(node_list)
  337. pmic = dtb.GetNode('/dest/over')
  338. self.assertTrue(pmic)
  339. subn = dtb.GetNode('/dest/first@0')
  340. self.assertTrue(subn)
  341. self.assertEqual({'a-prop', 'b-prop', 'reg'}, subn.props.keys())
  342. self.assertEqual(
  343. ['/dest/earlier', '/dest/later', '/dest/over', '/dest/first@0',
  344. '/dest/second', '/dest/existing', '/dest/base'],
  345. [n.path for n in dst.subnodes])
  346. # Make sure that the phandle for 'over' is not copied
  347. over = dst.FindNode('over')
  348. tout.debug(f'keys: {over.props.keys()}')
  349. self.assertNotIn('phandle', over.props.keys())
  350. # Check the merged properties, first the base ones in '/dest'
  351. expect = {'bootph-all', 'compatible', 'stringarray', 'longbytearray',
  352. 'maybe-empty-int'}
  353. # Properties from 'base'
  354. expect.update({'#address-cells', '#size-cells'})
  355. # Properties from 'another'
  356. expect.add('new-prop')
  357. self.assertEqual(expect, set(dst.props.keys()))
  358. class TestProp(unittest.TestCase):
  359. """Test operation of the Prop class"""
  360. @classmethod
  361. def setUpClass(cls):
  362. tools.prepare_output_dir(None)
  363. @classmethod
  364. def tearDownClass(cls):
  365. tools.finalise_output_dir()
  366. def setUp(self):
  367. self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
  368. self.node = self.dtb.GetNode('/spl-test')
  369. self.fdt = self.dtb.GetFdtObj()
  370. def test_missing_node(self):
  371. """Test GetNode() when the node is missing"""
  372. self.assertEqual(None, self.dtb.GetNode('missing'))
  373. def test_phandle(self):
  374. """Test GetNode() on a phandle"""
  375. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
  376. node = dtb.GetNode('/phandle-source2')
  377. prop = node.props['clocks']
  378. self.assertTrue(fdt32_to_cpu(prop.value) > 0)
  379. def _convert_prop(self, prop_name):
  380. """Helper function to look up a property in self.node and return it
  381. Args:
  382. str: Property name to find
  383. Returns:
  384. fdt.Prop: object for this property
  385. """
  386. prop = self.fdt.getprop(self.node.Offset(), prop_name)
  387. return fdt.Prop(self.node, -1, prop_name, prop)
  388. def test_make_prop(self):
  389. """Test we can convert all the the types that are supported"""
  390. prop = self._convert_prop('boolval')
  391. self.assertEqual(Type.BOOL, prop.type)
  392. self.assertEqual(True, prop.value)
  393. prop = self._convert_prop('intval')
  394. self.assertEqual(Type.INT, prop.type)
  395. self.assertEqual(1, fdt32_to_cpu(prop.value))
  396. prop = self._convert_prop('int64val')
  397. self.assertEqual(Type.INT, prop.type)
  398. self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
  399. prop = self._convert_prop('intarray')
  400. self.assertEqual(Type.INT, prop.type)
  401. val = [fdt32_to_cpu(val) for val in prop.value]
  402. self.assertEqual([2, 3, 4], val)
  403. prop = self._convert_prop('byteval')
  404. self.assertEqual(Type.BYTE, prop.type)
  405. self.assertEqual(5, ord(prop.value))
  406. prop = self._convert_prop('longbytearray')
  407. self.assertEqual(Type.BYTE, prop.type)
  408. val = [ord(val) for val in prop.value]
  409. self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
  410. prop = self._convert_prop('stringval')
  411. self.assertEqual(Type.STRING, prop.type)
  412. self.assertEqual('message', prop.value)
  413. prop = self._convert_prop('stringarray')
  414. self.assertEqual(Type.STRING, prop.type)
  415. self.assertEqual(['multi-word', 'message'], prop.value)
  416. prop = self._convert_prop('notstring')
  417. self.assertEqual(Type.BYTE, prop.type)
  418. val = [ord(val) for val in prop.value]
  419. self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
  420. def test_get_empty(self):
  421. """Tests the GetEmpty() function for the various supported types"""
  422. self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
  423. self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
  424. self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
  425. self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
  426. def test_get_offset(self):
  427. """Test we can get the offset of a property"""
  428. prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
  429. self.assertEqual(prop.value, value)
  430. def test_widen(self):
  431. """Test widening of values"""
  432. node2 = self.dtb.GetNode('/spl-test2')
  433. node3 = self.dtb.GetNode('/spl-test3')
  434. prop = self.node.props['intval']
  435. # No action
  436. prop2 = node2.props['intval']
  437. prop.Widen(prop2)
  438. self.assertEqual(Type.INT, prop.type)
  439. self.assertEqual(1, fdt32_to_cpu(prop.value))
  440. # Convert single value to array
  441. prop2 = self.node.props['intarray']
  442. prop.Widen(prop2)
  443. self.assertEqual(Type.INT, prop.type)
  444. self.assertTrue(isinstance(prop.value, list))
  445. # A 4-byte array looks like a single integer. When widened by a longer
  446. # byte array, it should turn into an array.
  447. prop = self.node.props['longbytearray']
  448. prop2 = node2.props['longbytearray']
  449. prop3 = node3.props['longbytearray']
  450. self.assertFalse(isinstance(prop2.value, list))
  451. self.assertEqual(4, len(prop2.value))
  452. self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
  453. prop2.Widen(prop)
  454. self.assertTrue(isinstance(prop2.value, list))
  455. self.assertEqual(9, len(prop2.value))
  456. self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
  457. '\0', '\0', '\0', '\0'], prop2.value)
  458. prop3.Widen(prop)
  459. self.assertTrue(isinstance(prop3.value, list))
  460. self.assertEqual(9, len(prop3.value))
  461. self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
  462. '\x0e', '\x0f', '\x10', '\0'], prop3.value)
  463. def test_widen_more(self):
  464. """More tests of widening values"""
  465. node2 = self.dtb.GetNode('/spl-test2')
  466. node3 = self.dtb.GetNode('/spl-test3')
  467. prop = self.node.props['intval']
  468. # Test widening a single string into a string array
  469. prop = self.node.props['stringval']
  470. prop2 = node2.props['stringarray']
  471. self.assertFalse(isinstance(prop.value, list))
  472. self.assertEqual(7, len(prop.value))
  473. prop.Widen(prop2)
  474. self.assertTrue(isinstance(prop.value, list))
  475. self.assertEqual(3, len(prop.value))
  476. # Enlarging an existing array
  477. prop = self.node.props['stringarray']
  478. prop2 = node2.props['stringarray']
  479. self.assertTrue(isinstance(prop.value, list))
  480. self.assertEqual(2, len(prop.value))
  481. prop.Widen(prop2)
  482. self.assertTrue(isinstance(prop.value, list))
  483. self.assertEqual(3, len(prop.value))
  484. # Widen an array of ints with an int (should do nothing)
  485. prop = self.node.props['intarray']
  486. prop2 = node2.props['intval']
  487. self.assertEqual(Type.INT, prop.type)
  488. self.assertEqual(3, len(prop.value))
  489. prop.Widen(prop2)
  490. self.assertEqual(Type.INT, prop.type)
  491. self.assertEqual(3, len(prop.value))
  492. # Widen an empty bool to an int
  493. prop = self.node.props['maybe-empty-int']
  494. prop3 = node3.props['maybe-empty-int']
  495. self.assertEqual(Type.BOOL, prop.type)
  496. self.assertEqual(True, prop.value)
  497. self.assertEqual(Type.INT, prop3.type)
  498. self.assertFalse(isinstance(prop.value, list))
  499. self.assertEqual(4, len(prop3.value))
  500. prop.Widen(prop3)
  501. self.assertEqual(Type.INT, prop.type)
  502. self.assertTrue(isinstance(prop.value, list))
  503. self.assertEqual(1, len(prop.value))
  504. def test_add(self):
  505. """Test adding properties"""
  506. self.fdt.pack()
  507. # This function should automatically expand the device tree
  508. self.node.AddZeroProp('one')
  509. self.node.AddZeroProp('two')
  510. self.node.AddZeroProp('three')
  511. self.dtb.Sync(auto_resize=True)
  512. # Updating existing properties should be OK, since the device-tree size
  513. # does not change
  514. self.fdt.pack()
  515. self.node.SetInt('one', 1)
  516. self.node.SetInt('two', 2)
  517. self.node.SetInt('three', 3)
  518. self.dtb.Sync(auto_resize=False)
  519. # This should fail since it would need to increase the device-tree size
  520. self.node.AddZeroProp('four')
  521. with self.assertRaises(libfdt.FdtException) as exc:
  522. self.dtb.Sync(auto_resize=False)
  523. self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
  524. self.dtb.Sync(auto_resize=True)
  525. def test_add_more(self):
  526. """Test various other methods for adding and setting properties"""
  527. self.node.AddZeroProp('one')
  528. self.dtb.Sync(auto_resize=True)
  529. data = self.fdt.getprop(self.node.Offset(), 'one')
  530. self.assertEqual(0, fdt32_to_cpu(data))
  531. self.node.SetInt('one', 1)
  532. self.dtb.Sync(auto_resize=False)
  533. data = self.fdt.getprop(self.node.Offset(), 'one')
  534. self.assertEqual(1, fdt32_to_cpu(data))
  535. val = 1234
  536. self.node.AddInt('integer', val)
  537. self.dtb.Sync(auto_resize=True)
  538. data = self.fdt.getprop(self.node.Offset(), 'integer')
  539. self.assertEqual(val, fdt32_to_cpu(data))
  540. val = '123' + chr(0) + '456'
  541. self.node.AddString('string', val)
  542. self.dtb.Sync(auto_resize=True)
  543. data = self.fdt.getprop(self.node.Offset(), 'string')
  544. self.assertEqual(tools.to_bytes(val) + b'\0', data)
  545. self.fdt.pack()
  546. self.node.SetString('string', val + 'x')
  547. with self.assertRaises(libfdt.FdtException) as exc:
  548. self.dtb.Sync(auto_resize=False)
  549. self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
  550. self.node.SetString('string', val[:-1])
  551. prop = self.node.props['string']
  552. prop.SetData(tools.to_bytes(val))
  553. self.dtb.Sync(auto_resize=False)
  554. data = self.fdt.getprop(self.node.Offset(), 'string')
  555. self.assertEqual(tools.to_bytes(val), data)
  556. self.node.AddEmptyProp('empty', 5)
  557. self.dtb.Sync(auto_resize=True)
  558. prop = self.node.props['empty']
  559. prop.SetData(tools.to_bytes(val))
  560. self.dtb.Sync(auto_resize=False)
  561. data = self.fdt.getprop(self.node.Offset(), 'empty')
  562. self.assertEqual(tools.to_bytes(val), data)
  563. self.node.SetData('empty', b'123')
  564. self.assertEqual(b'123', prop.bytes)
  565. # Trying adding a lot of data at once
  566. self.node.AddData('data', tools.get_bytes(65, 20000))
  567. self.dtb.Sync(auto_resize=True)
  568. def test_string_list(self):
  569. """Test adding string-list property to a node"""
  570. val = ['123', '456']
  571. self.node.AddStringList('stringlist', val)
  572. self.dtb.Sync(auto_resize=True)
  573. data = self.fdt.getprop(self.node.Offset(), 'stringlist')
  574. self.assertEqual(b'123\x00456\0', data)
  575. val = []
  576. self.node.AddStringList('stringlist', val)
  577. self.dtb.Sync(auto_resize=True)
  578. data = self.fdt.getprop(self.node.Offset(), 'stringlist')
  579. self.assertEqual(b'', data)
  580. def test_delete_node(self):
  581. """Test deleting a node"""
  582. old_offset = self.fdt.path_offset('/spl-test')
  583. self.assertGreater(old_offset, 0)
  584. self.node.Delete()
  585. self.dtb.Sync()
  586. new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
  587. self.assertEqual(-libfdt.NOTFOUND, new_offset)
  588. def test_from_data(self):
  589. """Test creating an FDT from data"""
  590. dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
  591. self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
  592. self.node.AddEmptyProp('empty', 5)
  593. self.dtb.Sync(auto_resize=True)
  594. self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
  595. def test_missing_set_int(self):
  596. """Test handling of a missing property with SetInt"""
  597. with self.assertRaises(ValueError) as exc:
  598. self.node.SetInt('one', 1)
  599. self.assertIn("node '/spl-test': Missing property 'one'",
  600. str(exc.exception))
  601. def test_missing_set_data(self):
  602. """Test handling of a missing property with SetData"""
  603. with self.assertRaises(ValueError) as exc:
  604. self.node.SetData('one', b'data')
  605. self.assertIn("node '/spl-test': Missing property 'one'",
  606. str(exc.exception))
  607. def test_missing_set_string(self):
  608. """Test handling of a missing property with SetString"""
  609. with self.assertRaises(ValueError) as exc:
  610. self.node.SetString('one', 1)
  611. self.assertIn("node '/spl-test': Missing property 'one'",
  612. str(exc.exception))
  613. def test_get_filename(self):
  614. """Test the dtb filename can be provided"""
  615. self.assertEqual(tools.get_output_filename('source.dtb'),
  616. self.dtb.GetFilename())
  617. class TestFdtUtil(unittest.TestCase):
  618. """Tests for the fdt_util module
  619. This module will likely be mostly replaced at some point, once upstream
  620. libfdt has better Python support. For now, this provides tests for current
  621. functionality.
  622. """
  623. @classmethod
  624. def setUpClass(cls):
  625. tools.prepare_output_dir(None)
  626. @classmethod
  627. def tearDownClass(cls):
  628. tools.finalise_output_dir()
  629. def setUp(self):
  630. self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
  631. self.node = self.dtb.GetNode('/spl-test')
  632. def test_get_int(self):
  633. """Test getting an int from a node"""
  634. self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
  635. self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
  636. with self.assertRaises(ValueError) as exc:
  637. fdt_util.GetInt(self.node, 'intarray')
  638. self.assertIn("property 'intarray' has list value: expecting a single "
  639. 'integer', str(exc.exception))
  640. def test_get_int64(self):
  641. """Test getting a 64-bit int from a node"""
  642. self.assertEqual(0x123456789abcdef0,
  643. fdt_util.GetInt64(self.node, 'int64val'))
  644. self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
  645. with self.assertRaises(ValueError) as exc:
  646. fdt_util.GetInt64(self.node, 'intarray')
  647. self.assertIn(
  648. "property 'intarray' should be a list with 2 items for 64-bit values",
  649. str(exc.exception))
  650. def test_get_string(self):
  651. """Test getting a string from a node"""
  652. self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
  653. self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
  654. 'test'))
  655. self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
  656. with self.assertRaises(ValueError) as exc:
  657. self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
  658. self.assertIn("property 'stringarray' has list value: expecting a "
  659. 'single string', str(exc.exception))
  660. def test_get_string_list(self):
  661. """Test getting a string list from a node"""
  662. self.assertEqual(['message'],
  663. fdt_util.GetStringList(self.node, 'stringval'))
  664. self.assertEqual(
  665. ['multi-word', 'message'],
  666. fdt_util.GetStringList(self.node, 'stringarray'))
  667. self.assertEqual(['test'],
  668. fdt_util.GetStringList(self.node, 'missing', ['test']))
  669. self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
  670. def test_get_args(self):
  671. """Test getting arguments from a node"""
  672. node = self.dtb.GetNode('/orig-node')
  673. self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
  674. self.assertEqual(
  675. ['multi-word', 'message'],
  676. fdt_util.GetArgs(self.node, 'stringarray'))
  677. self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
  678. self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
  679. fdt_util.GetArgs(node, 'args'))
  680. self.assertEqual(['a space', 'there'],
  681. fdt_util.GetArgs(node, 'args2'))
  682. self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
  683. fdt_util.GetArgs(node, 'args3'))
  684. with self.assertRaises(ValueError) as exc:
  685. fdt_util.GetArgs(self.node, 'missing')
  686. self.assertIn(
  687. "Node '/spl-test': Expected property 'missing'",
  688. str(exc.exception))
  689. def test_get_bool(self):
  690. """Test getting a bool from a node"""
  691. self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
  692. self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
  693. self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
  694. self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
  695. def test_get_byte(self):
  696. """Test getting a byte from a node"""
  697. self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
  698. self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
  699. with self.assertRaises(ValueError) as exc:
  700. fdt_util.GetByte(self.node, 'longbytearray')
  701. self.assertIn("property 'longbytearray' has list value: expecting a "
  702. 'single byte', str(exc.exception))
  703. with self.assertRaises(ValueError) as exc:
  704. fdt_util.GetByte(self.node, 'intval')
  705. self.assertIn("property 'intval' has length 4, expecting 1",
  706. str(exc.exception))
  707. def test_get_bytes(self):
  708. """Test getting multiple bytes from a node"""
  709. self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
  710. self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
  711. self.assertEqual(
  712. bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
  713. with self.assertRaises(ValueError) as exc:
  714. fdt_util.GetBytes(self.node, 'longbytearray', 7)
  715. self.assertIn(
  716. "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
  717. str(exc.exception))
  718. self.assertEqual(
  719. bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
  720. self.assertEqual(
  721. bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
  722. def test_get_phandle_list(self):
  723. """Test getting a list of phandles from a node"""
  724. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
  725. node = dtb.GetNode('/phandle-source2')
  726. self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
  727. node = dtb.GetNode('/phandle-source')
  728. self.assertEqual([1, 2, 11, 3, 12, 13, 1],
  729. fdt_util.GetPhandleList(node, 'clocks'))
  730. self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
  731. def test_get_data_type(self):
  732. """Test getting a value of a particular type from a node"""
  733. self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
  734. self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
  735. str))
  736. with self.assertRaises(ValueError):
  737. self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
  738. bool))
  739. def test_fdt_cells_to_cpu(self):
  740. """Test getting cells with the correct endianness"""
  741. val = self.node.props['intarray'].value
  742. self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
  743. self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
  744. dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
  745. node1 = dtb2.GetNode('/test1')
  746. val = node1.props['reg'].value
  747. self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
  748. node2 = dtb2.GetNode('/test2')
  749. val = node2.props['reg'].value
  750. self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
  751. self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
  752. 2))
  753. self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
  754. def test_ensure_compiled(self):
  755. """Test a degenerate case of this function (file already compiled)"""
  756. dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
  757. self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
  758. def test_ensure_compiled_tmpdir(self):
  759. """Test providing a temporary directory"""
  760. old_outdir = tools.outdir
  761. try:
  762. tools.outdir= None
  763. tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
  764. dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
  765. tmpdir)
  766. self.assertEqual(tmpdir, os.path.dirname(dtb))
  767. shutil.rmtree(tmpdir)
  768. finally:
  769. tools.outdir = old_outdir
  770. def test_get_phandle_name_offset(self):
  771. val = fdt_util.GetPhandleNameOffset(self.node, 'missing')
  772. self.assertIsNone(val)
  773. dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
  774. node = dtb.GetNode('/phandle-source')
  775. node, name, offset = fdt_util.GetPhandleNameOffset(node,
  776. 'phandle-name-offset')
  777. self.assertEqual('phandle3-target', node.name)
  778. self.assertEqual('fred', name)
  779. self.assertEqual(123, offset)
  780. def run_test_coverage(build_dir):
  781. """Run the tests and check that we get 100% coverage
  782. Args:
  783. build_dir (str): Directory containing the build output
  784. """
  785. test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
  786. ['tools/patman/*.py', 'tools/u_boot_pylib/*', '*test_fdt.py'],
  787. build_dir)
  788. def run_tests(names, processes):
  789. """Run all the test we have for the fdt model
  790. Args:
  791. names (list of str): List of test names provided. Only the first is used
  792. processes (int): Number of processes to use (None means as many as there
  793. are CPUs on the system. This must be set to 1 when running under
  794. the python3-coverage tool
  795. Returns:
  796. int: Return code, 0 on success
  797. """
  798. test_name = names[0] if names else None
  799. result = test_util.run_test_suites(
  800. 'test_fdt', False, False, False, processes, test_name, None,
  801. [TestFdt, TestNode, TestProp, TestFdtUtil])
  802. return (0 if result.wasSuccessful() else 1)
  803. def main():
  804. """Main program for this tool"""
  805. parser = ArgumentParser()
  806. parser.add_argument('-B', '--build-dir', type=str, default='b',
  807. help='Directory containing the build output')
  808. parser.add_argument('-P', '--processes', type=int,
  809. help='set number of processes to use for running tests')
  810. parser.add_argument('-t', '--test', action='store_true', dest='test',
  811. default=False, help='run tests')
  812. parser.add_argument('-T', '--test-coverage', action='store_true',
  813. default=False,
  814. help='run tests and check for 100% coverage')
  815. parser.add_argument('name', nargs='*')
  816. args = parser.parse_args()
  817. # Run our meagre tests
  818. if args.test:
  819. ret_code = run_tests(args.name, args.processes)
  820. return ret_code
  821. if args.test_coverage:
  822. run_test_coverage(args.build_dir)
  823. return 0
  824. if __name__ == '__main__':
  825. sys.exit(main())