From a64b944942d828fe98e4843929662aad7f47bcca Mon Sep 17 00:00:00 2001 From: "Chen, Christine" Date: Thu, 28 Apr 2022 20:49:37 +0800 Subject: BaseTools: Add FMMT Python Tool The FMMT python tool is used for firmware files operation, which has the Fv/FFs-based 'View'&'Add'&'Delete'&'Replace' operation function: 1.Parse a FD(Firmware Device) / FV(Firmware Volume) / FFS(Firmware Files) 2.Add a new FFS into a FV file (both included in a FD file or not) 3.Replace an FFS in a FV file with a new FFS file 4.Delete an FFS in a FV file (both included in a FD file or not) 5.Extract the FFS from a FV file (both included in a FD file or not) This version of FMMT Python tool does not support PEIM rebase feature, this feature will be added in future update. Currently the FMMT C tool is saved in edk2-staging repo, but its quality and coding style can't meet the Edk2 quality, which is hard to maintain (Hard/Duplicate Code; Regression bugs; Restrict usage). The new Python version keeps same functions with origin C version. It has higher quality and better coding style, and it is much easier to extend new functions and to maintain. REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1847 RFC Link: https://edk2.groups.io/g/devel/message/82877 Staging Link: https://github.com/tianocore/edk2-staging/tree/PyFMMT Cc: Bob Feng Cc: Liming Gao Signed-off-by: Yuwei Chen Reviewed-by: Bob Feng Acked-by: Liming Gao --- BaseTools/Source/Python/FMMT/core/BiosTree.py | 198 ++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 BaseTools/Source/Python/FMMT/core/BiosTree.py (limited to 'BaseTools/Source/Python/FMMT/core/BiosTree.py') diff --git a/BaseTools/Source/Python/FMMT/core/BiosTree.py b/BaseTools/Source/Python/FMMT/core/BiosTree.py new file mode 100644 index 0000000000..d8fa474335 --- /dev/null +++ b/BaseTools/Source/Python/FMMT/core/BiosTree.py @@ -0,0 +1,198 @@ +## @file +# This file is used to define the Bios layout tree structure and related operations. +# +# Copyright (c) 2021-, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## +import collections +from FirmwareStorageFormat.Common import * +from utils.FmmtLogger import FmmtLogger as logger + +ROOT_TREE = 'ROOT' +ROOT_FV_TREE = 'ROOT_FV_TREE' +ROOT_FFS_TREE = 'ROOT_FFS_TREE' +ROOT_SECTION_TREE = 'ROOT_SECTION_TREE' + +FV_TREE = 'FV' +DATA_FV_TREE = 'DATA_FV' +FFS_TREE = 'FFS' +FFS_PAD = 'FFS_PAD' +FFS_FREE_SPACE = 'FFS_FREE_SPACE' +SECTION_TREE = 'SECTION' +SEC_FV_TREE = 'SEC_FV_IMAGE' +BINARY_DATA = 'BINARY' + +RootType = [ROOT_TREE, ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE] +FvType = [FV_TREE, SEC_FV_TREE] +FfsType = FFS_TREE +SecType = SECTION_TREE + +class BIOSTREE: + def __init__(self, NodeName: str) -> None: + self.key = NodeName + self.type = None + self.Data = None + self.Child = [] + self.Findlist = [] + self.Parent = None + self.NextRel = None + self.LastRel = None + + def HasChild(self) -> bool: + if self.Child == []: + return False + else: + return True + + def isFinalChild(self) -> bool: + ParTree = self.Parent + if ParTree: + if ParTree.Child[-1] == self: + return True + return False + + # FvTree.insertChild() + def insertChild(self, newNode, pos: int=None) -> None: + if len(self.Child) == 0: + self.Child.append(newNode) + else: + if not pos: + LastTree = self.Child[-1] + self.Child.append(newNode) + LastTree.NextRel = newNode + newNode.LastRel = LastTree + else: + newNode.NextRel = self.Child[pos-1].NextRel + newNode.LastRel = self.Child[pos].LastRel + self.Child[pos-1].NextRel = newNode + self.Child[pos].LastRel = newNode + self.Child.insert(pos, newNode) + newNode.Parent = self + + # lastNode.insertRel(newNode) + def insertRel(self, newNode) -> None: + if self.Parent: + parentTree = self.Parent + new_index = parentTree.Child.index(self) + 1 + parentTree.Child.insert(new_index, newNode) + self.NextRel = newNode + newNode.LastRel = self + + def deleteNode(self, deletekey: str) -> None: + FindStatus, DeleteTree = self.FindNode(deletekey) + if FindStatus: + parentTree = DeleteTree.Parent + lastTree = DeleteTree.LastRel + nextTree = DeleteTree.NextRel + if parentTree: + index = parentTree.Child.index(DeleteTree) + del parentTree.Child[index] + if lastTree and nextTree: + lastTree.NextRel = nextTree + nextTree.LastRel = lastTree + elif lastTree: + lastTree.NextRel = None + elif nextTree: + nextTree.LastRel = None + return DeleteTree + else: + logger.error('Could not find the target tree') + return None + + def FindNode(self, key: str, Findlist: list) -> None: + if self.key == key or (self.Data and self.Data.Name == key) or (self.type == FFS_TREE and self.Data.UiName == key): + Findlist.append(self) + for item in self.Child: + item.FindNode(key, Findlist) + + def GetTreePath(self): + BiosTreePath = [self] + while self.Parent: + BiosTreePath.insert(0, self.Parent) + self = self.Parent + return BiosTreePath + + def parserTree(self, TargetDict: dict=None, Info: list=None, space: int=0, ParFvId="") -> None: + Key = list(TargetDict.keys())[0] + if TargetDict[Key]["Type"] in RootType: + Info.append("Image File: {}".format(Key)) + Info.append("FilesNum: {}".format(TargetDict.get(Key).get('FilesNum'))) + Info.append("\n") + elif TargetDict[Key]["Type"] in FvType: + space += 2 + if TargetDict[Key]["Type"] == SEC_FV_TREE: + Info.append("{}Child FV named {} of {}".format(space*" ", Key, ParFvId)) + space += 2 + else: + Info.append("FvId: {}".format(Key)) + ParFvId = Key + Info.append("{}FvNameGuid: {}".format(space*" ", TargetDict.get(Key).get('FvNameGuid'))) + Info.append("{}Attributes: {}".format(space*" ", TargetDict.get(Key).get('Attributes'))) + Info.append("{}Total Volume Size: {}".format(space*" ", TargetDict.get(Key).get('Size'))) + Info.append("{}Free Volume Size: {}".format(space*" ", TargetDict.get(Key).get('FreeSize'))) + Info.append("{}Volume Offset: {}".format(space*" ", TargetDict.get(Key).get('Offset'))) + Info.append("{}FilesNum: {}".format(space*" ", TargetDict.get(Key).get('FilesNum'))) + elif TargetDict[Key]["Type"] in FfsType: + space += 2 + if TargetDict.get(Key).get('UiName') != "b''": + Info.append("{}File: {} / {}".format(space*" ", Key, TargetDict.get(Key).get('UiName'))) + else: + Info.append("{}File: {}".format(space*" ", Key)) + if "Files" in list(TargetDict[Key].keys()): + for item in TargetDict[Key]["Files"]: + self.parserTree(item, Info, space, ParFvId) + + def ExportTree(self,TreeInfo: dict=None) -> dict: + if TreeInfo is None: + TreeInfo =collections.OrderedDict() + + if self.type == ROOT_TREE or self.type == ROOT_FV_TREE or self.type == ROOT_FFS_TREE or self.type == ROOT_SECTION_TREE: + key = str(self.key) + TreeInfo[self.key] = collections.OrderedDict() + TreeInfo[self.key]["Name"] = key + TreeInfo[self.key]["Type"] = self.type + TreeInfo[self.key]["FilesNum"] = len(self.Child) + elif self.type == FV_TREE or self.type == SEC_FV_TREE: + key = str(self.Data.FvId) + TreeInfo[key] = collections.OrderedDict() + TreeInfo[key]["Name"] = key + if self.Data.FvId != self.Data.Name: + TreeInfo[key]["FvNameGuid"] = str(self.Data.Name) + TreeInfo[key]["Type"] = self.type + TreeInfo[key]["Attributes"] = hex(self.Data.Header.Attributes) + TreeInfo[key]["Size"] = hex(self.Data.Header.FvLength) + TreeInfo[key]["FreeSize"] = hex(self.Data.Free_Space) + TreeInfo[key]["Offset"] = hex(self.Data.HOffset) + TreeInfo[key]["FilesNum"] = len(self.Child) + elif self.type == FFS_TREE: + key = str(self.Data.Name) + TreeInfo[key] = collections.OrderedDict() + TreeInfo[key]["Name"] = key + TreeInfo[key]["UiName"] = '{}'.format(self.Data.UiName) + TreeInfo[key]["Version"] = '{}'.format(self.Data.Version) + TreeInfo[key]["Type"] = self.type + TreeInfo[key]["Size"] = hex(self.Data.Size) + TreeInfo[key]["Offset"] = hex(self.Data.HOffset) + TreeInfo[key]["FilesNum"] = len(self.Child) + elif self.type == SECTION_TREE and self.Data.Type == 0x02: + key = str(self.Data.Name) + TreeInfo[key] = collections.OrderedDict() + TreeInfo[key]["Name"] = key + TreeInfo[key]["Type"] = self.type + TreeInfo[key]["Size"] = hex(len(self.Data.OriData) + self.Data.HeaderLength) + TreeInfo[key]["DecompressedSize"] = hex(self.Data.Size) + TreeInfo[key]["Offset"] = hex(self.Data.HOffset) + TreeInfo[key]["FilesNum"] = len(self.Child) + elif self is not None: + key = str(self.Data.Name) + TreeInfo[key] = collections.OrderedDict() + TreeInfo[key]["Name"] = key + TreeInfo[key]["Type"] = self.type + TreeInfo[key]["Size"] = hex(self.Data.Size) + TreeInfo[key]["Offset"] = hex(self.Data.HOffset) + TreeInfo[key]["FilesNum"] = len(self.Child) + + for item in self.Child: + TreeInfo[key].setdefault('Files',[]).append( item.ExportTree()) + + return TreeInfo \ No newline at end of file -- cgit v1.2.3