fdt.py 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. #!/usr/bin/python
  2. # SPDX-License-Identifier: GPL-2.0+
  3. #
  4. # Copyright (C) 2016 Google, Inc
  5. # Written by Simon Glass <sjg@chromium.org>
  6. #
  7. from enum import IntEnum
  8. import struct
  9. import sys
  10. from dtoc import fdt_util
  11. import libfdt
  12. from libfdt import QUIET_NOTFOUND
  13. from u_boot_pylib import tools
  14. from u_boot_pylib import tout
  15. # This deals with a device tree, presenting it as an assortment of Node and
  16. # Prop objects, representing nodes and properties, respectively. This file
  17. # contains the base classes and defines the high-level API. You can use
  18. # FdtScan() as a convenience function to create and scan an Fdt.
  19. # This implementation uses a libfdt Python library to access the device tree,
  20. # so it is fairly efficient.
  21. # A list of types we support
  22. class Type(IntEnum):
  23. # Types in order from widest to narrowest
  24. (BYTE, INT, STRING, BOOL, INT64) = range(5)
  25. def needs_widening(self, other):
  26. """Check if this type needs widening to hold a value from another type
  27. A wider type is one that can hold a wider array of information than
  28. another one, or is less restrictive, so it can hold the information of
  29. another type as well as its own. This is similar to the concept of
  30. type-widening in C.
  31. This uses a simple arithmetic comparison, since type values are in order
  32. from widest (BYTE) to narrowest (INT64).
  33. Args:
  34. other: Other type to compare against
  35. Return:
  36. True if the other type is wider
  37. """
  38. return self.value > other.value
  39. def CheckErr(errnum, msg):
  40. if errnum:
  41. raise ValueError('Error %d: %s: %s' %
  42. (errnum, libfdt.fdt_strerror(errnum), msg))
  43. def BytesToValue(data):
  44. """Converts a string of bytes into a type and value
  45. Args:
  46. A bytes value (which on Python 2 is an alias for str)
  47. Return:
  48. A tuple:
  49. Type of data
  50. Data, either a single element or a list of elements. Each element
  51. is one of:
  52. Type.STRING: str/bytes value from the property
  53. Type.INT: a byte-swapped integer stored as a 4-byte str/bytes
  54. Type.BYTE: a byte stored as a single-byte str/bytes
  55. """
  56. data = bytes(data)
  57. size = len(data)
  58. strings = data.split(b'\0')
  59. is_string = True
  60. count = len(strings) - 1
  61. if count > 0 and not len(strings[-1]):
  62. for string in strings[:-1]:
  63. if not string:
  64. is_string = False
  65. break
  66. for ch in string:
  67. if ch < 32 or ch > 127:
  68. is_string = False
  69. break
  70. else:
  71. is_string = False
  72. if is_string:
  73. if count == 1:
  74. return Type.STRING, strings[0].decode()
  75. else:
  76. return Type.STRING, [s.decode() for s in strings[:-1]]
  77. if size % 4:
  78. if size == 1:
  79. return Type.BYTE, chr(data[0])
  80. else:
  81. return Type.BYTE, [chr(ch) for ch in list(data)]
  82. val = []
  83. for i in range(0, size, 4):
  84. val.append(data[i:i + 4])
  85. if size == 4:
  86. return Type.INT, val[0]
  87. else:
  88. return Type.INT, val
  89. class Prop:
  90. """A device tree property
  91. Properties:
  92. node: Node containing this property
  93. offset: Offset of the property (None if still to be synced)
  94. name: Property name (as per the device tree)
  95. value: Property value as a string of bytes, or a list of strings of
  96. bytes
  97. type: Value type
  98. """
  99. def __init__(self, node, offset, name, data):
  100. self._node = node
  101. self._offset = offset
  102. self.name = name
  103. self.value = None
  104. self.bytes = bytes(data)
  105. self.dirty = offset is None
  106. if not data:
  107. self.type = Type.BOOL
  108. self.value = True
  109. return
  110. self.type, self.value = BytesToValue(bytes(data))
  111. def RefreshOffset(self, poffset):
  112. self._offset = poffset
  113. def Widen(self, newprop):
  114. """Figure out which property type is more general
  115. Given a current property and a new property, this function returns the
  116. one that is less specific as to type. The less specific property will
  117. be ble to represent the data in the more specific property. This is
  118. used for things like:
  119. node1 {
  120. compatible = "fred";
  121. value = <1>;
  122. };
  123. node1 {
  124. compatible = "fred";
  125. value = <1 2>;
  126. };
  127. He we want to use an int array for 'value'. The first property
  128. suggests that a single int is enough, but the second one shows that
  129. it is not. Calling this function with these two propertes would
  130. update the current property to be like the second, since it is less
  131. specific.
  132. """
  133. if self.type.needs_widening(newprop.type):
  134. # A boolean has an empty value: if it exists it is True and if not
  135. # it is False. So when widening we always start with an empty list
  136. # since the only valid integer property would be an empty list of
  137. # integers.
  138. # e.g. this is a boolean:
  139. # some-prop;
  140. # and it would be widened to int list by:
  141. # some-prop = <1 2>;
  142. if self.type == Type.BOOL:
  143. self.type = Type.INT
  144. self.value = [self.GetEmpty(self.type)]
  145. if self.type == Type.INT and newprop.type == Type.BYTE:
  146. if type(self.value) == list:
  147. new_value = []
  148. for val in self.value:
  149. new_value += [chr(by) for by in val]
  150. else:
  151. new_value = [chr(by) for by in self.value]
  152. self.value = new_value
  153. self.type = newprop.type
  154. if type(newprop.value) == list:
  155. if type(self.value) != list:
  156. self.value = [self.value]
  157. if len(newprop.value) > len(self.value):
  158. val = self.GetEmpty(self.type)
  159. while len(self.value) < len(newprop.value):
  160. self.value.append(val)
  161. @classmethod
  162. def GetEmpty(self, type):
  163. """Get an empty / zero value of the given type
  164. Returns:
  165. A single value of the given type
  166. """
  167. if type == Type.BYTE:
  168. return chr(0)
  169. elif type == Type.INT:
  170. return struct.pack('>I', 0);
  171. elif type == Type.STRING:
  172. return ''
  173. else:
  174. return True
  175. def GetOffset(self):
  176. """Get the offset of a property
  177. Returns:
  178. The offset of the property (struct fdt_property) within the file
  179. """
  180. self._node._fdt.CheckCache()
  181. return self._node._fdt.GetStructOffset(self._offset)
  182. def SetInt(self, val):
  183. """Set the integer value of the property
  184. The device tree is marked dirty so that the value will be written to
  185. the block on the next sync.
  186. Args:
  187. val: Integer value (32-bit, single cell)
  188. """
  189. self.bytes = struct.pack('>I', val);
  190. self.value = self.bytes
  191. self.type = Type.INT
  192. self.dirty = True
  193. def SetData(self, bytes):
  194. """Set the value of a property as bytes
  195. Args:
  196. bytes: New property value to set
  197. """
  198. self.bytes = bytes
  199. self.type, self.value = BytesToValue(bytes)
  200. self.dirty = True
  201. def Sync(self, auto_resize=False):
  202. """Sync property changes back to the device tree
  203. This updates the device tree blob with any changes to this property
  204. since the last sync.
  205. Args:
  206. auto_resize: Resize the device tree automatically if it does not
  207. have enough space for the update
  208. Raises:
  209. FdtException if auto_resize is False and there is not enough space
  210. """
  211. if self.dirty:
  212. node = self._node
  213. tout.debug(f'sync {node.path}: {self.name}')
  214. fdt_obj = node._fdt._fdt_obj
  215. node_name = fdt_obj.get_name(node._offset)
  216. if node_name and node_name != node.name:
  217. raise ValueError("Internal error, node '%s' name mismatch '%s'" %
  218. (node.path, node_name))
  219. if auto_resize:
  220. while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
  221. (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
  222. fdt_obj.resize(fdt_obj.totalsize() + 1024 +
  223. len(self.bytes))
  224. fdt_obj.setprop(node.Offset(), self.name, self.bytes)
  225. else:
  226. fdt_obj.setprop(node.Offset(), self.name, self.bytes)
  227. self.dirty = False
  228. def purge(self):
  229. """Set a property offset to None
  230. The property remains in the tree structure and will be recreated when
  231. the FDT is synced
  232. """
  233. self._offset = None
  234. self.dirty = True
  235. class Node:
  236. """A device tree node
  237. Properties:
  238. parent: Parent Node
  239. offset: Integer offset in the device tree (None if to be synced)
  240. name: Device tree node tname
  241. path: Full path to node, along with the node name itself
  242. _fdt: Device tree object
  243. subnodes: A list of subnodes for this node, each a Node object
  244. props: A dict of properties for this node, each a Prop object.
  245. Keyed by property name
  246. """
  247. def __init__(self, fdt, parent, offset, name, path):
  248. self._fdt = fdt
  249. self.parent = parent
  250. self._offset = offset
  251. self.name = name
  252. self.path = path
  253. self.subnodes = []
  254. self.props = {}
  255. def GetFdt(self):
  256. """Get the Fdt object for this node
  257. Returns:
  258. Fdt object
  259. """
  260. return self._fdt
  261. def FindNode(self, name):
  262. """Find a node given its name
  263. Args:
  264. name: Node name to look for
  265. Returns:
  266. Node object if found, else None
  267. """
  268. for subnode in self.subnodes:
  269. if subnode.name == name:
  270. return subnode
  271. return None
  272. def Offset(self):
  273. """Returns the offset of a node, after checking the cache
  274. This should be used instead of self._offset directly, to ensure that
  275. the cache does not contain invalid offsets.
  276. """
  277. self._fdt.CheckCache()
  278. return self._offset
  279. def Scan(self):
  280. """Scan a node's properties and subnodes
  281. This fills in the props and subnodes properties, recursively
  282. searching into subnodes so that the entire tree is built.
  283. """
  284. fdt_obj = self._fdt._fdt_obj
  285. self.props = self._fdt.GetProps(self)
  286. phandle = fdt_obj.get_phandle(self.Offset())
  287. if phandle:
  288. dup = self._fdt.phandle_to_node.get(phandle)
  289. if dup:
  290. raise ValueError(
  291. f'Duplicate phandle {phandle} in nodes {dup.path} and {self.path}')
  292. self._fdt.phandle_to_node[phandle] = self
  293. offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND)
  294. while offset >= 0:
  295. sep = '' if self.path[-1] == '/' else '/'
  296. name = fdt_obj.get_name(offset)
  297. path = self.path + sep + name
  298. node = Node(self._fdt, self, offset, name, path)
  299. self.subnodes.append(node)
  300. node.Scan()
  301. offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
  302. def Refresh(self, my_offset):
  303. """Fix up the _offset for each node, recursively
  304. Note: This does not take account of property offsets - these will not
  305. be updated.
  306. """
  307. fdt_obj = self._fdt._fdt_obj
  308. if self._offset != my_offset:
  309. self._offset = my_offset
  310. name = fdt_obj.get_name(self._offset)
  311. if name and self.name != name:
  312. raise ValueError("Internal error, node '%s' name mismatch '%s'" %
  313. (self.path, name))
  314. offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
  315. for subnode in self.subnodes:
  316. if subnode._offset is None:
  317. continue
  318. if subnode.name != fdt_obj.get_name(offset):
  319. raise ValueError('Internal error, node name mismatch %s != %s' %
  320. (subnode.name, fdt_obj.get_name(offset)))
  321. subnode.Refresh(offset)
  322. offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND)
  323. if offset != -libfdt.FDT_ERR_NOTFOUND:
  324. raise ValueError('Internal error, offset == %d' % offset)
  325. poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND)
  326. while poffset >= 0:
  327. p = fdt_obj.get_property_by_offset(poffset)
  328. prop = self.props.get(p.name)
  329. if not prop:
  330. raise ValueError("Internal error, node '%s' property '%s' missing, "
  331. 'offset %d' % (self.path, p.name, poffset))
  332. prop.RefreshOffset(poffset)
  333. poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
  334. def DeleteProp(self, prop_name):
  335. """Delete a property of a node
  336. The property is deleted and the offset cache is invalidated.
  337. Args:
  338. prop_name: Name of the property to delete
  339. Raises:
  340. ValueError if the property does not exist
  341. """
  342. CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name),
  343. "Node '%s': delete property: '%s'" % (self.path, prop_name))
  344. del self.props[prop_name]
  345. self._fdt.Invalidate()
  346. def AddZeroProp(self, prop_name):
  347. """Add a new property to the device tree with an integer value of 0.
  348. Args:
  349. prop_name: Name of property
  350. """
  351. self.props[prop_name] = Prop(self, None, prop_name,
  352. tools.get_bytes(0, 4))
  353. def AddEmptyProp(self, prop_name, len):
  354. """Add a property with a fixed data size, for filling in later
  355. The device tree is marked dirty so that the value will be written to
  356. the blob on the next sync.
  357. Args:
  358. prop_name: Name of property
  359. len: Length of data in property
  360. """
  361. value = tools.get_bytes(0, len)
  362. self.props[prop_name] = Prop(self, None, prop_name, value)
  363. def _CheckProp(self, prop_name):
  364. """Check if a property is present
  365. Args:
  366. prop_name: Name of property
  367. Returns:
  368. self
  369. Raises:
  370. ValueError if the property is missing
  371. """
  372. if prop_name not in self.props:
  373. raise ValueError("Fdt '%s', node '%s': Missing property '%s'" %
  374. (self._fdt._fname, self.path, prop_name))
  375. return self
  376. def SetInt(self, prop_name, val):
  377. """Update an integer property int the device tree.
  378. This is not allowed to change the size of the FDT.
  379. The device tree is marked dirty so that the value will be written to
  380. the blob on the next sync.
  381. Args:
  382. prop_name: Name of property
  383. val: Value to set
  384. """
  385. self._CheckProp(prop_name).props[prop_name].SetInt(val)
  386. def SetData(self, prop_name, val):
  387. """Set the data value of a property
  388. The device tree is marked dirty so that the value will be written to
  389. the blob on the next sync.
  390. Args:
  391. prop_name: Name of property to set
  392. val: Data value to set
  393. """
  394. self._CheckProp(prop_name).props[prop_name].SetData(val)
  395. def SetString(self, prop_name, val):
  396. """Set the string value of a property
  397. The device tree is marked dirty so that the value will be written to
  398. the blob on the next sync.
  399. Args:
  400. prop_name: Name of property to set
  401. val: String value to set (will be \0-terminated in DT)
  402. """
  403. if type(val) == str:
  404. val = val.encode('utf-8')
  405. self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0')
  406. def AddData(self, prop_name, val):
  407. """Add a new property to a node
  408. The device tree is marked dirty so that the value will be written to
  409. the blob on the next sync.
  410. Args:
  411. prop_name: Name of property to add
  412. val: Bytes value of property
  413. Returns:
  414. Prop added
  415. """
  416. prop = Prop(self, None, prop_name, val)
  417. self.props[prop_name] = prop
  418. return prop
  419. def AddString(self, prop_name, val):
  420. """Add a new string property to a node
  421. The device tree is marked dirty so that the value will be written to
  422. the blob on the next sync.
  423. Args:
  424. prop_name: Name of property to add
  425. val: String value of property
  426. Returns:
  427. Prop added
  428. """
  429. val = bytes(val, 'utf-8')
  430. return self.AddData(prop_name, val + b'\0')
  431. def AddStringList(self, prop_name, val):
  432. """Add a new string-list property to a node
  433. The device tree is marked dirty so that the value will be written to
  434. the blob on the next sync.
  435. Args:
  436. prop_name: Name of property to add
  437. val (list of str): List of strings to add
  438. Returns:
  439. Prop added
  440. """
  441. out = b'\0'.join(bytes(s, 'utf-8') for s in val) + b'\0' if val else b''
  442. return self.AddData(prop_name, out)
  443. def AddInt(self, prop_name, val):
  444. """Add a new integer property to a node
  445. The device tree is marked dirty so that the value will be written to
  446. the blob on the next sync.
  447. Args:
  448. prop_name: Name of property to add
  449. val: Integer value of property
  450. Returns:
  451. Prop added
  452. """
  453. return self.AddData(prop_name, struct.pack('>I', val))
  454. def Subnode(self, name):
  455. """Create new subnode for the node
  456. Args:
  457. name: name of node to add
  458. Returns:
  459. New subnode that was created
  460. """
  461. path = self.path + '/' + name
  462. return Node(self._fdt, self, None, name, path)
  463. def AddSubnode(self, name):
  464. """Add a new subnode to the node, after all other subnodes
  465. Args:
  466. name: name of node to add
  467. Returns:
  468. New subnode that was created
  469. """
  470. subnode = self.Subnode(name)
  471. self.subnodes.append(subnode)
  472. return subnode
  473. def insert_subnode(self, name):
  474. """Add a new subnode to the node, before all other subnodes
  475. This deletes other subnodes and sets their offset to None, so that they
  476. will be recreated after this one.
  477. Args:
  478. name: name of node to add
  479. Returns:
  480. New subnode that was created
  481. """
  482. # Deleting a node invalidates the offsets of all following nodes, so
  483. # process in reverse order so that the offset of each node remains valid
  484. # until deletion.
  485. for subnode in reversed(self.subnodes):
  486. subnode.purge(True)
  487. subnode = self.Subnode(name)
  488. self.subnodes.insert(0, subnode)
  489. return subnode
  490. def purge(self, delete_it=False):
  491. """Purge this node, setting offset to None and deleting from FDT"""
  492. if self._offset is not None:
  493. if delete_it:
  494. CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
  495. "Node '%s': delete" % self.path)
  496. self._offset = None
  497. self._fdt.Invalidate()
  498. for prop in self.props.values():
  499. prop.purge()
  500. for subnode in self.subnodes:
  501. subnode.purge(False)
  502. def move_to_first(self):
  503. """Move the current node to first in its parent's node list"""
  504. parent = self.parent
  505. if parent.subnodes and parent.subnodes[0] == self:
  506. return
  507. for subnode in reversed(parent.subnodes):
  508. subnode.purge(True)
  509. new_subnodes = [self]
  510. for subnode in parent.subnodes:
  511. #subnode.purge(False)
  512. if subnode != self:
  513. new_subnodes.append(subnode)
  514. parent.subnodes = new_subnodes
  515. def Delete(self):
  516. """Delete a node
  517. The node is deleted and the offset cache is invalidated.
  518. Args:
  519. node (Node): Node to delete
  520. Raises:
  521. ValueError if the node does not exist
  522. """
  523. CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
  524. "Node '%s': delete" % self.path)
  525. parent = self.parent
  526. self._fdt.Invalidate()
  527. parent.subnodes.remove(self)
  528. def Sync(self, auto_resize=False):
  529. """Sync node changes back to the device tree
  530. This updates the device tree blob with any changes to this node and its
  531. subnodes since the last sync.
  532. Args:
  533. auto_resize: Resize the device tree automatically if it does not
  534. have enough space for the update
  535. Returns:
  536. True if the node had to be added, False if it already existed
  537. Raises:
  538. FdtException if auto_resize is False and there is not enough space
  539. """
  540. added = False
  541. if self._offset is None:
  542. # The subnode doesn't exist yet, so add it
  543. fdt_obj = self._fdt._fdt_obj
  544. if auto_resize:
  545. while True:
  546. offset = fdt_obj.add_subnode(self.parent._offset, self.name,
  547. (libfdt.NOSPACE,))
  548. if offset != -libfdt.NOSPACE:
  549. break
  550. fdt_obj.resize(fdt_obj.totalsize() + 1024)
  551. else:
  552. offset = fdt_obj.add_subnode(self.parent._offset, self.name)
  553. self._offset = offset
  554. added = True
  555. # Sync the existing subnodes first, so that we can rely on the offsets
  556. # being correct. As soon as we add new subnodes, it pushes all the
  557. # existing subnodes up.
  558. for node in reversed(self.subnodes):
  559. if node._offset is not None:
  560. node.Sync(auto_resize)
  561. # Sync subnodes in reverse so that we get the expected order. Each
  562. # new node goes at the start of the subnode list. This avoids an O(n^2)
  563. # rescan of node offsets.
  564. num_added = 0
  565. for node in reversed(self.subnodes):
  566. if node.Sync(auto_resize):
  567. num_added += 1
  568. if num_added:
  569. # Reorder our list of nodes to put the new ones first, since that's
  570. # what libfdt does
  571. old_count = len(self.subnodes) - num_added
  572. subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
  573. self.subnodes = subnodes
  574. # Sync properties now, whose offsets should not have been disturbed,
  575. # since properties come before subnodes. This is done after all the
  576. # subnode processing above, since updating properties can disturb the
  577. # offsets of those subnodes.
  578. # Properties are synced in reverse order, with new properties added
  579. # before existing properties are synced. This ensures that the offsets
  580. # of earlier properties are not disturbed.
  581. # Note that new properties will have an offset of None here, which
  582. # Python cannot sort against int. So use a large value instead so that
  583. # new properties are added first.
  584. prop_list = sorted(self.props.values(),
  585. key=lambda prop: prop._offset or 1 << 31,
  586. reverse=True)
  587. for prop in prop_list:
  588. prop.Sync(auto_resize)
  589. return added
  590. def merge_props(self, src, copy_phandles):
  591. """Copy missing properties (except 'phandle') from another node
  592. Args:
  593. src (Node): Node containing properties to copy
  594. copy_phandles (bool): True to copy phandle properties in nodes
  595. Adds properties which are present in src but not in this node. Any
  596. 'phandle' property is not copied since this might result in two nodes
  597. with the same phandle, thus making phandle references ambiguous.
  598. """
  599. tout.debug(f'copy to {self.path}: {src.path}')
  600. for name, src_prop in src.props.items():
  601. done = False
  602. if name not in self.props:
  603. if copy_phandles or name != 'phandle':
  604. self.props[name] = Prop(self, None, name, src_prop.bytes)
  605. done = True
  606. tout.debug(f" {name}{'' if done else ' - ignored'}")
  607. def copy_node(self, src, copy_phandles=False):
  608. """Copy a node and all its subnodes into this node
  609. Args:
  610. src (Node): Node to copy
  611. copy_phandles (bool): True to copy phandle properties in nodes
  612. Returns:
  613. Node: Resulting destination node
  614. This works recursively, with copy_phandles being set to True for the
  615. recursive calls
  616. The new node is put before all other nodes. If the node already
  617. exists, just its subnodes and properties are copied, placing them before
  618. any existing subnodes. Properties which exist in the destination node
  619. already are not copied.
  620. """
  621. dst = self.FindNode(src.name)
  622. if dst:
  623. dst.move_to_first()
  624. else:
  625. dst = self.insert_subnode(src.name)
  626. dst.merge_props(src, copy_phandles)
  627. # Process in reverse order so that they appear correctly in the result,
  628. # since copy_node() puts the node first in the list
  629. for node in reversed(src.subnodes):
  630. dst.copy_node(node, True)
  631. return dst
  632. def copy_subnodes_from_phandles(self, phandle_list):
  633. """Copy subnodes of a list of nodes into another node
  634. Args:
  635. phandle_list (list of int): List of phandles of nodes to copy
  636. For each node in the phandle list, its subnodes and their properties are
  637. copied recursively. Note that it does not copy the node itself, nor its
  638. properties.
  639. """
  640. # Process in reverse order, since new nodes are inserted at the start of
  641. # the destination's node list. We want them to appear in order of the
  642. # phandle list
  643. for phandle in phandle_list.__reversed__():
  644. parent = self.GetFdt().LookupPhandle(phandle)
  645. tout.debug(f'adding template {parent.path} to node {self.path}')
  646. for node in parent.subnodes.__reversed__():
  647. dst = self.copy_node(node)
  648. tout.debug(f'merge props from {parent.path} to {dst.path}')
  649. self.merge_props(parent, False)
  650. class Fdt:
  651. """Provides simple access to a flat device tree blob using libfdts.
  652. Properties:
  653. fname: Filename of fdt
  654. _root: Root of device tree (a Node object)
  655. name: Helpful name for this Fdt for the user (useful when creating the
  656. DT from data rather than a file)
  657. """
  658. def __init__(self, fname):
  659. self._fname = fname
  660. self._cached_offsets = False
  661. self.phandle_to_node = {}
  662. self.name = ''
  663. if self._fname:
  664. self.name = self._fname
  665. self._fname = fdt_util.EnsureCompiled(self._fname)
  666. with open(self._fname, 'rb') as fd:
  667. self._fdt_obj = libfdt.Fdt(fd.read())
  668. @staticmethod
  669. def FromData(data, name=''):
  670. """Create a new Fdt object from the given data
  671. Args:
  672. data: Device-tree data blob
  673. name: Helpful name for this Fdt for the user
  674. Returns:
  675. Fdt object containing the data
  676. """
  677. fdt = Fdt(None)
  678. fdt._fdt_obj = libfdt.Fdt(bytes(data))
  679. fdt.name = name
  680. return fdt
  681. def LookupPhandle(self, phandle):
  682. """Look up a phandle
  683. Args:
  684. phandle: Phandle to look up (int)
  685. Returns:
  686. Node object the phandle points to
  687. """
  688. return self.phandle_to_node.get(phandle)
  689. def Scan(self, root='/'):
  690. """Scan a device tree, building up a tree of Node objects
  691. This fills in the self._root property
  692. Args:
  693. root: Ignored
  694. TODO(sjg@chromium.org): Implement the 'root' parameter
  695. """
  696. self.phandle_to_node = {}
  697. self._cached_offsets = True
  698. self._root = self.Node(self, None, 0, '/', '/')
  699. self._root.Scan()
  700. def GetRoot(self):
  701. """Get the root Node of the device tree
  702. Returns:
  703. The root Node object
  704. """
  705. return self._root
  706. def GetNode(self, path):
  707. """Look up a node from its path
  708. Args:
  709. path: Path to look up, e.g. '/microcode/update@0'
  710. Returns:
  711. Node object, or None if not found
  712. """
  713. node = self._root
  714. parts = path.split('/')
  715. if len(parts) < 2:
  716. return None
  717. if len(parts) == 2 and parts[1] == '':
  718. return node
  719. for part in parts[1:]:
  720. node = node.FindNode(part)
  721. if not node:
  722. return None
  723. return node
  724. def Flush(self):
  725. """Flush device tree changes back to the file
  726. If the device tree has changed in memory, write it back to the file.
  727. """
  728. with open(self._fname, 'wb') as fd:
  729. fd.write(self._fdt_obj.as_bytearray())
  730. def Sync(self, auto_resize=False):
  731. """Make sure any DT changes are written to the blob
  732. Args:
  733. auto_resize: Resize the device tree automatically if it does not
  734. have enough space for the update
  735. Raises:
  736. FdtException if auto_resize is False and there is not enough space
  737. """
  738. self.CheckCache()
  739. self._root.Sync(auto_resize)
  740. self.Refresh()
  741. def Pack(self):
  742. """Pack the device tree down to its minimum size
  743. When nodes and properties shrink or are deleted, wasted space can
  744. build up in the device tree binary.
  745. """
  746. CheckErr(self._fdt_obj.pack(), 'pack')
  747. self.Refresh()
  748. def GetContents(self):
  749. """Get the contents of the FDT
  750. Returns:
  751. The FDT contents as a string of bytes
  752. """
  753. return bytes(self._fdt_obj.as_bytearray())
  754. def GetFdtObj(self):
  755. """Get the contents of the FDT
  756. Returns:
  757. The FDT contents as a libfdt.Fdt object
  758. """
  759. return self._fdt_obj
  760. def GetProps(self, node):
  761. """Get all properties from a node.
  762. Args:
  763. node: Full path to node name to look in.
  764. Returns:
  765. A dictionary containing all the properties, indexed by node name.
  766. The entries are Prop objects.
  767. Raises:
  768. ValueError: if the node does not exist.
  769. """
  770. props_dict = {}
  771. poffset = self._fdt_obj.first_property_offset(node._offset,
  772. QUIET_NOTFOUND)
  773. while poffset >= 0:
  774. p = self._fdt_obj.get_property_by_offset(poffset)
  775. prop = Prop(node, poffset, p.name, p)
  776. props_dict[prop.name] = prop
  777. poffset = self._fdt_obj.next_property_offset(poffset,
  778. QUIET_NOTFOUND)
  779. return props_dict
  780. def Invalidate(self):
  781. """Mark our offset cache as invalid"""
  782. self._cached_offsets = False
  783. def CheckCache(self):
  784. """Refresh the offset cache if needed"""
  785. if self._cached_offsets:
  786. return
  787. self.Refresh()
  788. def Refresh(self):
  789. """Refresh the offset cache"""
  790. self._root.Refresh(0)
  791. self._cached_offsets = True
  792. def GetStructOffset(self, offset):
  793. """Get the file offset of a given struct offset
  794. Args:
  795. offset: Offset within the 'struct' region of the device tree
  796. Returns:
  797. Position of @offset within the device tree binary
  798. """
  799. return self._fdt_obj.off_dt_struct() + offset
  800. @classmethod
  801. def Node(self, fdt, parent, offset, name, path):
  802. """Create a new node
  803. This is used by Fdt.Scan() to create a new node using the correct
  804. class.
  805. Args:
  806. fdt: Fdt object
  807. parent: Parent node, or None if this is the root node
  808. offset: Offset of node
  809. name: Node name
  810. path: Full path to node
  811. """
  812. node = Node(fdt, parent, offset, name, path)
  813. return node
  814. def GetFilename(self):
  815. """Get the filename of the device tree
  816. Returns:
  817. String filename
  818. """
  819. return self._fname
  820. def FdtScan(fname):
  821. """Returns a new Fdt object"""
  822. dtb = Fdt(fname)
  823. dtb.Scan()
  824. return dtb