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