pyfdt.py (12468:b4b8bdb9712a) pyfdt.py (13714:35636064b7a1)
1# -*- coding: utf-8 -*-
2"""
3Device Tree Blob Parser
4
5 Copyright 2014 Neil 'superna' Armstrong <superna9999@gmail.com>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19@author: Neil 'superna' Armstrong <superna9999@gmail.com>
20"""
21
1# -*- coding: utf-8 -*-
2"""
3Device Tree Blob Parser
4
5 Copyright 2014 Neil 'superna' Armstrong <superna9999@gmail.com>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19@author: Neil 'superna' Armstrong <superna9999@gmail.com>
20"""
21
22from __future__ import print_function
23from __future__ import absolute_import
24
22import string
23import os
24import json
25from copy import deepcopy, copy
26from struct import Struct, unpack, pack
27
28FDT_MAGIC = 0xd00dfeed
29FDT_BEGIN_NODE = 0x1
30FDT_END_NODE = 0x2
31FDT_PROP = 0x3
32FDT_NOP = 0x4
33FDT_END = 0x9
34
35INDENT = ' ' * 4
36
37FDT_MAX_VERSION = 17
38
39
40class FdtProperty(object):
41 """ Represents an empty property"""
42
43 @staticmethod
44 def __validate_dt_name(name):
45 """Checks the name validity"""
46 return not any([True for char in name
47 if char not in string.printable])
48
49 def __init__(self, name):
50 """Init with name"""
51 self.name = name
52 if not FdtProperty.__validate_dt_name(self.name):
53 raise Exception("Invalid name '%s'" % self.name)
54
55 def get_name(self):
56 """Get property name"""
57 return self.name
58
59 def __str__(self):
60 """String representation"""
61 return "Property(%s)" % self.name
62
63 def dts_represent(self, depth=0):
64 """Get dts string representation"""
65 return INDENT*depth + self.name + ';'
66
67 def dtb_represent(self, string_store, pos=0, version=17):
68 """Get blob representation"""
69 # print "%x:%s" % (pos, self)
70 strpos = string_store.find(self.name+'\0')
71 if strpos < 0:
72 strpos = len(string_store)
73 string_store += self.name+'\0'
74 pos += 12
75 return (pack('>III', FDT_PROP, 0, strpos),
76 string_store, pos)
77
78 def json_represent(self, depth=0):
79 """Ouput JSON"""
80 return '%s: null' % json.dumps(self.name)
81
82 def to_raw(self):
83 """Return RAW value representation"""
84 return ''
85
86 def __getitem__(self, value):
87 """Returns No Items"""
88 return None
89
90 def __ne__(self, node):
91 """Check property inequality
92 """
93 return not self.__eq__(node)
94
95 def __eq__(self, node):
96 """Check node equality
97 check properties are the same (same values)
98 """
99 if not isinstance(node, FdtProperty):
100 raise Exception("Invalid object type")
101 if self.name != node.get_name():
102 return False
103 return True
104
105 @staticmethod
106 def __check_prop_strings(value):
107 """Check property string validity
108 Python version of util_is_printable_string from dtc
109 """
110 pos = 0
111 posi = 0
112 end = len(value)
113
114 if not len(value):
115 return None
116
117 #Needed for python 3 support: If a bytes object is passed,
118 #decode it with the ascii codec. If the decoding fails, assume
119 #it was not a string object.
120 try:
121 value = value.decode('ascii')
122 except ValueError:
123 return None
124
125 #Test both against string 0 and int 0 because of
126 # python2/3 compatibility
127 if value[-1] != '\0':
128 return None
129
130 while pos < end:
131 posi = pos
132 while pos < end and value[pos] != '\0' \
133 and value[pos] in string.printable \
134 and value[pos] not in ('\r', '\n'):
135 pos += 1
136
137 if value[pos] != '\0' or pos == posi:
138 return None
139 pos += 1
140
141 return True
142
143 @staticmethod
144 def new_raw_property(name, raw_value):
145 """Instantiate property with raw value type"""
146 if FdtProperty.__check_prop_strings(raw_value):
147 return FdtPropertyStrings.init_raw(name, raw_value)
148 elif len(raw_value) and len(raw_value) % 4 == 0:
149 return FdtPropertyWords.init_raw(name, raw_value)
150 elif len(raw_value) and len(raw_value):
151 return FdtPropertyBytes.init_raw(name, raw_value)
152 else:
153 return FdtProperty(name)
154
155
156class FdtPropertyStrings(FdtProperty):
157 """Property with strings as value"""
158
159 @classmethod
160 def __extract_prop_strings(cls, value):
161 """Extract strings from raw_value"""
162 return [st for st in \
163 value.decode('ascii').split('\0') if len(st)]
164
165 def __init__(self, name, strings):
166 """Init with strings"""
167 FdtProperty.__init__(self, name)
168 if not strings:
169 raise Exception("Invalid strings")
170 for stri in strings:
171 if len(stri) == 0:
172 raise Exception("Invalid strings")
173 if any([True for char in stri
174 if char not in string.printable
175 or char in ('\r', '\n')]):
176 raise Exception("Invalid chars in strings")
177 self.strings = strings
178
179 @classmethod
180 def init_raw(cls, name, raw_value):
181 """Init from raw"""
182 return cls(name, cls.__extract_prop_strings(raw_value))
183
184 def dts_represent(self, depth=0):
185 """Get dts string representation"""
186 return INDENT*depth + self.name + ' = "' + \
187 '", "'.join(self.strings) + '";'
188
189 def dtb_represent(self, string_store, pos=0, version=17):
190 """Get blob representation"""
191 # print "%x:%s" % (pos, self)
192 blob = pack('')
193 for chars in self.strings:
194 blob += chars.encode('ascii') + pack('b', 0)
195 blob_len = len(blob)
196 if version < 16 and (pos+12) % 8 != 0:
197 blob = pack('b', 0) * (8-((pos+12) % 8)) + blob
198 if blob_len % 4:
199 blob += pack('b', 0) * (4-(blob_len % 4))
200 strpos = string_store.find(self.name+'\0')
201 if strpos < 0:
202 strpos = len(string_store)
203 string_store += self.name+'\0'
204 blob = pack('>III', FDT_PROP, blob_len, strpos) + blob
205 pos += len(blob)
206 return (blob, string_store, pos)
207
208 def json_represent(self, depth=0):
209 """Ouput JSON"""
210 result = '%s: ["strings", ' % json.dumps(self.name)
211 result += ', '.join([json.dumps(stri) for stri in self.strings])
212 result += ']'
213 return result
214
215 def to_raw(self):
216 """Return RAW value representation"""
217 return ''.join([chars+'\0' for chars in self.strings])
218
219 def __str__(self):
220 """String representation"""
221 return "Property(%s,Strings:%s)" % (self.name, self.strings)
222
223 def __getitem__(self, index):
224 """Get strings, returns a string"""
225 return self.strings[index]
226
227 def __len__(self):
228 """Get strings count"""
229 return len(self.strings)
230
231 def __eq__(self, node):
232 """Check node equality
233 check properties are the same (same values)
234 """
235 if not FdtProperty.__eq__(self, node):
236 return False
237 if self.__len__() != len(node):
238 return False
239 for index in range(self.__len__()):
240 if self.strings[index] != node[index]:
241 return False
242 return True
243
244class FdtPropertyWords(FdtProperty):
245 """Property with words as value"""
246
247 def __init__(self, name, words):
248 """Init with words"""
249 FdtProperty.__init__(self, name)
250 for word in words:
251 if not 0 <= word <= 4294967295:
252 raise Exception(("Invalid word value %d, requires " +
253 "0 <= number <= 4294967295") % word)
254 if not len(words):
255 raise Exception("Invalid Words")
256 self.words = words
257
258 @classmethod
259 def init_raw(cls, name, raw_value):
260 """Init from raw"""
261 if len(raw_value) % 4 == 0:
262 words = [unpack(">I", raw_value[i:i+4])[0]
263 for i in range(0, len(raw_value), 4)]
264 return cls(name, words)
265 else:
266 raise Exception("Invalid raw Words")
267
268 def dts_represent(self, depth=0):
269 """Get dts string representation"""
270 return INDENT*depth + self.name + ' = <' + \
271 ' '.join(["0x%08x" % word for word in self.words]) + ">;"
272
273 def dtb_represent(self, string_store, pos=0, version=17):
274 """Get blob representation"""
275 # # print "%x:%s" % (pos, self)
276 strpos = string_store.find(self.name+'\0')
277 if strpos < 0:
278 strpos = len(string_store)
279 string_store += self.name+'\0'
280 blob = pack('>III', FDT_PROP, len(self.words)*4, strpos) + \
281 pack('').join([pack('>I', word) for word in self.words])
282 pos += len(blob)
283 return (blob, string_store, pos)
284
285 def json_represent(self, depth=0):
286 """Ouput JSON"""
287 result = '%s: ["words", "' % json.dumps(self.name)
288 result += '", "'.join(["0x%08x" % word for word in self.words])
289 result += '"]'
290 return result
291
292 def to_raw(self):
293 """Return RAW value representation"""
294 return ''.join([pack('>I', word) for word in self.words])
295
296 def __str__(self):
297 """String representation"""
298 return "Property(%s,Words:%s)" % (self.name, self.words)
299
300 def __getitem__(self, index):
301 """Get words, returns a word integer"""
302 return self.words[index]
303
304 def __len__(self):
305 """Get words count"""
306 return len(self.words)
307
308 def __eq__(self, node):
309 """Check node equality
310 check properties are the same (same values)
311 """
312 if not FdtProperty.__eq__(self, node):
313 return False
314 if self.__len__() != len(node):
315 return False
316 for index in range(self.__len__()):
317 if self.words[index] != node[index]:
318 return False
319 return True
320
321
322class FdtPropertyBytes(FdtProperty):
323 """Property with signed bytes as value"""
324
325 def __init__(self, name, bytez):
326 """Init with bytes"""
327 FdtProperty.__init__(self, name)
328 for byte in bytez:
329 if not -128 <= byte <= 127:
330 raise Exception(("Invalid value for byte %d, " +
331 "requires -128 <= number <= 127") % byte)
332 if not bytez:
333 raise Exception("Invalid Bytes")
334 self.bytes = bytez
335
336 @classmethod
337 def init_raw(cls, name, raw_value):
338 """Init from raw"""
339 return cls(name, unpack('b' * len(raw_value), raw_value))
340
341 def dts_represent(self, depth=0):
342 """Get dts string representation"""
343 return INDENT*depth + self.name + ' = [' + \
344 ' '.join(["%02x" % (byte & int('ffffffff',16))
345 for byte in self.bytes]) + "];"
346
347 def dtb_represent(self, string_store, pos=0, version=17):
348 """Get blob representation"""
349 # print "%x:%s" % (pos, self)
350 strpos = string_store.find(self.name+'\0')
351 if strpos < 0:
352 strpos = len(string_store)
353 string_store += self.name+'\0'
354 blob = pack('>III', FDT_PROP, len(self.bytes), strpos)
355 blob += pack('').join([pack('>b', byte) for byte in self.bytes])
356 if len(blob) % 4:
357 blob += pack('b', 0) * (4-(len(blob) % 4))
358 pos += len(blob)
359 return (blob, string_store, pos)
360
361 def json_represent(self, depth=0):
362 """Ouput JSON"""
363 result = '%s: ["bytes", "' % json.dumps(self.name)
364 result += '", "'.join(["%02x" % byte
365 for byte in self.bytes])
366 result += '"]'
367 return result
368
369 def to_raw(self):
370 """Return RAW value representation"""
371 return ''.join([pack('>b', byte) for byte in self.bytes])
372
373 def __str__(self):
374 """String representation"""
375 return "Property(%s,Bytes:%s)" % (self.name, self.bytes)
376
377 def __getitem__(self, index):
378 """Get bytes, returns a byte"""
379 return self.bytes[index]
380
381 def __len__(self):
382 """Get strings count"""
383 return len(self.bytes)
384
385 def __eq__(self, node):
386 """Check node equality
387 check properties are the same (same values)
388 """
389 if not FdtProperty.__eq__(self, node):
390 return False
391 if self.__len__() != len(node):
392 return False
393 for index in range(self.__len__()):
394 if self.bytes[index] != node[index]:
395 return False
396 return True
397
398
399class FdtNop(object): # pylint: disable-msg=R0903
400 """Nop child representation"""
401
402 def __init__(self):
403 """Init with nothing"""
404
405 def get_name(self): # pylint: disable-msg=R0201
406 """Return name"""
407 return None
408
409 def __str__(self):
410 """String representation"""
411 return ''
412
413 def dts_represent(self, depth=0): # pylint: disable-msg=R0201
414 """Get dts string representation"""
415 return INDENT*depth+'// [NOP]'
416
417 def dtb_represent(self, string_store, pos=0, version=17):
418 """Get blob representation"""
419 # print "%x:%s" % (pos, self)
420 pos += 4
421 return (pack('>I', FDT_NOP), string_store, pos)
422
423
424class FdtNode(object):
425 """Node representation"""
426
427 @staticmethod
428 def __validate_dt_name(name):
429 """Checks the name validity"""
430 return not any([True for char in name
431 if char not in string.printable])
432
433 def __init__(self, name):
434 """Init node with name"""
435 self.name = name
436 self.subdata = []
437 self.parent = None
438 if not FdtNode.__validate_dt_name(self.name):
439 raise Exception("Invalid name '%s'" % self.name)
440
441 def get_name(self):
442 """Get property name"""
443 return self.name
444
445 def __check_name_duplicate(self, name):
446 """Checks if name is not in a subnode"""
447 for data in self.subdata:
448 if not isinstance(data, FdtNop) \
449 and data.get_name() == name:
450 return True
451 return False
452
453 def add_subnode(self, node):
454 """Add child, deprecated use append()"""
455 self.append(node)
456
457 def add_raw_attribute(self, name, raw_value):
458 """Construct a raw attribute and add to child"""
459 self.append(FdtProperty.new_raw_property(name, raw_value))
460
461 def set_parent_node(self, node):
462 """Set parent node, None and FdtNode accepted"""
463 if node is not None and \
464 not isinstance(node, FdtNode):
465 raise Exception("Invalid object type")
466 self.parent = node
467
468 def get_parent_node(self):
469 """Get parent node"""
470 return self.parent
471
472 def __str__(self):
473 """String representation"""
474 return "Node(%s)" % self.name
475
476 def dts_represent(self, depth=0):
477 """Get dts string representation"""
478 result = ('\n').join([sub.dts_represent(depth+1)
479 for sub in self.subdata])
480 if len(result) > 0:
481 result += '\n'
482 return INDENT*depth + self.name + ' {\n' + \
483 result + INDENT*depth + "};"
484
485 def dtb_represent(self, strings_store, pos=0, version=17):
486 """Get blob representation
487 Pass string storage as strings_store, pos for current node start
488 and version as current dtb version
489 """
490 # print "%x:%s" % (pos, self)
491 strings = strings_store
492 if self.get_name() == '/':
493 blob = pack('>II', FDT_BEGIN_NODE, 0)
494 else:
495 blob = pack('>I', FDT_BEGIN_NODE)
496 blob += self.get_name().encode('ascii') + pack('b', 0)
497 if len(blob) % 4:
498 blob += pack('b', 0) * (4-(len(blob) % 4))
499 pos += len(blob)
500 for sub in self.subdata:
501 (data, strings, pos) = sub.dtb_represent(strings, pos, version)
502 blob += data
503 pos += 4
504 blob += pack('>I', FDT_END_NODE)
505 return (blob, strings, pos)
506
507 def json_represent(self, depth=0):
508 """Get dts string representation"""
509 result = (',\n'+ \
510 INDENT*(depth+1)).join([sub.json_represent(depth+1)
511 for sub in self.subdata
512 if not isinstance(sub, FdtNop)])
513 if len(result) > 0:
514 result = INDENT + result + '\n'+INDENT*depth
515 if self.get_name() == '/':
516 return "{\n" + INDENT*(depth) + result + "}"
517 else:
518 return json.dumps(self.name) + ': {\n' + \
519 INDENT*(depth) + result + "}"
520
521 def __getitem__(self, index):
522 """Get subnodes, returns either a Node, a Property or a Nop"""
523 return self.subdata[index]
524
525 def __setitem__(self, index, subnode):
526 """Set node at index, replacing previous subnode,
527 must not be a duplicate name
528 """
529 if self.subdata[index].get_name() != subnode.get_name() and \
530 self.__check_name_duplicate(subnode.get_name()):
531 raise Exception("%s : %s subnode already exists" % \
532 (self, subnode))
533 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
534 raise Exception("Invalid object type")
535 self.subdata[index] = subnode
536
537 def __len__(self):
538 """Get strings count"""
539 return len(self.subdata)
540
541 def __ne__(self, node):
542 """Check node inequality
543 i.e. is subnodes are the same, in either order
544 and properties are the same (same values)
545 The FdtNop is excluded from the check
546 """
547 return not self.__eq__(node)
548
549 def __eq__(self, node):
550 """Check node equality
551 i.e. is subnodes are the same, in either order
552 and properties are the same (same values)
553 The FdtNop is excluded from the check
554 """
555 if not isinstance(node, FdtNode):
556 raise Exception("Invalid object type")
557 if self.name != node.get_name():
558 return False
559 curnames = set([subnode.get_name() for subnode in self.subdata
560 if not isinstance(subnode, FdtNop)])
561 cmpnames = set([subnode.get_name() for subnode in node
562 if not isinstance(subnode, FdtNop)])
563 if curnames != cmpnames:
564 return False
565 for subnode in [subnode for subnode in self.subdata
566 if not isinstance(subnode, FdtNop)]:
567 index = node.index(subnode.get_name())
568 if subnode != node[index]:
569 return False
570 return True
571
572 def append(self, subnode):
573 """Append subnode, same as add_subnode"""
574 if self.__check_name_duplicate(subnode.get_name()):
575 raise Exception("%s : %s subnode already exists" % \
576 (self, subnode))
577 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
578 raise Exception("Invalid object type")
579 self.subdata.append(subnode)
580
581 def pop(self, index=-1):
582 """Remove and returns subnode at index, default the last"""
583 return self.subdata.pop(index)
584
585 def insert(self, index, subnode):
586 """Insert subnode before index, must not be a duplicate name"""
587 if self.__check_name_duplicate(subnode.get_name()):
588 raise Exception("%s : %s subnode already exists" % \
589 (self, subnode))
590 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
591 raise Exception("Invalid object type")
592 self.subdata.insert(index, subnode)
593
594 def _find(self, name):
595 """Find name in subnodes"""
596 for i in range(0, len(self.subdata)):
597 if not isinstance(self.subdata[i], FdtNop) and \
598 name == self.subdata[i].get_name():
599 return i
600 return None
601
602 def remove(self, name):
603 """Remove subnode with the name
604 Raises ValueError is not present
605 """
606 index = self._find(name)
607 if index is None:
608 raise ValueError("Not present")
609 return self.subdata.pop(index)
610
611 def index(self, name):
612 """Returns position of subnode with the name
613 Raises ValueError is not present
614 """
615 index = self._find(name)
616 if index is None:
617 raise ValueError("Not present")
618 return index
619
620 def merge(self, node):
621 """Merge two nodes and subnodes
622 Replace current properties with the given properties
623 """
624 if not isinstance(node, FdtNode):
625 raise Exception("Can only merge with a FdtNode")
626 for subnode in [obj for obj in node
627 if isinstance(obj, (FdtNode, FdtProperty))]:
628 index = self._find(subnode.get_name())
629 if index is None:
630 dup = deepcopy(subnode)
631 if isinstance(subnode, FdtNode):
632 dup.set_parent_node(self)
633 self.append(dup)
634 elif isinstance(subnode, FdtNode):
635 self.subdata[index].merge(subnode)
636 else:
637 self.subdata[index] = copy(subnode)
638
639 def walk(self):
640 """Walk into subnodes and yield paths and objects
641 Returns set with (path string, node object)
642 """
643 node = self
644 start = 0
645 hist = []
646 curpath = []
647
648 while True:
649 for index in range(start, len(node)):
650 if isinstance(node[index], (FdtNode, FdtProperty)):
651 yield ('/' + '/'.join(curpath+[node[index].get_name()]),
652 node[index])
653 if isinstance(node[index], FdtNode):
654 if len(node[index]):
655 hist.append((node, index+1))
656 curpath.append(node[index].get_name())
657 node = node[index]
658 start = 0
659 index = -1
660 break
661 if index >= 0:
662 if len(hist):
663 (node, start) = hist.pop()
664 curpath.pop()
665 else:
666 break
667
668
669class Fdt(object):
670 """Flattened Device Tree representation"""
671
672 def __init__(self, version=17, last_comp_version=16, boot_cpuid_phys=0):
673 """Init FDT object with version and boot values"""
674 self.header = {'magic': FDT_MAGIC,
675 'totalsize': 0,
676 'off_dt_struct': 0,
677 'off_dt_strings': 0,
678 'off_mem_rsvmap': 0,
679 'version': version,
680 'last_comp_version': last_comp_version,
681 'boot_cpuid_phys': boot_cpuid_phys,
682 'size_dt_strings': 0,
683 'size_dt_struct': 0}
684 self.rootnode = None
685 self.prenops = None
686 self.postnops = None
687 self.reserve_entries = None
688
689 def add_rootnode(self, rootnode, prenops=None, postnops=None):
690 """Add root node"""
691 self.rootnode = rootnode
692 self.prenops = prenops
693 self.postnops = postnops
694
695 def get_rootnode(self):
696 """Get root node"""
697 return self.rootnode
698
699 def add_reserve_entries(self, reserve_entries):
700 """Add reserved entries as list of dict with
701 'address' and 'size' keys"""
702 self.reserve_entries = reserve_entries
703
704 def to_dts(self):
705 """Export to DTS representation in string format"""
706 result = "/dts-v1/;\n"
707 result += "// version:\t\t%d\n" % self.header['version']
708 result += "// last_comp_version:\t%d\n" % \
709 self.header['last_comp_version']
710 if self.header['version'] >= 2:
711 result += "// boot_cpuid_phys:\t0x%x\n" % \
712 self.header['boot_cpuid_phys']
713 result += '\n'
714 if self.reserve_entries is not None:
715 for entry in self.reserve_entries:
716 result += "/memreserve/ "
717 if entry['address']:
718 result += "%#x " % entry['address']
719 else:
720 result += "0 "
721 if entry['size']:
722 result += "%#x" % entry['size']
723 else:
724 result += "0"
725 result += ";\n"
726 if self.prenops:
727 result += '\n'.join([nop.dts_represent() for nop in self.prenops])
728 result += '\n'
729 if self.rootnode is not None:
730 result += self.rootnode.dts_represent()
731 if self.postnops:
732 result += '\n'
733 result += '\n'.join([nop.dts_represent() for nop in self.postnops])
734 return result
735
736 def to_dtb(self):
737 """Export to Blob format"""
738 if self.rootnode is None:
739 return None
740 blob_reserve_entries = pack('')
741 if self.reserve_entries is not None:
742 for entry in self.reserve_entries:
743 blob_reserve_entries += pack('>QQ',
744 entry['address'],
745 entry['size'])
746 blob_reserve_entries += pack('>QQ', 0, 0)
747 header_size = 7 * 4
748 if self.header['version'] >= 2:
749 header_size += 4
750 if self.header['version'] >= 3:
751 header_size += 4
752 if self.header['version'] >= 17:
753 header_size += 4
754 header_adjust = pack('')
755 if header_size % 8 != 0:
756 header_adjust = pack('b', 0) * (8 - (header_size % 8))
757 header_size += len(header_adjust)
758 dt_start = header_size + len(blob_reserve_entries)
759 # print "dt_start %d" % dt_start
760 (blob_dt, blob_strings, dt_pos) = \
761 self.rootnode.dtb_represent('', dt_start, self.header['version'])
762 if self.prenops is not None:
763 blob_dt = pack('').join([nop.dtb_represent('')[0]
764 for nop in self.prenops])\
765 + blob_dt
766 if self.postnops is not None:
767 blob_dt += pack('').join([nop.dtb_represent('')[0]
768 for nop in self.postnops])
769 blob_dt += pack('>I', FDT_END)
770 self.header['size_dt_strings'] = len(blob_strings)
771 self.header['size_dt_struct'] = len(blob_dt)
772 self.header['off_mem_rsvmap'] = header_size
773 self.header['off_dt_struct'] = dt_start
774 self.header['off_dt_strings'] = dt_start + len(blob_dt)
775 self.header['totalsize'] = dt_start + len(blob_dt) + len(blob_strings)
776 blob_header = pack('>IIIIIII', self.header['magic'],
777 self.header['totalsize'],
778 self.header['off_dt_struct'],
779 self.header['off_dt_strings'],
780 self.header['off_mem_rsvmap'],
781 self.header['version'],
782 self.header['last_comp_version'])
783 if self.header['version'] >= 2:
784 blob_header += pack('>I', self.header['boot_cpuid_phys'])
785 if self.header['version'] >= 3:
786 blob_header += pack('>I', self.header['size_dt_strings'])
787 if self.header['version'] >= 17:
788 blob_header += pack('>I', self.header['size_dt_struct'])
789 return blob_header + header_adjust + blob_reserve_entries + \
790 blob_dt + blob_strings.encode('ascii')
791
792 def to_json(self):
793 """Ouput JSON"""
794 if self.rootnode is None:
795 return None
796 return self.rootnode.json_represent()
797
798 def resolve_path(self, path):
799 """Resolve path like /memory/reg and return either a FdtNode,
800 a FdtProperty or None"""
801 if self.rootnode is None:
802 return None
803 if not path.startswith('/'):
804 return None
805 if len(path) > 1 and path.endswith('/'):
806 path = path[:-1]
807 if path == '/':
808 return self.rootnode
809 curnode = self.rootnode
810 for subpath in path[1:].split('/'):
811 found = None
812 if not isinstance(curnode, FdtNode):
813 return None
814 for node in curnode:
815 if subpath == node.get_name():
816 found = node
817 break
818 if found is None:
819 return None
820 curnode = found
821 return curnode
822
823def _add_json_to_fdtnode(node, subjson):
824 """Populate FdtNode with JSON dict items"""
825 for (key, value) in subjson.items():
826 if isinstance(value, dict):
827 subnode = FdtNode(key)
828 subnode.set_parent_node(node)
829 node.append(subnode)
830 _add_json_to_fdtnode(subnode, value)
831 elif isinstance(value, list):
832 if len(value) < 2:
833 raise Exception("Invalid list for %s" % key)
834 if value[0] == "words":
835 words = [int(word, 16) for word in value[1:]]
836 node.append(FdtPropertyWords(key, words))
837 elif value[0] == "bytes":
838 bytez = [int(byte, 16) for byte in value[1:]]
839 node.append(FdtPropertyBytes(key, bytez))
840 elif value[0] == "strings":
841 node.append(FdtPropertyStrings(key, \
842 [s for s in value[1:]]))
843 else:
844 raise Exception("Invalid list for %s" % key)
845 elif value is None:
846 node.append(FdtProperty(key))
847 else:
848 raise Exception("Invalid value for %s" % key)
849
850def FdtJsonParse(buf):
851 """Import FDT from JSON representation, see JSONDeviceTree.md for
852 structure and encoding
853 Returns an Fdt object
854 """
855 tree = json.loads(buf)
856
857 root = FdtNode('/')
858
859 _add_json_to_fdtnode(root, tree)
860
861 fdt = Fdt()
862 fdt.add_rootnode(root)
863 return fdt
864
865def FdtFsParse(path):
866 """Parse device tree filesystem and return a Fdt instance
867 Should be /proc/device-tree on a device, or the fusemount.py
868 mount point.
869 """
870 root = FdtNode("/")
871
872 if path.endswith('/'):
873 path = path[:-1]
874
875 nodes = {path: root}
876
877 for subpath, subdirs, files in os.walk(path):
878 if subpath not in nodes.keys():
879 raise Exception("os.walk error")
880 cur = nodes[subpath]
881 for f in files:
882 with open(subpath+'/'+f, 'rb') as content_file:
883 content = content_file.read()
884 prop = FdtProperty.new_raw_property(f, content)
885 cur.add_subnode(prop)
886 for subdir in subdirs:
887 subnode = FdtNode(subdir)
888 cur.add_subnode(subnode)
889 subnode.set_parent_node(cur)
890 nodes[subpath+'/'+subdir] = subnode
891
892 fdt = Fdt()
893 fdt.add_rootnode(root)
894 return fdt
895
896class FdtBlobParse(object): # pylint: disable-msg=R0903
897 """Parse from file input"""
898
899 __fdt_header_format = ">IIIIIII"
900 __fdt_header_names = ('magic', 'totalsize', 'off_dt_struct',
901 'off_dt_strings', 'off_mem_rsvmap', 'version',
902 'last_comp_version')
903
904 __fdt_reserve_entry_format = ">QQ"
905 __fdt_reserve_entry_names = ('address', 'size')
906
907 __fdt_dt_cell_format = ">I"
908 __fdt_dt_prop_format = ">II"
909 __fdt_dt_tag_name = {FDT_BEGIN_NODE: 'node_begin',
910 FDT_END_NODE: 'node_end',
911 FDT_PROP: 'prop',
912 FDT_NOP: 'nop',
913 FDT_END: 'end'}
914
915 def __extract_fdt_header(self):
916 """Extract DTB header"""
917 header = Struct(self.__fdt_header_format)
918 header_entry = Struct(">I")
919 data = self.infile.read(header.size)
920 result = dict(zip(self.__fdt_header_names, header.unpack_from(data)))
921 if result['version'] >= 2:
922 data = self.infile.read(header_entry.size)
923 result['boot_cpuid_phys'] = header_entry.unpack_from(data)[0]
924 if result['version'] >= 3:
925 data = self.infile.read(header_entry.size)
926 result['size_dt_strings'] = header_entry.unpack_from(data)[0]
927 if result['version'] >= 17:
928 data = self.infile.read(header_entry.size)
929 result['size_dt_struct'] = header_entry.unpack_from(data)[0]
930 return result
931
932 def __extract_fdt_reserve_entries(self):
933 """Extract reserved memory entries"""
934 header = Struct(self.__fdt_reserve_entry_format)
935 entries = []
936 self.infile.seek(self.fdt_header['off_mem_rsvmap'])
937 while True:
938 data = self.infile.read(header.size)
939 result = dict(zip(self.__fdt_reserve_entry_names,
940 header.unpack_from(data)))
941 if result['address'] == 0 and result['size'] == 0:
942 return entries
943 entries.append(result)
944
945 def __extract_fdt_nodename(self):
946 """Extract node name"""
947 data = ''
948 pos = self.infile.tell()
949 while True:
950 byte = self.infile.read(1)
951 if ord(byte) == 0:
952 break
953 data += byte.decode('ascii')
954 align_pos = pos + len(data) + 1
955 align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1))
956 self.infile.seek(align_pos)
957 return data
958
959 def __extract_fdt_string(self, prop_string_pos):
960 """Extract string from string pool"""
961 data = ''
962 pos = self.infile.tell()
963 self.infile.seek(self.fdt_header['off_dt_strings']+prop_string_pos)
964 while True:
965 byte = self.infile.read(1)
966 if ord(byte) == 0:
967 break
968 data += byte.decode('ascii')
969 self.infile.seek(pos)
970 return data
971
972 def __extract_fdt_prop(self):
973 """Extract property"""
974 prop = Struct(self.__fdt_dt_prop_format)
975 pos = self.infile.tell()
976 data = self.infile.read(prop.size)
977 (prop_size, prop_string_pos,) = prop.unpack_from(data)
978
979 prop_start = pos + prop.size
980 if self.fdt_header['version'] < 16 and prop_size >= 8:
981 prop_start = (((prop_start) + ((8) - 1)) & ~((8) - 1))
982
983 self.infile.seek(prop_start)
984 value = self.infile.read(prop_size)
985
986 align_pos = self.infile.tell()
987 align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1))
988 self.infile.seek(align_pos)
989
990 return (self.__extract_fdt_string(prop_string_pos), value)
991
992 def __extract_fdt_dt(self):
993 """Extract tags"""
994 cell = Struct(self.__fdt_dt_cell_format)
995 tags = []
996 self.infile.seek(self.fdt_header['off_dt_struct'])
997 while True:
998 data = self.infile.read(cell.size)
999 if len(data) < cell.size:
1000 break
1001 tag, = cell.unpack_from(data)
1002 # print "*** %s" % self.__fdt_dt_tag_name.get(tag, '')
1003 if self.__fdt_dt_tag_name.get(tag, '') in 'node_begin':
1004 name = self.__extract_fdt_nodename()
1005 if len(name) == 0:
1006 name = '/'
1007 tags.append((tag, name))
1008 elif self.__fdt_dt_tag_name.get(tag, '') in ('node_end', 'nop'):
1009 tags.append((tag, ''))
1010 elif self.__fdt_dt_tag_name.get(tag, '') in 'end':
1011 tags.append((tag, ''))
1012 break
1013 elif self.__fdt_dt_tag_name.get(tag, '') in 'prop':
1014 propdata = self.__extract_fdt_prop()
1015 tags.append((tag, propdata))
1016 else:
1017 print("Unknown Tag %d" % tag)
1018 return tags
1019
1020 def __init__(self, infile):
1021 """Init with file input"""
1022 self.infile = infile
1023 self.fdt_header = self.__extract_fdt_header()
1024 if self.fdt_header['magic'] != FDT_MAGIC:
1025 raise Exception('Invalid Magic')
1026 if self.fdt_header['version'] > FDT_MAX_VERSION:
1027 raise Exception('Invalid Version %d' % self.fdt_header['version'])
1028 if self.fdt_header['last_comp_version'] > FDT_MAX_VERSION-1:
1029 raise Exception('Invalid last compatible Version %d' %
1030 self.fdt_header['last_comp_version'])
1031 self.fdt_reserve_entries = self.__extract_fdt_reserve_entries()
1032 self.fdt_dt_tags = self.__extract_fdt_dt()
1033
1034 def __to_nodes(self):
1035 """Represent fdt as Node and properties structure
1036 Returns a set with the pre-node Nops, the Root Node,
1037 and the post-node Nops.
1038 """
1039 prenops = []
1040 postnops = []
1041 rootnode = None
1042 curnode = None
1043 for tag in self.fdt_dt_tags:
1044 if self.__fdt_dt_tag_name.get(tag[0], '') in 'node_begin':
1045 newnode = FdtNode(tag[1])
1046 if rootnode is None:
1047 rootnode = newnode
1048 if curnode is not None:
1049 curnode.add_subnode(newnode)
1050 newnode.set_parent_node(curnode)
1051 curnode = newnode
1052 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'node_end':
1053 if curnode is not None:
1054 curnode = curnode.get_parent_node()
1055 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'nop':
1056 if curnode is not None:
1057 curnode.add_subnode(FdtNop())
1058 elif rootnode is not None:
1059 postnops.append(FdtNop())
1060 else:
1061 prenops.append(FdtNop())
1062 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'prop':
1063 if curnode is not None:
1064 curnode.add_raw_attribute(tag[1][0], tag[1][1])
1065 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'end':
1066 continue
1067 return (prenops, rootnode, postnops)
1068
1069 def to_fdt(self):
1070 """Create a fdt object
1071 Returns a Fdt object
1072 """
1073 if self.fdt_header['version'] >= 2:
1074 boot_cpuid_phys = self.fdt_header['boot_cpuid_phys']
1075 else:
1076 boot_cpuid_phys = 0
1077 fdt = Fdt(version=self.fdt_header['version'],
1078 last_comp_version=self.fdt_header['last_comp_version'],
1079 boot_cpuid_phys=boot_cpuid_phys)
1080 (prenops, rootnode, postnops) = self.__to_nodes()
1081 fdt.add_rootnode(rootnode, prenops=prenops, postnops=postnops)
1082 fdt.add_reserve_entries(self.fdt_reserve_entries)
1083 return fdt
25import string
26import os
27import json
28from copy import deepcopy, copy
29from struct import Struct, unpack, pack
30
31FDT_MAGIC = 0xd00dfeed
32FDT_BEGIN_NODE = 0x1
33FDT_END_NODE = 0x2
34FDT_PROP = 0x3
35FDT_NOP = 0x4
36FDT_END = 0x9
37
38INDENT = ' ' * 4
39
40FDT_MAX_VERSION = 17
41
42
43class FdtProperty(object):
44 """ Represents an empty property"""
45
46 @staticmethod
47 def __validate_dt_name(name):
48 """Checks the name validity"""
49 return not any([True for char in name
50 if char not in string.printable])
51
52 def __init__(self, name):
53 """Init with name"""
54 self.name = name
55 if not FdtProperty.__validate_dt_name(self.name):
56 raise Exception("Invalid name '%s'" % self.name)
57
58 def get_name(self):
59 """Get property name"""
60 return self.name
61
62 def __str__(self):
63 """String representation"""
64 return "Property(%s)" % self.name
65
66 def dts_represent(self, depth=0):
67 """Get dts string representation"""
68 return INDENT*depth + self.name + ';'
69
70 def dtb_represent(self, string_store, pos=0, version=17):
71 """Get blob representation"""
72 # print "%x:%s" % (pos, self)
73 strpos = string_store.find(self.name+'\0')
74 if strpos < 0:
75 strpos = len(string_store)
76 string_store += self.name+'\0'
77 pos += 12
78 return (pack('>III', FDT_PROP, 0, strpos),
79 string_store, pos)
80
81 def json_represent(self, depth=0):
82 """Ouput JSON"""
83 return '%s: null' % json.dumps(self.name)
84
85 def to_raw(self):
86 """Return RAW value representation"""
87 return ''
88
89 def __getitem__(self, value):
90 """Returns No Items"""
91 return None
92
93 def __ne__(self, node):
94 """Check property inequality
95 """
96 return not self.__eq__(node)
97
98 def __eq__(self, node):
99 """Check node equality
100 check properties are the same (same values)
101 """
102 if not isinstance(node, FdtProperty):
103 raise Exception("Invalid object type")
104 if self.name != node.get_name():
105 return False
106 return True
107
108 @staticmethod
109 def __check_prop_strings(value):
110 """Check property string validity
111 Python version of util_is_printable_string from dtc
112 """
113 pos = 0
114 posi = 0
115 end = len(value)
116
117 if not len(value):
118 return None
119
120 #Needed for python 3 support: If a bytes object is passed,
121 #decode it with the ascii codec. If the decoding fails, assume
122 #it was not a string object.
123 try:
124 value = value.decode('ascii')
125 except ValueError:
126 return None
127
128 #Test both against string 0 and int 0 because of
129 # python2/3 compatibility
130 if value[-1] != '\0':
131 return None
132
133 while pos < end:
134 posi = pos
135 while pos < end and value[pos] != '\0' \
136 and value[pos] in string.printable \
137 and value[pos] not in ('\r', '\n'):
138 pos += 1
139
140 if value[pos] != '\0' or pos == posi:
141 return None
142 pos += 1
143
144 return True
145
146 @staticmethod
147 def new_raw_property(name, raw_value):
148 """Instantiate property with raw value type"""
149 if FdtProperty.__check_prop_strings(raw_value):
150 return FdtPropertyStrings.init_raw(name, raw_value)
151 elif len(raw_value) and len(raw_value) % 4 == 0:
152 return FdtPropertyWords.init_raw(name, raw_value)
153 elif len(raw_value) and len(raw_value):
154 return FdtPropertyBytes.init_raw(name, raw_value)
155 else:
156 return FdtProperty(name)
157
158
159class FdtPropertyStrings(FdtProperty):
160 """Property with strings as value"""
161
162 @classmethod
163 def __extract_prop_strings(cls, value):
164 """Extract strings from raw_value"""
165 return [st for st in \
166 value.decode('ascii').split('\0') if len(st)]
167
168 def __init__(self, name, strings):
169 """Init with strings"""
170 FdtProperty.__init__(self, name)
171 if not strings:
172 raise Exception("Invalid strings")
173 for stri in strings:
174 if len(stri) == 0:
175 raise Exception("Invalid strings")
176 if any([True for char in stri
177 if char not in string.printable
178 or char in ('\r', '\n')]):
179 raise Exception("Invalid chars in strings")
180 self.strings = strings
181
182 @classmethod
183 def init_raw(cls, name, raw_value):
184 """Init from raw"""
185 return cls(name, cls.__extract_prop_strings(raw_value))
186
187 def dts_represent(self, depth=0):
188 """Get dts string representation"""
189 return INDENT*depth + self.name + ' = "' + \
190 '", "'.join(self.strings) + '";'
191
192 def dtb_represent(self, string_store, pos=0, version=17):
193 """Get blob representation"""
194 # print "%x:%s" % (pos, self)
195 blob = pack('')
196 for chars in self.strings:
197 blob += chars.encode('ascii') + pack('b', 0)
198 blob_len = len(blob)
199 if version < 16 and (pos+12) % 8 != 0:
200 blob = pack('b', 0) * (8-((pos+12) % 8)) + blob
201 if blob_len % 4:
202 blob += pack('b', 0) * (4-(blob_len % 4))
203 strpos = string_store.find(self.name+'\0')
204 if strpos < 0:
205 strpos = len(string_store)
206 string_store += self.name+'\0'
207 blob = pack('>III', FDT_PROP, blob_len, strpos) + blob
208 pos += len(blob)
209 return (blob, string_store, pos)
210
211 def json_represent(self, depth=0):
212 """Ouput JSON"""
213 result = '%s: ["strings", ' % json.dumps(self.name)
214 result += ', '.join([json.dumps(stri) for stri in self.strings])
215 result += ']'
216 return result
217
218 def to_raw(self):
219 """Return RAW value representation"""
220 return ''.join([chars+'\0' for chars in self.strings])
221
222 def __str__(self):
223 """String representation"""
224 return "Property(%s,Strings:%s)" % (self.name, self.strings)
225
226 def __getitem__(self, index):
227 """Get strings, returns a string"""
228 return self.strings[index]
229
230 def __len__(self):
231 """Get strings count"""
232 return len(self.strings)
233
234 def __eq__(self, node):
235 """Check node equality
236 check properties are the same (same values)
237 """
238 if not FdtProperty.__eq__(self, node):
239 return False
240 if self.__len__() != len(node):
241 return False
242 for index in range(self.__len__()):
243 if self.strings[index] != node[index]:
244 return False
245 return True
246
247class FdtPropertyWords(FdtProperty):
248 """Property with words as value"""
249
250 def __init__(self, name, words):
251 """Init with words"""
252 FdtProperty.__init__(self, name)
253 for word in words:
254 if not 0 <= word <= 4294967295:
255 raise Exception(("Invalid word value %d, requires " +
256 "0 <= number <= 4294967295") % word)
257 if not len(words):
258 raise Exception("Invalid Words")
259 self.words = words
260
261 @classmethod
262 def init_raw(cls, name, raw_value):
263 """Init from raw"""
264 if len(raw_value) % 4 == 0:
265 words = [unpack(">I", raw_value[i:i+4])[0]
266 for i in range(0, len(raw_value), 4)]
267 return cls(name, words)
268 else:
269 raise Exception("Invalid raw Words")
270
271 def dts_represent(self, depth=0):
272 """Get dts string representation"""
273 return INDENT*depth + self.name + ' = <' + \
274 ' '.join(["0x%08x" % word for word in self.words]) + ">;"
275
276 def dtb_represent(self, string_store, pos=0, version=17):
277 """Get blob representation"""
278 # # print "%x:%s" % (pos, self)
279 strpos = string_store.find(self.name+'\0')
280 if strpos < 0:
281 strpos = len(string_store)
282 string_store += self.name+'\0'
283 blob = pack('>III', FDT_PROP, len(self.words)*4, strpos) + \
284 pack('').join([pack('>I', word) for word in self.words])
285 pos += len(blob)
286 return (blob, string_store, pos)
287
288 def json_represent(self, depth=0):
289 """Ouput JSON"""
290 result = '%s: ["words", "' % json.dumps(self.name)
291 result += '", "'.join(["0x%08x" % word for word in self.words])
292 result += '"]'
293 return result
294
295 def to_raw(self):
296 """Return RAW value representation"""
297 return ''.join([pack('>I', word) for word in self.words])
298
299 def __str__(self):
300 """String representation"""
301 return "Property(%s,Words:%s)" % (self.name, self.words)
302
303 def __getitem__(self, index):
304 """Get words, returns a word integer"""
305 return self.words[index]
306
307 def __len__(self):
308 """Get words count"""
309 return len(self.words)
310
311 def __eq__(self, node):
312 """Check node equality
313 check properties are the same (same values)
314 """
315 if not FdtProperty.__eq__(self, node):
316 return False
317 if self.__len__() != len(node):
318 return False
319 for index in range(self.__len__()):
320 if self.words[index] != node[index]:
321 return False
322 return True
323
324
325class FdtPropertyBytes(FdtProperty):
326 """Property with signed bytes as value"""
327
328 def __init__(self, name, bytez):
329 """Init with bytes"""
330 FdtProperty.__init__(self, name)
331 for byte in bytez:
332 if not -128 <= byte <= 127:
333 raise Exception(("Invalid value for byte %d, " +
334 "requires -128 <= number <= 127") % byte)
335 if not bytez:
336 raise Exception("Invalid Bytes")
337 self.bytes = bytez
338
339 @classmethod
340 def init_raw(cls, name, raw_value):
341 """Init from raw"""
342 return cls(name, unpack('b' * len(raw_value), raw_value))
343
344 def dts_represent(self, depth=0):
345 """Get dts string representation"""
346 return INDENT*depth + self.name + ' = [' + \
347 ' '.join(["%02x" % (byte & int('ffffffff',16))
348 for byte in self.bytes]) + "];"
349
350 def dtb_represent(self, string_store, pos=0, version=17):
351 """Get blob representation"""
352 # print "%x:%s" % (pos, self)
353 strpos = string_store.find(self.name+'\0')
354 if strpos < 0:
355 strpos = len(string_store)
356 string_store += self.name+'\0'
357 blob = pack('>III', FDT_PROP, len(self.bytes), strpos)
358 blob += pack('').join([pack('>b', byte) for byte in self.bytes])
359 if len(blob) % 4:
360 blob += pack('b', 0) * (4-(len(blob) % 4))
361 pos += len(blob)
362 return (blob, string_store, pos)
363
364 def json_represent(self, depth=0):
365 """Ouput JSON"""
366 result = '%s: ["bytes", "' % json.dumps(self.name)
367 result += '", "'.join(["%02x" % byte
368 for byte in self.bytes])
369 result += '"]'
370 return result
371
372 def to_raw(self):
373 """Return RAW value representation"""
374 return ''.join([pack('>b', byte) for byte in self.bytes])
375
376 def __str__(self):
377 """String representation"""
378 return "Property(%s,Bytes:%s)" % (self.name, self.bytes)
379
380 def __getitem__(self, index):
381 """Get bytes, returns a byte"""
382 return self.bytes[index]
383
384 def __len__(self):
385 """Get strings count"""
386 return len(self.bytes)
387
388 def __eq__(self, node):
389 """Check node equality
390 check properties are the same (same values)
391 """
392 if not FdtProperty.__eq__(self, node):
393 return False
394 if self.__len__() != len(node):
395 return False
396 for index in range(self.__len__()):
397 if self.bytes[index] != node[index]:
398 return False
399 return True
400
401
402class FdtNop(object): # pylint: disable-msg=R0903
403 """Nop child representation"""
404
405 def __init__(self):
406 """Init with nothing"""
407
408 def get_name(self): # pylint: disable-msg=R0201
409 """Return name"""
410 return None
411
412 def __str__(self):
413 """String representation"""
414 return ''
415
416 def dts_represent(self, depth=0): # pylint: disable-msg=R0201
417 """Get dts string representation"""
418 return INDENT*depth+'// [NOP]'
419
420 def dtb_represent(self, string_store, pos=0, version=17):
421 """Get blob representation"""
422 # print "%x:%s" % (pos, self)
423 pos += 4
424 return (pack('>I', FDT_NOP), string_store, pos)
425
426
427class FdtNode(object):
428 """Node representation"""
429
430 @staticmethod
431 def __validate_dt_name(name):
432 """Checks the name validity"""
433 return not any([True for char in name
434 if char not in string.printable])
435
436 def __init__(self, name):
437 """Init node with name"""
438 self.name = name
439 self.subdata = []
440 self.parent = None
441 if not FdtNode.__validate_dt_name(self.name):
442 raise Exception("Invalid name '%s'" % self.name)
443
444 def get_name(self):
445 """Get property name"""
446 return self.name
447
448 def __check_name_duplicate(self, name):
449 """Checks if name is not in a subnode"""
450 for data in self.subdata:
451 if not isinstance(data, FdtNop) \
452 and data.get_name() == name:
453 return True
454 return False
455
456 def add_subnode(self, node):
457 """Add child, deprecated use append()"""
458 self.append(node)
459
460 def add_raw_attribute(self, name, raw_value):
461 """Construct a raw attribute and add to child"""
462 self.append(FdtProperty.new_raw_property(name, raw_value))
463
464 def set_parent_node(self, node):
465 """Set parent node, None and FdtNode accepted"""
466 if node is not None and \
467 not isinstance(node, FdtNode):
468 raise Exception("Invalid object type")
469 self.parent = node
470
471 def get_parent_node(self):
472 """Get parent node"""
473 return self.parent
474
475 def __str__(self):
476 """String representation"""
477 return "Node(%s)" % self.name
478
479 def dts_represent(self, depth=0):
480 """Get dts string representation"""
481 result = ('\n').join([sub.dts_represent(depth+1)
482 for sub in self.subdata])
483 if len(result) > 0:
484 result += '\n'
485 return INDENT*depth + self.name + ' {\n' + \
486 result + INDENT*depth + "};"
487
488 def dtb_represent(self, strings_store, pos=0, version=17):
489 """Get blob representation
490 Pass string storage as strings_store, pos for current node start
491 and version as current dtb version
492 """
493 # print "%x:%s" % (pos, self)
494 strings = strings_store
495 if self.get_name() == '/':
496 blob = pack('>II', FDT_BEGIN_NODE, 0)
497 else:
498 blob = pack('>I', FDT_BEGIN_NODE)
499 blob += self.get_name().encode('ascii') + pack('b', 0)
500 if len(blob) % 4:
501 blob += pack('b', 0) * (4-(len(blob) % 4))
502 pos += len(blob)
503 for sub in self.subdata:
504 (data, strings, pos) = sub.dtb_represent(strings, pos, version)
505 blob += data
506 pos += 4
507 blob += pack('>I', FDT_END_NODE)
508 return (blob, strings, pos)
509
510 def json_represent(self, depth=0):
511 """Get dts string representation"""
512 result = (',\n'+ \
513 INDENT*(depth+1)).join([sub.json_represent(depth+1)
514 for sub in self.subdata
515 if not isinstance(sub, FdtNop)])
516 if len(result) > 0:
517 result = INDENT + result + '\n'+INDENT*depth
518 if self.get_name() == '/':
519 return "{\n" + INDENT*(depth) + result + "}"
520 else:
521 return json.dumps(self.name) + ': {\n' + \
522 INDENT*(depth) + result + "}"
523
524 def __getitem__(self, index):
525 """Get subnodes, returns either a Node, a Property or a Nop"""
526 return self.subdata[index]
527
528 def __setitem__(self, index, subnode):
529 """Set node at index, replacing previous subnode,
530 must not be a duplicate name
531 """
532 if self.subdata[index].get_name() != subnode.get_name() and \
533 self.__check_name_duplicate(subnode.get_name()):
534 raise Exception("%s : %s subnode already exists" % \
535 (self, subnode))
536 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
537 raise Exception("Invalid object type")
538 self.subdata[index] = subnode
539
540 def __len__(self):
541 """Get strings count"""
542 return len(self.subdata)
543
544 def __ne__(self, node):
545 """Check node inequality
546 i.e. is subnodes are the same, in either order
547 and properties are the same (same values)
548 The FdtNop is excluded from the check
549 """
550 return not self.__eq__(node)
551
552 def __eq__(self, node):
553 """Check node equality
554 i.e. is subnodes are the same, in either order
555 and properties are the same (same values)
556 The FdtNop is excluded from the check
557 """
558 if not isinstance(node, FdtNode):
559 raise Exception("Invalid object type")
560 if self.name != node.get_name():
561 return False
562 curnames = set([subnode.get_name() for subnode in self.subdata
563 if not isinstance(subnode, FdtNop)])
564 cmpnames = set([subnode.get_name() for subnode in node
565 if not isinstance(subnode, FdtNop)])
566 if curnames != cmpnames:
567 return False
568 for subnode in [subnode for subnode in self.subdata
569 if not isinstance(subnode, FdtNop)]:
570 index = node.index(subnode.get_name())
571 if subnode != node[index]:
572 return False
573 return True
574
575 def append(self, subnode):
576 """Append subnode, same as add_subnode"""
577 if self.__check_name_duplicate(subnode.get_name()):
578 raise Exception("%s : %s subnode already exists" % \
579 (self, subnode))
580 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
581 raise Exception("Invalid object type")
582 self.subdata.append(subnode)
583
584 def pop(self, index=-1):
585 """Remove and returns subnode at index, default the last"""
586 return self.subdata.pop(index)
587
588 def insert(self, index, subnode):
589 """Insert subnode before index, must not be a duplicate name"""
590 if self.__check_name_duplicate(subnode.get_name()):
591 raise Exception("%s : %s subnode already exists" % \
592 (self, subnode))
593 if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)):
594 raise Exception("Invalid object type")
595 self.subdata.insert(index, subnode)
596
597 def _find(self, name):
598 """Find name in subnodes"""
599 for i in range(0, len(self.subdata)):
600 if not isinstance(self.subdata[i], FdtNop) and \
601 name == self.subdata[i].get_name():
602 return i
603 return None
604
605 def remove(self, name):
606 """Remove subnode with the name
607 Raises ValueError is not present
608 """
609 index = self._find(name)
610 if index is None:
611 raise ValueError("Not present")
612 return self.subdata.pop(index)
613
614 def index(self, name):
615 """Returns position of subnode with the name
616 Raises ValueError is not present
617 """
618 index = self._find(name)
619 if index is None:
620 raise ValueError("Not present")
621 return index
622
623 def merge(self, node):
624 """Merge two nodes and subnodes
625 Replace current properties with the given properties
626 """
627 if not isinstance(node, FdtNode):
628 raise Exception("Can only merge with a FdtNode")
629 for subnode in [obj for obj in node
630 if isinstance(obj, (FdtNode, FdtProperty))]:
631 index = self._find(subnode.get_name())
632 if index is None:
633 dup = deepcopy(subnode)
634 if isinstance(subnode, FdtNode):
635 dup.set_parent_node(self)
636 self.append(dup)
637 elif isinstance(subnode, FdtNode):
638 self.subdata[index].merge(subnode)
639 else:
640 self.subdata[index] = copy(subnode)
641
642 def walk(self):
643 """Walk into subnodes and yield paths and objects
644 Returns set with (path string, node object)
645 """
646 node = self
647 start = 0
648 hist = []
649 curpath = []
650
651 while True:
652 for index in range(start, len(node)):
653 if isinstance(node[index], (FdtNode, FdtProperty)):
654 yield ('/' + '/'.join(curpath+[node[index].get_name()]),
655 node[index])
656 if isinstance(node[index], FdtNode):
657 if len(node[index]):
658 hist.append((node, index+1))
659 curpath.append(node[index].get_name())
660 node = node[index]
661 start = 0
662 index = -1
663 break
664 if index >= 0:
665 if len(hist):
666 (node, start) = hist.pop()
667 curpath.pop()
668 else:
669 break
670
671
672class Fdt(object):
673 """Flattened Device Tree representation"""
674
675 def __init__(self, version=17, last_comp_version=16, boot_cpuid_phys=0):
676 """Init FDT object with version and boot values"""
677 self.header = {'magic': FDT_MAGIC,
678 'totalsize': 0,
679 'off_dt_struct': 0,
680 'off_dt_strings': 0,
681 'off_mem_rsvmap': 0,
682 'version': version,
683 'last_comp_version': last_comp_version,
684 'boot_cpuid_phys': boot_cpuid_phys,
685 'size_dt_strings': 0,
686 'size_dt_struct': 0}
687 self.rootnode = None
688 self.prenops = None
689 self.postnops = None
690 self.reserve_entries = None
691
692 def add_rootnode(self, rootnode, prenops=None, postnops=None):
693 """Add root node"""
694 self.rootnode = rootnode
695 self.prenops = prenops
696 self.postnops = postnops
697
698 def get_rootnode(self):
699 """Get root node"""
700 return self.rootnode
701
702 def add_reserve_entries(self, reserve_entries):
703 """Add reserved entries as list of dict with
704 'address' and 'size' keys"""
705 self.reserve_entries = reserve_entries
706
707 def to_dts(self):
708 """Export to DTS representation in string format"""
709 result = "/dts-v1/;\n"
710 result += "// version:\t\t%d\n" % self.header['version']
711 result += "// last_comp_version:\t%d\n" % \
712 self.header['last_comp_version']
713 if self.header['version'] >= 2:
714 result += "// boot_cpuid_phys:\t0x%x\n" % \
715 self.header['boot_cpuid_phys']
716 result += '\n'
717 if self.reserve_entries is not None:
718 for entry in self.reserve_entries:
719 result += "/memreserve/ "
720 if entry['address']:
721 result += "%#x " % entry['address']
722 else:
723 result += "0 "
724 if entry['size']:
725 result += "%#x" % entry['size']
726 else:
727 result += "0"
728 result += ";\n"
729 if self.prenops:
730 result += '\n'.join([nop.dts_represent() for nop in self.prenops])
731 result += '\n'
732 if self.rootnode is not None:
733 result += self.rootnode.dts_represent()
734 if self.postnops:
735 result += '\n'
736 result += '\n'.join([nop.dts_represent() for nop in self.postnops])
737 return result
738
739 def to_dtb(self):
740 """Export to Blob format"""
741 if self.rootnode is None:
742 return None
743 blob_reserve_entries = pack('')
744 if self.reserve_entries is not None:
745 for entry in self.reserve_entries:
746 blob_reserve_entries += pack('>QQ',
747 entry['address'],
748 entry['size'])
749 blob_reserve_entries += pack('>QQ', 0, 0)
750 header_size = 7 * 4
751 if self.header['version'] >= 2:
752 header_size += 4
753 if self.header['version'] >= 3:
754 header_size += 4
755 if self.header['version'] >= 17:
756 header_size += 4
757 header_adjust = pack('')
758 if header_size % 8 != 0:
759 header_adjust = pack('b', 0) * (8 - (header_size % 8))
760 header_size += len(header_adjust)
761 dt_start = header_size + len(blob_reserve_entries)
762 # print "dt_start %d" % dt_start
763 (blob_dt, blob_strings, dt_pos) = \
764 self.rootnode.dtb_represent('', dt_start, self.header['version'])
765 if self.prenops is not None:
766 blob_dt = pack('').join([nop.dtb_represent('')[0]
767 for nop in self.prenops])\
768 + blob_dt
769 if self.postnops is not None:
770 blob_dt += pack('').join([nop.dtb_represent('')[0]
771 for nop in self.postnops])
772 blob_dt += pack('>I', FDT_END)
773 self.header['size_dt_strings'] = len(blob_strings)
774 self.header['size_dt_struct'] = len(blob_dt)
775 self.header['off_mem_rsvmap'] = header_size
776 self.header['off_dt_struct'] = dt_start
777 self.header['off_dt_strings'] = dt_start + len(blob_dt)
778 self.header['totalsize'] = dt_start + len(blob_dt) + len(blob_strings)
779 blob_header = pack('>IIIIIII', self.header['magic'],
780 self.header['totalsize'],
781 self.header['off_dt_struct'],
782 self.header['off_dt_strings'],
783 self.header['off_mem_rsvmap'],
784 self.header['version'],
785 self.header['last_comp_version'])
786 if self.header['version'] >= 2:
787 blob_header += pack('>I', self.header['boot_cpuid_phys'])
788 if self.header['version'] >= 3:
789 blob_header += pack('>I', self.header['size_dt_strings'])
790 if self.header['version'] >= 17:
791 blob_header += pack('>I', self.header['size_dt_struct'])
792 return blob_header + header_adjust + blob_reserve_entries + \
793 blob_dt + blob_strings.encode('ascii')
794
795 def to_json(self):
796 """Ouput JSON"""
797 if self.rootnode is None:
798 return None
799 return self.rootnode.json_represent()
800
801 def resolve_path(self, path):
802 """Resolve path like /memory/reg and return either a FdtNode,
803 a FdtProperty or None"""
804 if self.rootnode is None:
805 return None
806 if not path.startswith('/'):
807 return None
808 if len(path) > 1 and path.endswith('/'):
809 path = path[:-1]
810 if path == '/':
811 return self.rootnode
812 curnode = self.rootnode
813 for subpath in path[1:].split('/'):
814 found = None
815 if not isinstance(curnode, FdtNode):
816 return None
817 for node in curnode:
818 if subpath == node.get_name():
819 found = node
820 break
821 if found is None:
822 return None
823 curnode = found
824 return curnode
825
826def _add_json_to_fdtnode(node, subjson):
827 """Populate FdtNode with JSON dict items"""
828 for (key, value) in subjson.items():
829 if isinstance(value, dict):
830 subnode = FdtNode(key)
831 subnode.set_parent_node(node)
832 node.append(subnode)
833 _add_json_to_fdtnode(subnode, value)
834 elif isinstance(value, list):
835 if len(value) < 2:
836 raise Exception("Invalid list for %s" % key)
837 if value[0] == "words":
838 words = [int(word, 16) for word in value[1:]]
839 node.append(FdtPropertyWords(key, words))
840 elif value[0] == "bytes":
841 bytez = [int(byte, 16) for byte in value[1:]]
842 node.append(FdtPropertyBytes(key, bytez))
843 elif value[0] == "strings":
844 node.append(FdtPropertyStrings(key, \
845 [s for s in value[1:]]))
846 else:
847 raise Exception("Invalid list for %s" % key)
848 elif value is None:
849 node.append(FdtProperty(key))
850 else:
851 raise Exception("Invalid value for %s" % key)
852
853def FdtJsonParse(buf):
854 """Import FDT from JSON representation, see JSONDeviceTree.md for
855 structure and encoding
856 Returns an Fdt object
857 """
858 tree = json.loads(buf)
859
860 root = FdtNode('/')
861
862 _add_json_to_fdtnode(root, tree)
863
864 fdt = Fdt()
865 fdt.add_rootnode(root)
866 return fdt
867
868def FdtFsParse(path):
869 """Parse device tree filesystem and return a Fdt instance
870 Should be /proc/device-tree on a device, or the fusemount.py
871 mount point.
872 """
873 root = FdtNode("/")
874
875 if path.endswith('/'):
876 path = path[:-1]
877
878 nodes = {path: root}
879
880 for subpath, subdirs, files in os.walk(path):
881 if subpath not in nodes.keys():
882 raise Exception("os.walk error")
883 cur = nodes[subpath]
884 for f in files:
885 with open(subpath+'/'+f, 'rb') as content_file:
886 content = content_file.read()
887 prop = FdtProperty.new_raw_property(f, content)
888 cur.add_subnode(prop)
889 for subdir in subdirs:
890 subnode = FdtNode(subdir)
891 cur.add_subnode(subnode)
892 subnode.set_parent_node(cur)
893 nodes[subpath+'/'+subdir] = subnode
894
895 fdt = Fdt()
896 fdt.add_rootnode(root)
897 return fdt
898
899class FdtBlobParse(object): # pylint: disable-msg=R0903
900 """Parse from file input"""
901
902 __fdt_header_format = ">IIIIIII"
903 __fdt_header_names = ('magic', 'totalsize', 'off_dt_struct',
904 'off_dt_strings', 'off_mem_rsvmap', 'version',
905 'last_comp_version')
906
907 __fdt_reserve_entry_format = ">QQ"
908 __fdt_reserve_entry_names = ('address', 'size')
909
910 __fdt_dt_cell_format = ">I"
911 __fdt_dt_prop_format = ">II"
912 __fdt_dt_tag_name = {FDT_BEGIN_NODE: 'node_begin',
913 FDT_END_NODE: 'node_end',
914 FDT_PROP: 'prop',
915 FDT_NOP: 'nop',
916 FDT_END: 'end'}
917
918 def __extract_fdt_header(self):
919 """Extract DTB header"""
920 header = Struct(self.__fdt_header_format)
921 header_entry = Struct(">I")
922 data = self.infile.read(header.size)
923 result = dict(zip(self.__fdt_header_names, header.unpack_from(data)))
924 if result['version'] >= 2:
925 data = self.infile.read(header_entry.size)
926 result['boot_cpuid_phys'] = header_entry.unpack_from(data)[0]
927 if result['version'] >= 3:
928 data = self.infile.read(header_entry.size)
929 result['size_dt_strings'] = header_entry.unpack_from(data)[0]
930 if result['version'] >= 17:
931 data = self.infile.read(header_entry.size)
932 result['size_dt_struct'] = header_entry.unpack_from(data)[0]
933 return result
934
935 def __extract_fdt_reserve_entries(self):
936 """Extract reserved memory entries"""
937 header = Struct(self.__fdt_reserve_entry_format)
938 entries = []
939 self.infile.seek(self.fdt_header['off_mem_rsvmap'])
940 while True:
941 data = self.infile.read(header.size)
942 result = dict(zip(self.__fdt_reserve_entry_names,
943 header.unpack_from(data)))
944 if result['address'] == 0 and result['size'] == 0:
945 return entries
946 entries.append(result)
947
948 def __extract_fdt_nodename(self):
949 """Extract node name"""
950 data = ''
951 pos = self.infile.tell()
952 while True:
953 byte = self.infile.read(1)
954 if ord(byte) == 0:
955 break
956 data += byte.decode('ascii')
957 align_pos = pos + len(data) + 1
958 align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1))
959 self.infile.seek(align_pos)
960 return data
961
962 def __extract_fdt_string(self, prop_string_pos):
963 """Extract string from string pool"""
964 data = ''
965 pos = self.infile.tell()
966 self.infile.seek(self.fdt_header['off_dt_strings']+prop_string_pos)
967 while True:
968 byte = self.infile.read(1)
969 if ord(byte) == 0:
970 break
971 data += byte.decode('ascii')
972 self.infile.seek(pos)
973 return data
974
975 def __extract_fdt_prop(self):
976 """Extract property"""
977 prop = Struct(self.__fdt_dt_prop_format)
978 pos = self.infile.tell()
979 data = self.infile.read(prop.size)
980 (prop_size, prop_string_pos,) = prop.unpack_from(data)
981
982 prop_start = pos + prop.size
983 if self.fdt_header['version'] < 16 and prop_size >= 8:
984 prop_start = (((prop_start) + ((8) - 1)) & ~((8) - 1))
985
986 self.infile.seek(prop_start)
987 value = self.infile.read(prop_size)
988
989 align_pos = self.infile.tell()
990 align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1))
991 self.infile.seek(align_pos)
992
993 return (self.__extract_fdt_string(prop_string_pos), value)
994
995 def __extract_fdt_dt(self):
996 """Extract tags"""
997 cell = Struct(self.__fdt_dt_cell_format)
998 tags = []
999 self.infile.seek(self.fdt_header['off_dt_struct'])
1000 while True:
1001 data = self.infile.read(cell.size)
1002 if len(data) < cell.size:
1003 break
1004 tag, = cell.unpack_from(data)
1005 # print "*** %s" % self.__fdt_dt_tag_name.get(tag, '')
1006 if self.__fdt_dt_tag_name.get(tag, '') in 'node_begin':
1007 name = self.__extract_fdt_nodename()
1008 if len(name) == 0:
1009 name = '/'
1010 tags.append((tag, name))
1011 elif self.__fdt_dt_tag_name.get(tag, '') in ('node_end', 'nop'):
1012 tags.append((tag, ''))
1013 elif self.__fdt_dt_tag_name.get(tag, '') in 'end':
1014 tags.append((tag, ''))
1015 break
1016 elif self.__fdt_dt_tag_name.get(tag, '') in 'prop':
1017 propdata = self.__extract_fdt_prop()
1018 tags.append((tag, propdata))
1019 else:
1020 print("Unknown Tag %d" % tag)
1021 return tags
1022
1023 def __init__(self, infile):
1024 """Init with file input"""
1025 self.infile = infile
1026 self.fdt_header = self.__extract_fdt_header()
1027 if self.fdt_header['magic'] != FDT_MAGIC:
1028 raise Exception('Invalid Magic')
1029 if self.fdt_header['version'] > FDT_MAX_VERSION:
1030 raise Exception('Invalid Version %d' % self.fdt_header['version'])
1031 if self.fdt_header['last_comp_version'] > FDT_MAX_VERSION-1:
1032 raise Exception('Invalid last compatible Version %d' %
1033 self.fdt_header['last_comp_version'])
1034 self.fdt_reserve_entries = self.__extract_fdt_reserve_entries()
1035 self.fdt_dt_tags = self.__extract_fdt_dt()
1036
1037 def __to_nodes(self):
1038 """Represent fdt as Node and properties structure
1039 Returns a set with the pre-node Nops, the Root Node,
1040 and the post-node Nops.
1041 """
1042 prenops = []
1043 postnops = []
1044 rootnode = None
1045 curnode = None
1046 for tag in self.fdt_dt_tags:
1047 if self.__fdt_dt_tag_name.get(tag[0], '') in 'node_begin':
1048 newnode = FdtNode(tag[1])
1049 if rootnode is None:
1050 rootnode = newnode
1051 if curnode is not None:
1052 curnode.add_subnode(newnode)
1053 newnode.set_parent_node(curnode)
1054 curnode = newnode
1055 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'node_end':
1056 if curnode is not None:
1057 curnode = curnode.get_parent_node()
1058 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'nop':
1059 if curnode is not None:
1060 curnode.add_subnode(FdtNop())
1061 elif rootnode is not None:
1062 postnops.append(FdtNop())
1063 else:
1064 prenops.append(FdtNop())
1065 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'prop':
1066 if curnode is not None:
1067 curnode.add_raw_attribute(tag[1][0], tag[1][1])
1068 elif self.__fdt_dt_tag_name.get(tag[0], '') in 'end':
1069 continue
1070 return (prenops, rootnode, postnops)
1071
1072 def to_fdt(self):
1073 """Create a fdt object
1074 Returns a Fdt object
1075 """
1076 if self.fdt_header['version'] >= 2:
1077 boot_cpuid_phys = self.fdt_header['boot_cpuid_phys']
1078 else:
1079 boot_cpuid_phys = 0
1080 fdt = Fdt(version=self.fdt_header['version'],
1081 last_comp_version=self.fdt_header['last_comp_version'],
1082 boot_cpuid_phys=boot_cpuid_phys)
1083 (prenops, rootnode, postnops) = self.__to_nodes()
1084 fdt.add_rootnode(rootnode, prenops=prenops, postnops=postnops)
1085 fdt.add_reserve_entries(self.fdt_reserve_entries)
1086 return fdt