Skip to content

Instantly share code, notes, and snippets.

@fourkbomb
Last active June 22, 2019 07:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fourkbomb/4959b2c943d3f086089ca11758ba7a44 to your computer and use it in GitHub Desktop.
Save fourkbomb/4959b2c943d3f086089ca11758ba7a44 to your computer and use it in GitHub Desktop.
basic diff between two device tree blobs, attempts to account for differing phandles
#!/usr/bin/env python
# Copyright (C) 2019 Simon Shields <simon@lineageos.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pyfdt.pyfdt import *
import sys
if len(sys.argv) < 3:
print("Need two files");
sys.exit(1)
with open(sys.argv[1], 'rb') as f:
blob1 = FdtBlobParse(f).to_fdt()
with open(sys.argv[2], 'rb') as f:
blob2 = FdtBlobParse(f).to_fdt()
print_buf = []
orig_print = print
indent = 0
def my_print(*args, **kwargs):
global print_buf
for p in print_buf:
orig_print(p, end='')
print_buf = []
orig_print(indent * ' ', end='')
orig_print(*args, **kwargs)
print = my_print
class Node:
def __init__(self, node):
self.props = {}
for i in range(len(node)):
el = node[i]
if type(el) == FdtNode:
continue
self.props[el.get_name()] = el
if 'phandle' in self.props:
self.phandle = self.props['phandle'].words[0]
else:
self.phandle = None
def __getitem__(self, val):
return self.props[val]
def blob_to_dict(blob):
ret = {}
phandles = {}
for (path, node) in blob.resolve_path('/').walk():
if type(node) != FdtNode:
continue
ret[path] = Node(node)
if ret[path].phandle is not None:
phandles[ret[path].phandle] = path
return (ret, phandles)
def check_is_phandle_equiv(v1, v2):
global phandles1, phandles2, max_ph1, max_ph2
if v1 > max_ph1 or v2 > max_ph2:
return False
if v1 not in phandles1 or v2 not in phandles2:
return False
if phandles1[v1] == phandles2[v2]:
return True
return False
def diff_props(prop1, prop2):
global indent
if type(prop1) != type(prop2):
return 'different type (<< %s / >> %s)'%(str(type(prop1)), str(type(prop2)))
has_diffs = False
if type(prop1) == FdtPropertyStrings:
i = 0
while i < len(prop1.strings) and i < len(prop2.strings):
if prop1.strings[i] != prop2.strings[i]:
has_diffs = True
print('%d: << "%s", >> "%s"'%(i, prop1.strings[i], prop2.strings[i]))
i += 1
if i < len(prop1.strings):
has_diffs = True
print('%d extra elements for <<: "%s"'%(len(prop1.strings) - len(prop2.strings),
'", "'.join(prop1.strings[i:])))
elif i < len(prop2.strings):
has_diffs = True
print('%d extra elements for >>: "%s"'%(len(prop2.strings) - len(prop1.strings),
'", "'.join(prop2.strings[i:])))
elif type(prop1) == FdtPropertyBytes:
i = 0
while i < len(prop1.bytes) and i < len(prop2.bytes):
if prop1.bytes[i] != prop2.bytes[i]:
has_diffs = True
print('%d: << %#x, >> %#x'%(i, prop1.bytes[i], prop2.bytes[i]))
i += 1
if i < len(prop1.bytes):
has_diffs = True
print('%d extra elements for <<: "%s"'%(len(prop1.bytes) - len(prop2.bytes),
'", "'.join(map(lambda a: hex(ord(a)), prop1.bytes[i:]))))
elif i < len(prop2.bytes):
has_diffs = True
print('%d extra elements for >>: "%s"'%(len(prop2.bytes) - len(prop1.bytes),
'", "'.join(map(lambda a: hex(ord(a)), prop2.bytes[i:]))))
elif type(prop1) == FdtPropertyWords:
i = 0
while i < len(prop1.words) and i < len(prop2.words):
if prop1.words[i] != prop2.words[i] and not \
check_is_phandle_equiv(prop1.words[i], prop2.words[i]):
has_diffs = True
print('%d: << %#x, >> %#x'%(i, prop1.words[i], prop2.words[i]))
i += 1
if i < len(prop1.words):
has_diffs = True
print('%d extra elements for <<: "%s"'%(len(prop1.words) - len(prop2.words),
'", "'.join(map(lambda a: hex(a), prop1.words[i:]))))
elif i < len(prop2.words):
has_diffs = True
print('%d extra elements for >>: "%s"'%(len(prop2.words) - len(prop1.words),
'", "'.join(map(lambda a: hex(a), prop2.words[i:]))))
if has_diffs:
return
return True
def diff_nodes(node1, node2):
global indent, print_buf
props = set(node1.props.keys())
props.union(set(node2.props.keys()))
for p in sorted(props):
if p in node1.props and p in node2.props:
print_buf.append((indent * ' ') + p + ':\n')
indent += 1
diff_props(node1.props[p], node2.props[p])
indent -= 1
if len(print_buf) > 0:
print_buf.pop()
elif p in node1.props:
print(p + ': only <<')
else:
print(p + ': only >>')
def diff_tree(tree1, tree2):
global indent, print_buf
paths = set(tree1.keys())
paths.union(set(tree2.keys()))
for p in sorted(paths):
indent = p.count('/') - 1
if p in tree1 and p in tree2:
print_buf.append((indent * ' ') + p + ':\n')
indent += 1
diff_nodes(tree1[p], tree2[p])
indent -= 1
if len(print_buf) > 0:
print_buf.pop()
elif p in tree1:
print(p, 'only in <<')
else:
print(p, 'only in >>')
(nodes1, phandles1) = blob_to_dict(blob1)
(nodes2, phandles2) = blob_to_dict(blob2)
max_ph1 = max(phandles1.keys())
max_ph2 = max(phandles2.keys())
diff_tree(nodes1, nodes2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment