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