merge_dtbs: Use logging and argparse

Use the logging module to log important information to various log
levels: info, debug, warning and error.
- Modify __str__ representations of classes DeviceTree and
  InnerMergedDeviceTree for readability in debug logs
- Make parse_dt_files() return instead of yield so as to order all the
  filename parsing at one go after the preceding log messages

Use the argparse module to specify script arguments cleanly. Also make
the corresponding change to merge_dtbs.sh to invoke this script with the
correct flags.

Change-Id: Id09d9d7b34cde60eff99876f0c0c4f6643852aee
Signed-off-by: Guru Das Srinagesh <quic_gurus@quicinc.com>
This commit is contained in:
Guru Das Srinagesh
2023-02-24 18:30:54 -08:00
committed by dianlujitao
parent bd92c42017
commit 81c8cad9e8
2 changed files with 52 additions and 28 deletions

View File

@@ -638,7 +638,7 @@ ifeq ($(BOARD_USES_QCOM_MERGE_DTBS_SCRIPT),true)
$(hide) find $(DTBS_BASE) -type f -name "*.dtb*" | xargs rm -f
$(hide) find $(DTBS_OUT) -type f -name "*.dtb*" | xargs rm -f
mv $(DTB_OUT)/arch/$(KERNEL_ARCH)/boot/dts/vendor/*/*.dtb $(DTB_OUT)/arch/$(KERNEL_ARCH)/boot/dts/vendor/*/*.dtbo $(DTBS_BASE)/
PATH=$(abspath $(HOST_OUT_EXECUTABLES)):$${PATH} python3 $(BUILD_TOP)/vendor/lineage/build/tools/merge_dtbs.py $(DTBS_BASE) $(DTB_OUT)/arch/$(KERNEL_ARCH)/boot/dts/vendor/qcom $(DTBS_OUT)
PATH=$(abspath $(HOST_OUT_EXECUTABLES)):$${PATH} python3 $(BUILD_TOP)/vendor/lineage/build/tools/merge_dtbs.py --base $(DTBS_BASE) --techpack $(DTB_OUT)/arch/$(KERNEL_ARCH)/boot/dts/vendor/qcom --out $(DTBS_OUT)
cat $(shell find $(DTB_OUT)/out -type f -name "${TARGET_MERGE_DTBS_WILDCARD}.dtb" | sort) > $@
else
cat $(shell find $(DTB_OUT)/arch/$(KERNEL_ARCH)/boot/dts -type f -name "*.dtb" | sort) > $@

View File

@@ -1,6 +1,7 @@
#! /usr/bin/env python3
# Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -33,6 +34,8 @@ import sys
import subprocess
import shutil
from itertools import product, combinations_with_replacement, chain
import logging
import argparse
def split_array(array, cells):
"""
@@ -219,6 +222,7 @@ class DeviceTreeInfo(object):
class DeviceTree(DeviceTreeInfo):
def __init__(self, filename):
self.filename = filename
logging.debug('Initializing new DeviceTree: {}'.format(os.path.basename(filename)))
msm_id = split_array(self.get_prop('/', 'qcom,msm-id', check_output=False), 2)
board_id = split_array(self.get_prop('/', 'qcom,board-id', check_output=False), 2)
# default pmic-id-size is 4
@@ -227,7 +231,7 @@ class DeviceTree(DeviceTreeInfo):
super().__init__(msm_id, board_id, pmic_id)
if not self.has_any_properties():
print('WARNING! {} has no properties and may match with any other devicetree'.format(os.path.basename(self.filename)))
logging.warning('{} has no properties and may match with any other devicetree'.format(os.path.basename(self.filename)))
def get_prop(self, node, property, prop_type='i', check_output=True):
r = subprocess.run(["fdtget", "-t", prop_type, self.filename, node, property],
@@ -252,7 +256,7 @@ class DeviceTree(DeviceTreeInfo):
return out
def __str__(self):
return "{} [{}]".format(super().__str__(), os.path.basename(self.filename))
return "[{}] {}".format(os.path.basename(self.filename), super().__str__())
class InnerMergedDeviceTree(DeviceTreeInfo):
"""
@@ -262,6 +266,7 @@ class InnerMergedDeviceTree(DeviceTreeInfo):
"""
def __init__(self, filename, plat_id, board_id, pmic_id, techpacks=None):
self.base = filename
# All inner merged device trees start with zero techpacks
self.techpacks = techpacks or []
super().__init__(plat_id, board_id, pmic_id)
@@ -271,6 +276,7 @@ class InnerMergedDeviceTree(DeviceTreeInfo):
intersection = techpack & self
if intersection in self:
self.techpacks.append(intersection)
logging.debug('Appended after intersection: {}'.format(repr(self)))
return True
return False
@@ -278,12 +284,13 @@ class InnerMergedDeviceTree(DeviceTreeInfo):
if name is None:
name = self.get_name()
logging.info('Saving to: {}'.format(name))
out_file = os.path.join(out_dir, name)
ext = os.path.splitext(os.path.basename(self.base))[1]
# This check might fail in future if we get into an edge case
# when splitting the base devicetree into multiple merged DTs
assert not os.path.exists(out_file)
assert not os.path.exists(out_file), "Cannot overwrite: {}".format(out_file)
if len(self.techpacks) == 0:
cmd = ['cp', self.base, out_file]
@@ -296,25 +303,25 @@ class InnerMergedDeviceTree(DeviceTreeInfo):
cmd.extend([tp.filename for tp in self.techpacks])
cmd.extend(['-o', out_file])
print(' {}'.format(' '.join(cmd)))
logging.debug(' {}'.format(' '.join(cmd)))
subprocess.run(cmd, check=True)
if self.plat_id:
plat_iter = self.plat_id if isinstance(self.plat_id, tuple) else chain.from_iterable(self.plat_id)
cmd = ['fdtput', '-t', 'i', out_file, '/', 'qcom,msm-id'] + list(map(str, plat_iter))
print(' {}'.format(' '.join(cmd)))
logging.debug(' {}'.format(' '.join(cmd)))
subprocess.run(cmd, check=True)
if self.board_id:
board_iter = self.board_id if isinstance(self.board_id, tuple) else chain.from_iterable(self.board_id)
cmd = ['fdtput', '-t', 'i', out_file, '/', 'qcom,board-id'] + list(map(str, board_iter))
print(' {}'.format(' '.join(cmd)))
logging.debug(' {}'.format(' '.join(cmd)))
subprocess.run(cmd, check=True)
if self.pmic_id:
pmic_iter = self.pmic_id if isinstance(self.pmic_id, tuple) else chain.from_iterable(self.pmic_id)
cmd = ['fdtput', '-t', 'i', out_file, '/', 'qcom,pmic-id'] + list(map(str, pmic_iter))
print(' {}'.format(' '.join(cmd)))
logging.debug(' {}'.format(' '.join(cmd)))
subprocess.run(cmd, check=True)
return DeviceTree(out_file)
@@ -332,15 +339,16 @@ class InnerMergedDeviceTree(DeviceTreeInfo):
return [part for part in parts if part not in ignored_parts]
def __str__(self):
return "{} [{} + {{{}}}]".format(super().__str__(), os.path.basename(self.base), " ".join(os.path.basename(t.filename) for t in self.techpacks))
return "[{} + {{{}}}] {}".format(os.path.basename(self.base), " ".join(os.path.basename(t.filename) for t in self.techpacks), super().__str__())
class MergedDeviceTree(object):
def __init__(self, other):
self.merged_devicetrees = {InnerMergedDeviceTree(other.filename, other.plat_id, other.board_id, other.pmic_id)}
def try_add(self, techpack):
def merged_dt_try_add(self, techpack):
did_add = False
for mdt in self.merged_devicetrees.copy():
logging.debug('{}'.format(repr(mdt)))
# techpack and kernel devicetree need only to overlap in order to merge,
# and not match exactly. Think: venn diagram.
# Need 2 things: The devicetree part that applies to
@@ -354,14 +362,16 @@ class MergedDeviceTree(object):
intersection = techpack & mdt
if intersection not in mdt:
continue
logging.debug('Found intersection!')
# mdt may apply to a superset of devices the techpack DT applies to
# (mdt - intersection) splits mdt into appropriate number of devicetrees
# such that we can apply techpack onto one of the resulting DTs in the
# difference
difference = mdt - intersection
if len(difference) > 1:
print('Splitting {}'.format(mdt))
print(' because {}'.format(techpack))
logging.debug('Splitting {}'.format(mdt))
logging.debug('because {}'.format(techpack))
self.merged_devicetrees.remove(mdt)
self.merged_devicetrees.update(difference)
@@ -378,47 +388,61 @@ class MergedDeviceTree(object):
else:
name = None
for mdt in self.merged_devicetrees:
print()
yield mdt.save(name, out_dir)
def parse_dt_files(dt_folder):
devicetrees = []
for root, dirs, files in os.walk(dt_folder):
for filename in files:
if os.path.splitext(filename)[1] not in ['.dtb', '.dtbo']:
continue
filepath = os.path.join(root, filename)
yield DeviceTree(filepath)
devicetrees.append(DeviceTree(filepath))
return devicetrees
def main():
if len(sys.argv) != 4:
print("Usage: {} <base dtb folder> <techpack dtb folder> <output folder>"
.format(sys.argv[0]))
sys.exit(1)
parser = argparse.ArgumentParser(description='Merge devicetree blobs of techpacks with Kernel Platform SoCs')
parser.add_argument('-b', '--base', required=True, help="Folder containing base DTBs from Kernel Platform output")
parser.add_argument('-t', '--techpack', required=True, help="Folder containing techpack DLKM DTBOs")
parser.add_argument('-o', '--out', required=True, help="Output folder where merged DTBs will be saved")
parser.add_argument('--loglevel', choices=['debug', 'info', 'warn', 'error'], default='info', help="Set loglevel to see debug messages")
args = parser.parse_args()
logging.basicConfig(level=args.loglevel.upper(), format='%(levelname)s: %(message)s'.format(os.path.basename(sys.argv[0])))
# 1. Parse the devicetrees -- extract the device info (msm-id, board-id, pmic-id)
bases = parse_dt_files(sys.argv[1])
techpacks = parse_dt_files(sys.argv[2])
logging.info('Parsing base dtb files from {}'.format(args.base))
bases = parse_dt_files(args.base)
all_bases = '\n'.join(list(map(lambda x: str(x), bases)))
logging.info('Parsed bases: \n{}'.format(all_bases))
logging.info('Parsing techpack dtb files from {}'.format(args.techpack))
techpacks = parse_dt_files(args.techpack)
all_techpacks = '\n'.join(list(map(lambda x: str(x), techpacks)))
logging.info('Parsed techpacks: \n{}'.format(all_techpacks))
# 2.1: Create an intermediate representation of the merged devicetrees, starting with the base
merged_devicetrees = list(map(lambda dt: MergedDeviceTree(dt), bases))
# 2.2: Try to add techpack devicetree to each base DT
for techpack in techpacks:
logging.debug('Trying to add techpack: {}'.format(techpack))
did_add = False
for dt in merged_devicetrees:
if dt.try_add(techpack):
if dt.merged_dt_try_add(techpack):
did_add = True
if not did_add:
print('WARNING! Could not apply {} to any devicetrees'.format(techpack))
logging.warning('Could not apply {} to any devicetrees'.format(techpack))
final_inner_merged_dts = '\n'.join(list(str(mdt.merged_devicetrees) for mdt in merged_devicetrees))
logging.info('Final Inner Merged Devicetrees: \n{}'.format(final_inner_merged_dts))
print()
print('==================================')
created = []
# 3. Save the deviectrees to real files
for dt in merged_devicetrees:
created.extend(dt.save(sys.argv[3]))
created.extend(dt.save(args.out))
print()
print('==================================')
# 4. Try to apply merged DTBOs onto merged DTBs, when appropriate
# This checks that DTBOs and DTBs generated by merge_dtbs.py can be merged by bootloader
# at runtime.
@@ -428,7 +452,7 @@ def main():
# See DeviceTreeInfo.__gt__; this checks whether dtbo is more specific than the base
if dtbo > base:
cmd = ['ufdt_apply_overlay', base.filename, dtbo.filename, '/dev/null']
print(' '.join(cmd))
logging.debug(' '.join(cmd))
subprocess.run(cmd, check=True)