fdt.py 26 KB

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