diff options
Diffstat (limited to 'src/python/m5/ext/pyfdt/pyfdt.py')
-rw-r--r-- | src/python/m5/ext/pyfdt/pyfdt.py | 1083 |
1 files changed, 1083 insertions, 0 deletions
diff --git a/src/python/m5/ext/pyfdt/pyfdt.py b/src/python/m5/ext/pyfdt/pyfdt.py new file mode 100644 index 000000000..3b3238e25 --- /dev/null +++ b/src/python/m5/ext/pyfdt/pyfdt.py @@ -0,0 +1,1083 @@ +# -*- coding: utf-8 -*- +""" +Device Tree Blob Parser + + Copyright 2014 Neil 'superna' Armstrong <superna9999@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +@author: Neil 'superna' Armstrong <superna9999@gmail.com> +""" + +import string +import os +import json +from copy import deepcopy, copy +from struct import Struct, unpack, pack + +FDT_MAGIC = 0xd00dfeed +FDT_BEGIN_NODE = 0x1 +FDT_END_NODE = 0x2 +FDT_PROP = 0x3 +FDT_NOP = 0x4 +FDT_END = 0x9 + +INDENT = ' ' * 4 + +FDT_MAX_VERSION = 17 + + +class FdtProperty(object): + """ Represents an empty property""" + + @staticmethod + def __validate_dt_name(name): + """Checks the name validity""" + return not any([True for char in name + if char not in string.printable]) + + def __init__(self, name): + """Init with name""" + self.name = name + if not FdtProperty.__validate_dt_name(self.name): + raise Exception("Invalid name '%s'" % self.name) + + def get_name(self): + """Get property name""" + return self.name + + def __str__(self): + """String representation""" + return "Property(%s)" % self.name + + def dts_represent(self, depth=0): + """Get dts string representation""" + return INDENT*depth + self.name + ';' + + def dtb_represent(self, string_store, pos=0, version=17): + """Get blob representation""" + # print "%x:%s" % (pos, self) + strpos = string_store.find(self.name+'\0') + if strpos < 0: + strpos = len(string_store) + string_store += self.name+'\0' + pos += 12 + return (pack('>III', FDT_PROP, 0, strpos), + string_store, pos) + + def json_represent(self, depth=0): + """Ouput JSON""" + return '%s: null' % json.dumps(self.name) + + def to_raw(self): + """Return RAW value representation""" + return '' + + def __getitem__(self, value): + """Returns No Items""" + return None + + def __ne__(self, node): + """Check property inequality + """ + return not self.__eq__(node) + + def __eq__(self, node): + """Check node equality + check properties are the same (same values) + """ + if not isinstance(node, FdtProperty): + raise Exception("Invalid object type") + if self.name != node.get_name(): + return False + return True + + @staticmethod + def __check_prop_strings(value): + """Check property string validity + Python version of util_is_printable_string from dtc + """ + pos = 0 + posi = 0 + end = len(value) + + if not len(value): + return None + + #Needed for python 3 support: If a bytes object is passed, + #decode it with the ascii codec. If the decoding fails, assume + #it was not a string object. + try: + value = value.decode('ascii') + except ValueError: + return None + + #Test both against string 0 and int 0 because of + # python2/3 compatibility + if value[-1] != '\0': + return None + + while pos < end: + posi = pos + while pos < end and value[pos] != '\0' \ + and value[pos] in string.printable \ + and value[pos] not in ('\r', '\n'): + pos += 1 + + if value[pos] != '\0' or pos == posi: + return None + pos += 1 + + return True + + @staticmethod + def new_raw_property(name, raw_value): + """Instantiate property with raw value type""" + if FdtProperty.__check_prop_strings(raw_value): + return FdtPropertyStrings.init_raw(name, raw_value) + elif len(raw_value) and len(raw_value) % 4 == 0: + return FdtPropertyWords.init_raw(name, raw_value) + elif len(raw_value) and len(raw_value): + return FdtPropertyBytes.init_raw(name, raw_value) + else: + return FdtProperty(name) + + +class FdtPropertyStrings(FdtProperty): + """Property with strings as value""" + + @classmethod + def __extract_prop_strings(cls, value): + """Extract strings from raw_value""" + return [st for st in \ + value.decode('ascii').split('\0') if len(st)] + + def __init__(self, name, strings): + """Init with strings""" + FdtProperty.__init__(self, name) + if not strings: + raise Exception("Invalid strings") + for stri in strings: + if len(stri) == 0: + raise Exception("Invalid strings") + if any([True for char in stri + if char not in string.printable + or char in ('\r', '\n')]): + raise Exception("Invalid chars in strings") + self.strings = strings + + @classmethod + def init_raw(cls, name, raw_value): + """Init from raw""" + return cls(name, cls.__extract_prop_strings(raw_value)) + + def dts_represent(self, depth=0): + """Get dts string representation""" + return INDENT*depth + self.name + ' = "' + \ + '", "'.join(self.strings) + '";' + + def dtb_represent(self, string_store, pos=0, version=17): + """Get blob representation""" + # print "%x:%s" % (pos, self) + blob = pack('') + for chars in self.strings: + blob += chars.encode('ascii') + pack('b', 0) + blob_len = len(blob) + if version < 16 and (pos+12) % 8 != 0: + blob = pack('b', 0) * (8-((pos+12) % 8)) + blob + if blob_len % 4: + blob += pack('b', 0) * (4-(blob_len % 4)) + strpos = string_store.find(self.name+'\0') + if strpos < 0: + strpos = len(string_store) + string_store += self.name+'\0' + blob = pack('>III', FDT_PROP, blob_len, strpos) + blob + pos += len(blob) + return (blob, string_store, pos) + + def json_represent(self, depth=0): + """Ouput JSON""" + result = '%s: ["strings", ' % json.dumps(self.name) + result += ', '.join([json.dumps(stri) for stri in self.strings]) + result += ']' + return result + + def to_raw(self): + """Return RAW value representation""" + return ''.join([chars+'\0' for chars in self.strings]) + + def __str__(self): + """String representation""" + return "Property(%s,Strings:%s)" % (self.name, self.strings) + + def __getitem__(self, index): + """Get strings, returns a string""" + return self.strings[index] + + def __len__(self): + """Get strings count""" + return len(self.strings) + + def __eq__(self, node): + """Check node equality + check properties are the same (same values) + """ + if not FdtProperty.__eq__(self, node): + return False + if self.__len__() != len(node): + return False + for index in range(self.__len__()): + if self.strings[index] != node[index]: + return False + return True + +class FdtPropertyWords(FdtProperty): + """Property with words as value""" + + def __init__(self, name, words): + """Init with words""" + FdtProperty.__init__(self, name) + for word in words: + if not 0 <= word <= 4294967295: + raise Exception(("Invalid word value %d, requires " + + "0 <= number <= 4294967295") % word) + if not len(words): + raise Exception("Invalid Words") + self.words = words + + @classmethod + def init_raw(cls, name, raw_value): + """Init from raw""" + if len(raw_value) % 4 == 0: + words = [unpack(">I", raw_value[i:i+4])[0] + for i in range(0, len(raw_value), 4)] + return cls(name, words) + else: + raise Exception("Invalid raw Words") + + def dts_represent(self, depth=0): + """Get dts string representation""" + return INDENT*depth + self.name + ' = <' + \ + ' '.join(["0x%08x" % word for word in self.words]) + ">;" + + def dtb_represent(self, string_store, pos=0, version=17): + """Get blob representation""" + # # print "%x:%s" % (pos, self) + strpos = string_store.find(self.name+'\0') + if strpos < 0: + strpos = len(string_store) + string_store += self.name+'\0' + blob = pack('>III', FDT_PROP, len(self.words)*4, strpos) + \ + pack('').join([pack('>I', word) for word in self.words]) + pos += len(blob) + return (blob, string_store, pos) + + def json_represent(self, depth=0): + """Ouput JSON""" + result = '%s: ["words", "' % json.dumps(self.name) + result += '", "'.join(["0x%08x" % word for word in self.words]) + result += '"]' + return result + + def to_raw(self): + """Return RAW value representation""" + return ''.join([pack('>I', word) for word in self.words]) + + def __str__(self): + """String representation""" + return "Property(%s,Words:%s)" % (self.name, self.words) + + def __getitem__(self, index): + """Get words, returns a word integer""" + return self.words[index] + + def __len__(self): + """Get words count""" + return len(self.words) + + def __eq__(self, node): + """Check node equality + check properties are the same (same values) + """ + if not FdtProperty.__eq__(self, node): + return False + if self.__len__() != len(node): + return False + for index in range(self.__len__()): + if self.words[index] != node[index]: + return False + return True + + +class FdtPropertyBytes(FdtProperty): + """Property with signed bytes as value""" + + def __init__(self, name, bytez): + """Init with bytes""" + FdtProperty.__init__(self, name) + for byte in bytez: + if not -128 <= byte <= 127: + raise Exception(("Invalid value for byte %d, " + + "requires -128 <= number <= 127") % byte) + if not bytez: + raise Exception("Invalid Bytes") + self.bytes = bytez + + @classmethod + def init_raw(cls, name, raw_value): + """Init from raw""" + return cls(name, unpack('b' * len(raw_value), raw_value)) + + def dts_represent(self, depth=0): + """Get dts string representation""" + return INDENT*depth + self.name + ' = [' + \ + ' '.join(["%02x" % (byte & int('ffffffff',16)) + for byte in self.bytes]) + "];" + + def dtb_represent(self, string_store, pos=0, version=17): + """Get blob representation""" + # print "%x:%s" % (pos, self) + strpos = string_store.find(self.name+'\0') + if strpos < 0: + strpos = len(string_store) + string_store += self.name+'\0' + blob = pack('>III', FDT_PROP, len(self.bytes), strpos) + blob += pack('').join([pack('>b', byte) for byte in self.bytes]) + if len(blob) % 4: + blob += pack('b', 0) * (4-(len(blob) % 4)) + pos += len(blob) + return (blob, string_store, pos) + + def json_represent(self, depth=0): + """Ouput JSON""" + result = '%s: ["bytes", "' % json.dumps(self.name) + result += '", "'.join(["%02x" % byte + for byte in self.bytes]) + result += '"]' + return result + + def to_raw(self): + """Return RAW value representation""" + return ''.join([pack('>b', byte) for byte in self.bytes]) + + def __str__(self): + """String representation""" + return "Property(%s,Bytes:%s)" % (self.name, self.bytes) + + def __getitem__(self, index): + """Get bytes, returns a byte""" + return self.bytes[index] + + def __len__(self): + """Get strings count""" + return len(self.bytes) + + def __eq__(self, node): + """Check node equality + check properties are the same (same values) + """ + if not FdtProperty.__eq__(self, node): + return False + if self.__len__() != len(node): + return False + for index in range(self.__len__()): + if self.bytes[index] != node[index]: + return False + return True + + +class FdtNop(object): # pylint: disable-msg=R0903 + """Nop child representation""" + + def __init__(self): + """Init with nothing""" + + def get_name(self): # pylint: disable-msg=R0201 + """Return name""" + return None + + def __str__(self): + """String representation""" + return '' + + def dts_represent(self, depth=0): # pylint: disable-msg=R0201 + """Get dts string representation""" + return INDENT*depth+'// [NOP]' + + def dtb_represent(self, string_store, pos=0, version=17): + """Get blob representation""" + # print "%x:%s" % (pos, self) + pos += 4 + return (pack('>I', FDT_NOP), string_store, pos) + + +class FdtNode(object): + """Node representation""" + + @staticmethod + def __validate_dt_name(name): + """Checks the name validity""" + return not any([True for char in name + if char not in string.printable]) + + def __init__(self, name): + """Init node with name""" + self.name = name + self.subdata = [] + self.parent = None + if not FdtNode.__validate_dt_name(self.name): + raise Exception("Invalid name '%s'" % self.name) + + def get_name(self): + """Get property name""" + return self.name + + def __check_name_duplicate(self, name): + """Checks if name is not in a subnode""" + for data in self.subdata: + if not isinstance(data, FdtNop) \ + and data.get_name() == name: + return True + return False + + def add_subnode(self, node): + """Add child, deprecated use append()""" + self.append(node) + + def add_raw_attribute(self, name, raw_value): + """Construct a raw attribute and add to child""" + self.append(FdtProperty.new_raw_property(name, raw_value)) + + def set_parent_node(self, node): + """Set parent node, None and FdtNode accepted""" + if node is not None and \ + not isinstance(node, FdtNode): + raise Exception("Invalid object type") + self.parent = node + + def get_parent_node(self): + """Get parent node""" + return self.parent + + def __str__(self): + """String representation""" + return "Node(%s)" % self.name + + def dts_represent(self, depth=0): + """Get dts string representation""" + result = ('\n').join([sub.dts_represent(depth+1) + for sub in self.subdata]) + if len(result) > 0: + result += '\n' + return INDENT*depth + self.name + ' {\n' + \ + result + INDENT*depth + "};" + + def dtb_represent(self, strings_store, pos=0, version=17): + """Get blob representation + Pass string storage as strings_store, pos for current node start + and version as current dtb version + """ + # print "%x:%s" % (pos, self) + strings = strings_store + if self.get_name() == '/': + blob = pack('>II', FDT_BEGIN_NODE, 0) + else: + blob = pack('>I', FDT_BEGIN_NODE) + blob += self.get_name().encode('ascii') + pack('b', 0) + if len(blob) % 4: + blob += pack('b', 0) * (4-(len(blob) % 4)) + pos += len(blob) + for sub in self.subdata: + (data, strings, pos) = sub.dtb_represent(strings, pos, version) + blob += data + pos += 4 + blob += pack('>I', FDT_END_NODE) + return (blob, strings, pos) + + def json_represent(self, depth=0): + """Get dts string representation""" + result = (',\n'+ \ + INDENT*(depth+1)).join([sub.json_represent(depth+1) + for sub in self.subdata + if not isinstance(sub, FdtNop)]) + if len(result) > 0: + result = INDENT + result + '\n'+INDENT*depth + if self.get_name() == '/': + return "{\n" + INDENT*(depth) + result + "}" + else: + return json.dumps(self.name) + ': {\n' + \ + INDENT*(depth) + result + "}" + + def __getitem__(self, index): + """Get subnodes, returns either a Node, a Property or a Nop""" + return self.subdata[index] + + def __setitem__(self, index, subnode): + """Set node at index, replacing previous subnode, + must not be a duplicate name + """ + if self.subdata[index].get_name() != subnode.get_name() and \ + self.__check_name_duplicate(subnode.get_name()): + raise Exception("%s : %s subnode already exists" % \ + (self, subnode)) + if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)): + raise Exception("Invalid object type") + self.subdata[index] = subnode + + def __len__(self): + """Get strings count""" + return len(self.subdata) + + def __ne__(self, node): + """Check node inequality + i.e. is subnodes are the same, in either order + and properties are the same (same values) + The FdtNop is excluded from the check + """ + return not self.__eq__(node) + + def __eq__(self, node): + """Check node equality + i.e. is subnodes are the same, in either order + and properties are the same (same values) + The FdtNop is excluded from the check + """ + if not isinstance(node, FdtNode): + raise Exception("Invalid object type") + if self.name != node.get_name(): + return False + curnames = set([subnode.get_name() for subnode in self.subdata + if not isinstance(subnode, FdtNop)]) + cmpnames = set([subnode.get_name() for subnode in node + if not isinstance(subnode, FdtNop)]) + if curnames != cmpnames: + return False + for subnode in [subnode for subnode in self.subdata + if not isinstance(subnode, FdtNop)]: + index = node.index(subnode.get_name()) + if subnode != node[index]: + return False + return True + + def append(self, subnode): + """Append subnode, same as add_subnode""" + if self.__check_name_duplicate(subnode.get_name()): + raise Exception("%s : %s subnode already exists" % \ + (self, subnode)) + if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)): + raise Exception("Invalid object type") + self.subdata.append(subnode) + + def pop(self, index=-1): + """Remove and returns subnode at index, default the last""" + return self.subdata.pop(index) + + def insert(self, index, subnode): + """Insert subnode before index, must not be a duplicate name""" + if self.__check_name_duplicate(subnode.get_name()): + raise Exception("%s : %s subnode already exists" % \ + (self, subnode)) + if not isinstance(subnode, (FdtNode, FdtProperty, FdtNop)): + raise Exception("Invalid object type") + self.subdata.insert(index, subnode) + + def _find(self, name): + """Find name in subnodes""" + for i in range(0, len(self.subdata)): + if not isinstance(self.subdata[i], FdtNop) and \ + name == self.subdata[i].get_name(): + return i + return None + + def remove(self, name): + """Remove subnode with the name + Raises ValueError is not present + """ + index = self._find(name) + if index is None: + raise ValueError("Not present") + return self.subdata.pop(index) + + def index(self, name): + """Returns position of subnode with the name + Raises ValueError is not present + """ + index = self._find(name) + if index is None: + raise ValueError("Not present") + return index + + def merge(self, node): + """Merge two nodes and subnodes + Replace current properties with the given properties + """ + if not isinstance(node, FdtNode): + raise Exception("Can only merge with a FdtNode") + for subnode in [obj for obj in node + if isinstance(obj, (FdtNode, FdtProperty))]: + index = self._find(subnode.get_name()) + if index is None: + dup = deepcopy(subnode) + if isinstance(subnode, FdtNode): + dup.set_parent_node(self) + self.append(dup) + elif isinstance(subnode, FdtNode): + self.subdata[index].merge(subnode) + else: + self.subdata[index] = copy(subnode) + + def walk(self): + """Walk into subnodes and yield paths and objects + Returns set with (path string, node object) + """ + node = self + start = 0 + hist = [] + curpath = [] + + while True: + for index in range(start, len(node)): + if isinstance(node[index], (FdtNode, FdtProperty)): + yield ('/' + '/'.join(curpath+[node[index].get_name()]), + node[index]) + if isinstance(node[index], FdtNode): + if len(node[index]): + hist.append((node, index+1)) + curpath.append(node[index].get_name()) + node = node[index] + start = 0 + index = -1 + break + if index >= 0: + if len(hist): + (node, start) = hist.pop() + curpath.pop() + else: + break + + +class Fdt(object): + """Flattened Device Tree representation""" + + def __init__(self, version=17, last_comp_version=16, boot_cpuid_phys=0): + """Init FDT object with version and boot values""" + self.header = {'magic': FDT_MAGIC, + 'totalsize': 0, + 'off_dt_struct': 0, + 'off_dt_strings': 0, + 'off_mem_rsvmap': 0, + 'version': version, + 'last_comp_version': last_comp_version, + 'boot_cpuid_phys': boot_cpuid_phys, + 'size_dt_strings': 0, + 'size_dt_struct': 0} + self.rootnode = None + self.prenops = None + self.postnops = None + self.reserve_entries = None + + def add_rootnode(self, rootnode, prenops=None, postnops=None): + """Add root node""" + self.rootnode = rootnode + self.prenops = prenops + self.postnops = postnops + + def get_rootnode(self): + """Get root node""" + return self.rootnode + + def add_reserve_entries(self, reserve_entries): + """Add reserved entries as list of dict with + 'address' and 'size' keys""" + self.reserve_entries = reserve_entries + + def to_dts(self): + """Export to DTS representation in string format""" + result = "/dts-v1/;\n" + result += "// version:\t\t%d\n" % self.header['version'] + result += "// last_comp_version:\t%d\n" % \ + self.header['last_comp_version'] + if self.header['version'] >= 2: + result += "// boot_cpuid_phys:\t0x%x\n" % \ + self.header['boot_cpuid_phys'] + result += '\n' + if self.reserve_entries is not None: + for entry in self.reserve_entries: + result += "/memreserve/ " + if entry['address']: + result += "%#x " % entry['address'] + else: + result += "0 " + if entry['size']: + result += "%#x" % entry['size'] + else: + result += "0" + result += ";\n" + if self.prenops: + result += '\n'.join([nop.dts_represent() for nop in self.prenops]) + result += '\n' + if self.rootnode is not None: + result += self.rootnode.dts_represent() + if self.postnops: + result += '\n' + result += '\n'.join([nop.dts_represent() for nop in self.postnops]) + return result + + def to_dtb(self): + """Export to Blob format""" + if self.rootnode is None: + return None + blob_reserve_entries = pack('') + if self.reserve_entries is not None: + for entry in self.reserve_entries: + blob_reserve_entries += pack('>QQ', + entry['address'], + entry['size']) + blob_reserve_entries += pack('>QQ', 0, 0) + header_size = 7 * 4 + if self.header['version'] >= 2: + header_size += 4 + if self.header['version'] >= 3: + header_size += 4 + if self.header['version'] >= 17: + header_size += 4 + header_adjust = pack('') + if header_size % 8 != 0: + header_adjust = pack('b', 0) * (8 - (header_size % 8)) + header_size += len(header_adjust) + dt_start = header_size + len(blob_reserve_entries) + # print "dt_start %d" % dt_start + (blob_dt, blob_strings, dt_pos) = \ + self.rootnode.dtb_represent('', dt_start, self.header['version']) + if self.prenops is not None: + blob_dt = pack('').join([nop.dtb_represent('')[0] + for nop in self.prenops])\ + + blob_dt + if self.postnops is not None: + blob_dt += pack('').join([nop.dtb_represent('')[0] + for nop in self.postnops]) + blob_dt += pack('>I', FDT_END) + self.header['size_dt_strings'] = len(blob_strings) + self.header['size_dt_struct'] = len(blob_dt) + self.header['off_mem_rsvmap'] = header_size + self.header['off_dt_struct'] = dt_start + self.header['off_dt_strings'] = dt_start + len(blob_dt) + self.header['totalsize'] = dt_start + len(blob_dt) + len(blob_strings) + blob_header = pack('>IIIIIII', self.header['magic'], + self.header['totalsize'], + self.header['off_dt_struct'], + self.header['off_dt_strings'], + self.header['off_mem_rsvmap'], + self.header['version'], + self.header['last_comp_version']) + if self.header['version'] >= 2: + blob_header += pack('>I', self.header['boot_cpuid_phys']) + if self.header['version'] >= 3: + blob_header += pack('>I', self.header['size_dt_strings']) + if self.header['version'] >= 17: + blob_header += pack('>I', self.header['size_dt_struct']) + return blob_header + header_adjust + blob_reserve_entries + \ + blob_dt + blob_strings.encode('ascii') + + def to_json(self): + """Ouput JSON""" + if self.rootnode is None: + return None + return self.rootnode.json_represent() + + def resolve_path(self, path): + """Resolve path like /memory/reg and return either a FdtNode, + a FdtProperty or None""" + if self.rootnode is None: + return None + if not path.startswith('/'): + return None + if len(path) > 1 and path.endswith('/'): + path = path[:-1] + if path == '/': + return self.rootnode + curnode = self.rootnode + for subpath in path[1:].split('/'): + found = None + if not isinstance(curnode, FdtNode): + return None + for node in curnode: + if subpath == node.get_name(): + found = node + break + if found is None: + return None + curnode = found + return curnode + +def _add_json_to_fdtnode(node, subjson): + """Populate FdtNode with JSON dict items""" + for (key, value) in subjson.items(): + if isinstance(value, dict): + subnode = FdtNode(key) + subnode.set_parent_node(node) + node.append(subnode) + _add_json_to_fdtnode(subnode, value) + elif isinstance(value, list): + if len(value) < 2: + raise Exception("Invalid list for %s" % key) + if value[0] == "words": + words = [int(word, 16) for word in value[1:]] + node.append(FdtPropertyWords(key, words)) + elif value[0] == "bytes": + bytez = [int(byte, 16) for byte in value[1:]] + node.append(FdtPropertyBytes(key, bytez)) + elif value[0] == "strings": + node.append(FdtPropertyStrings(key, \ + [s for s in value[1:]])) + else: + raise Exception("Invalid list for %s" % key) + elif value is None: + node.append(FdtProperty(key)) + else: + raise Exception("Invalid value for %s" % key) + +def FdtJsonParse(buf): + """Import FDT from JSON representation, see JSONDeviceTree.md for + structure and encoding + Returns an Fdt object + """ + tree = json.loads(buf) + + root = FdtNode('/') + + _add_json_to_fdtnode(root, tree) + + fdt = Fdt() + fdt.add_rootnode(root) + return fdt + +def FdtFsParse(path): + """Parse device tree filesystem and return a Fdt instance + Should be /proc/device-tree on a device, or the fusemount.py + mount point. + """ + root = FdtNode("/") + + if path.endswith('/'): + path = path[:-1] + + nodes = {path: root} + + for subpath, subdirs, files in os.walk(path): + if subpath not in nodes.keys(): + raise Exception("os.walk error") + cur = nodes[subpath] + for f in files: + with open(subpath+'/'+f, 'rb') as content_file: + content = content_file.read() + prop = FdtProperty.new_raw_property(f, content) + cur.add_subnode(prop) + for subdir in subdirs: + subnode = FdtNode(subdir) + cur.add_subnode(subnode) + subnode.set_parent_node(cur) + nodes[subpath+'/'+subdir] = subnode + + fdt = Fdt() + fdt.add_rootnode(root) + return fdt + +class FdtBlobParse(object): # pylint: disable-msg=R0903 + """Parse from file input""" + + __fdt_header_format = ">IIIIIII" + __fdt_header_names = ('magic', 'totalsize', 'off_dt_struct', + 'off_dt_strings', 'off_mem_rsvmap', 'version', + 'last_comp_version') + + __fdt_reserve_entry_format = ">QQ" + __fdt_reserve_entry_names = ('address', 'size') + + __fdt_dt_cell_format = ">I" + __fdt_dt_prop_format = ">II" + __fdt_dt_tag_name = {FDT_BEGIN_NODE: 'node_begin', + FDT_END_NODE: 'node_end', + FDT_PROP: 'prop', + FDT_NOP: 'nop', + FDT_END: 'end'} + + def __extract_fdt_header(self): + """Extract DTB header""" + header = Struct(self.__fdt_header_format) + header_entry = Struct(">I") + data = self.infile.read(header.size) + result = dict(zip(self.__fdt_header_names, header.unpack_from(data))) + if result['version'] >= 2: + data = self.infile.read(header_entry.size) + result['boot_cpuid_phys'] = header_entry.unpack_from(data)[0] + if result['version'] >= 3: + data = self.infile.read(header_entry.size) + result['size_dt_strings'] = header_entry.unpack_from(data)[0] + if result['version'] >= 17: + data = self.infile.read(header_entry.size) + result['size_dt_struct'] = header_entry.unpack_from(data)[0] + return result + + def __extract_fdt_reserve_entries(self): + """Extract reserved memory entries""" + header = Struct(self.__fdt_reserve_entry_format) + entries = [] + self.infile.seek(self.fdt_header['off_mem_rsvmap']) + while True: + data = self.infile.read(header.size) + result = dict(zip(self.__fdt_reserve_entry_names, + header.unpack_from(data))) + if result['address'] == 0 and result['size'] == 0: + return entries + entries.append(result) + + def __extract_fdt_nodename(self): + """Extract node name""" + data = '' + pos = self.infile.tell() + while True: + byte = self.infile.read(1) + if ord(byte) == 0: + break + data += byte.decode('ascii') + align_pos = pos + len(data) + 1 + align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1)) + self.infile.seek(align_pos) + return data + + def __extract_fdt_string(self, prop_string_pos): + """Extract string from string pool""" + data = '' + pos = self.infile.tell() + self.infile.seek(self.fdt_header['off_dt_strings']+prop_string_pos) + while True: + byte = self.infile.read(1) + if ord(byte) == 0: + break + data += byte.decode('ascii') + self.infile.seek(pos) + return data + + def __extract_fdt_prop(self): + """Extract property""" + prop = Struct(self.__fdt_dt_prop_format) + pos = self.infile.tell() + data = self.infile.read(prop.size) + (prop_size, prop_string_pos,) = prop.unpack_from(data) + + prop_start = pos + prop.size + if self.fdt_header['version'] < 16 and prop_size >= 8: + prop_start = (((prop_start) + ((8) - 1)) & ~((8) - 1)) + + self.infile.seek(prop_start) + value = self.infile.read(prop_size) + + align_pos = self.infile.tell() + align_pos = (((align_pos) + ((4) - 1)) & ~((4) - 1)) + self.infile.seek(align_pos) + + return (self.__extract_fdt_string(prop_string_pos), value) + + def __extract_fdt_dt(self): + """Extract tags""" + cell = Struct(self.__fdt_dt_cell_format) + tags = [] + self.infile.seek(self.fdt_header['off_dt_struct']) + while True: + data = self.infile.read(cell.size) + if len(data) < cell.size: + break + tag, = cell.unpack_from(data) + # print "*** %s" % self.__fdt_dt_tag_name.get(tag, '') + if self.__fdt_dt_tag_name.get(tag, '') in 'node_begin': + name = self.__extract_fdt_nodename() + if len(name) == 0: + name = '/' + tags.append((tag, name)) + elif self.__fdt_dt_tag_name.get(tag, '') in ('node_end', 'nop'): + tags.append((tag, '')) + elif self.__fdt_dt_tag_name.get(tag, '') in 'end': + tags.append((tag, '')) + break + elif self.__fdt_dt_tag_name.get(tag, '') in 'prop': + propdata = self.__extract_fdt_prop() + tags.append((tag, propdata)) + else: + print("Unknown Tag %d" % tag) + return tags + + def __init__(self, infile): + """Init with file input""" + self.infile = infile + self.fdt_header = self.__extract_fdt_header() + if self.fdt_header['magic'] != FDT_MAGIC: + raise Exception('Invalid Magic') + if self.fdt_header['version'] > FDT_MAX_VERSION: + raise Exception('Invalid Version %d' % self.fdt_header['version']) + if self.fdt_header['last_comp_version'] > FDT_MAX_VERSION-1: + raise Exception('Invalid last compatible Version %d' % + self.fdt_header['last_comp_version']) + self.fdt_reserve_entries = self.__extract_fdt_reserve_entries() + self.fdt_dt_tags = self.__extract_fdt_dt() + + def __to_nodes(self): + """Represent fdt as Node and properties structure + Returns a set with the pre-node Nops, the Root Node, + and the post-node Nops. + """ + prenops = [] + postnops = [] + rootnode = None + curnode = None + for tag in self.fdt_dt_tags: + if self.__fdt_dt_tag_name.get(tag[0], '') in 'node_begin': + newnode = FdtNode(tag[1]) + if rootnode is None: + rootnode = newnode + if curnode is not None: + curnode.add_subnode(newnode) + newnode.set_parent_node(curnode) + curnode = newnode + elif self.__fdt_dt_tag_name.get(tag[0], '') in 'node_end': + if curnode is not None: + curnode = curnode.get_parent_node() + elif self.__fdt_dt_tag_name.get(tag[0], '') in 'nop': + if curnode is not None: + curnode.add_subnode(FdtNop()) + elif rootnode is not None: + postnops.append(FdtNop()) + else: + prenops.append(FdtNop()) + elif self.__fdt_dt_tag_name.get(tag[0], '') in 'prop': + if curnode is not None: + curnode.add_raw_attribute(tag[1][0], tag[1][1]) + elif self.__fdt_dt_tag_name.get(tag[0], '') in 'end': + continue + return (prenops, rootnode, postnops) + + def to_fdt(self): + """Create a fdt object + Returns a Fdt object + """ + if self.fdt_header['version'] >= 2: + boot_cpuid_phys = self.fdt_header['boot_cpuid_phys'] + else: + boot_cpuid_phys = 0 + fdt = Fdt(version=self.fdt_header['version'], + last_comp_version=self.fdt_header['last_comp_version'], + boot_cpuid_phys=boot_cpuid_phys) + (prenops, rootnode, postnops) = self.__to_nodes() + fdt.add_rootnode(rootnode, prenops=prenops, postnops=postnops) + fdt.add_reserve_entries(self.fdt_reserve_entries) + return fdt |