pyfdt.py revision 12468
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 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 1084