--- /dev/null
+## @file\r
+# This file is used to implement of the various bianry parser.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from re import T\r
+import copy\r
+import os\r
+import sys\r
+from FirmwareStorageFormat.Common import *\r
+from core.BiosTreeNode import *\r
+from core.BiosTree import *\r
+from core.GuidTools import GUIDTools\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+\r
+ROOT_TREE = 'ROOT'\r
+ROOT_FV_TREE = 'ROOT_FV_TREE'\r
+ROOT_FFS_TREE = 'ROOT_FFS_TREE'\r
+ROOT_SECTION_TREE = 'ROOT_SECTION_TREE'\r
+\r
+FV_TREE = 'FV'\r
+DATA_FV_TREE = 'DATA_FV'\r
+FFS_TREE = 'FFS'\r
+FFS_PAD = 'FFS_PAD'\r
+FFS_FREE_SPACE = 'FFS_FREE_SPACE'\r
+SECTION_TREE = 'SECTION'\r
+SEC_FV_TREE = 'SEC_FV_IMAGE'\r
+BINARY_DATA = 'BINARY'\r
+Fv_count = 0\r
+\r
+## Abstract factory\r
+class BinaryFactory():\r
+ type:list = []\r
+\r
+ def Create_Product():\r
+ pass\r
+\r
+class BinaryProduct():\r
+ ## Use GuidTool to decompress data.\r
+ def DeCompressData(self, GuidTool, Section_Data: bytes, FileName) -> bytes:\r
+ guidtool = GUIDTools().__getitem__(struct2stream(GuidTool))\r
+ if not guidtool.ifexist:\r
+ logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, FileName))\r
+ raise Exception("Process Failed: GuidTool not found!")\r
+ DecompressedData = guidtool.unpack(Section_Data)\r
+ return DecompressedData\r
+\r
+ def ParserData():\r
+ pass\r
+\r
+class SectionFactory(BinaryFactory):\r
+ type = [SECTION_TREE]\r
+\r
+ def Create_Product():\r
+ return SectionProduct()\r
+\r
+class FfsFactory(BinaryFactory):\r
+ type = [ROOT_SECTION_TREE, FFS_TREE]\r
+\r
+ def Create_Product():\r
+ return FfsProduct()\r
+\r
+class FvFactory(BinaryFactory):\r
+ type = [ROOT_FFS_TREE, FV_TREE, SEC_FV_TREE]\r
+\r
+ def Create_Product():\r
+ return FvProduct()\r
+\r
+class FdFactory(BinaryFactory):\r
+ type = [ROOT_FV_TREE, ROOT_TREE]\r
+\r
+ def Create_Product():\r
+ return FdProduct()\r
+\r
+class SectionProduct(BinaryProduct):\r
+ ## Decompress the compressed section.\r
+ def ParserData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:\r
+ if Section_Tree.Data.Type == 0x01:\r
+ Section_Tree.Data.OriData = Section_Tree.Data.Data\r
+ self.ParserSection(Section_Tree, b'')\r
+ # Guided Define Section\r
+ elif Section_Tree.Data.Type == 0x02:\r
+ Section_Tree.Data.OriData = Section_Tree.Data.Data\r
+ DeCompressGuidTool = Section_Tree.Data.ExtHeader.SectionDefinitionGuid\r
+ Section_Tree.Data.Data = self.DeCompressData(DeCompressGuidTool, Section_Tree.Data.Data, Section_Tree.Parent.Data.Name)\r
+ Section_Tree.Data.Size = len(Section_Tree.Data.Data) + Section_Tree.Data.HeaderLength\r
+ self.ParserSection(Section_Tree, b'')\r
+ elif Section_Tree.Data.Type == 0x03:\r
+ Section_Tree.Data.OriData = Section_Tree.Data.Data\r
+ self.ParserSection(Section_Tree, b'')\r
+ # SEC_FV Section\r
+ elif Section_Tree.Data.Type == 0x17:\r
+ global Fv_count\r
+ Sec_Fv_Info = FvNode(Fv_count, Section_Tree.Data.Data)\r
+ Sec_Fv_Tree = BIOSTREE('FV'+ str(Fv_count))\r
+ Sec_Fv_Tree.type = SEC_FV_TREE\r
+ Sec_Fv_Tree.Data = Sec_Fv_Info\r
+ Sec_Fv_Tree.Data.HOffset = Section_Tree.Data.DOffset\r
+ Sec_Fv_Tree.Data.DOffset = Sec_Fv_Tree.Data.HOffset + Sec_Fv_Tree.Data.Header.HeaderLength\r
+ Sec_Fv_Tree.Data.Data = Section_Tree.Data.Data[Sec_Fv_Tree.Data.Header.HeaderLength:]\r
+ Section_Tree.insertChild(Sec_Fv_Tree)\r
+ Fv_count += 1\r
+\r
+ def ParserSection(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:\r
+ Rel_Offset = 0\r
+ Section_Offset = 0\r
+ # Get the Data from parent tree, if do not have the tree then get it from the whole_data.\r
+ if ParTree.Data != None:\r
+ Data_Size = len(ParTree.Data.Data)\r
+ Section_Offset = ParTree.Data.DOffset\r
+ Whole_Data = ParTree.Data.Data\r
+ else:\r
+ Data_Size = len(Whole_Data)\r
+ # Parser all the data to collect all the Section recorded in its Parent Section.\r
+ while Rel_Offset < Data_Size:\r
+ # Create a SectionNode and set it as the SectionTree's Data\r
+ Section_Info = SectionNode(Whole_Data[Rel_Offset:])\r
+ Section_Tree = BIOSTREE(Section_Info.Name)\r
+ Section_Tree.type = SECTION_TREE\r
+ Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.HeaderLength: Rel_Offset+Section_Info.Size]\r
+ Section_Info.DOffset = Section_Offset + Section_Info.HeaderLength + Rel_Whole_Offset\r
+ Section_Info.HOffset = Section_Offset + Rel_Whole_Offset\r
+ Section_Info.ROffset = Rel_Offset\r
+ if Section_Info.Header.Type == 0:\r
+ break\r
+ # The final Section in parent Section does not need to add padding, else must be 4-bytes align with parent Section start offset\r
+ Pad_Size = 0\r
+ if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Data) != Data_Size):\r
+ Pad_Size = GetPadSize(Section_Info.Size, SECTION_COMMON_ALIGNMENT)\r
+ Section_Info.PadData = Pad_Size * b'\x00'\r
+ if Section_Info.Header.Type == 0x02:\r
+ Section_Info.DOffset = Section_Offset + Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset\r
+ Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: Rel_Offset+Section_Info.Size]\r
+ if Section_Info.Header.Type == 0x14:\r
+ ParTree.Data.Version = Section_Info.ExtHeader.GetVersionString()\r
+ if Section_Info.Header.Type == 0x15:\r
+ ParTree.Data.UiName = Section_Info.ExtHeader.GetUiString()\r
+ if Section_Info.Header.Type == 0x19:\r
+ if Section_Info.Data.replace(b'\x00', b'') == b'':\r
+ Section_Info.IsPadSection = True\r
+ Section_Offset += Section_Info.Size + Pad_Size\r
+ Rel_Offset += Section_Info.Size + Pad_Size\r
+ Section_Tree.Data = Section_Info\r
+ ParTree.insertChild(Section_Tree)\r
+\r
+class FfsProduct(BinaryProduct):\r
+ # ParserFFs / GetSection\r
+ def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:\r
+ Rel_Offset = 0\r
+ Section_Offset = 0\r
+ # Get the Data from parent tree, if do not have the tree then get it from the whole_data.\r
+ if ParTree.Data != None:\r
+ Data_Size = len(ParTree.Data.Data)\r
+ Section_Offset = ParTree.Data.DOffset\r
+ Whole_Data = ParTree.Data.Data\r
+ else:\r
+ Data_Size = len(Whole_Data)\r
+ # Parser all the data to collect all the Section recorded in Ffs.\r
+ while Rel_Offset < Data_Size:\r
+ # Create a SectionNode and set it as the SectionTree's Data\r
+ Section_Info = SectionNode(Whole_Data[Rel_Offset:])\r
+ Section_Tree = BIOSTREE(Section_Info.Name)\r
+ Section_Tree.type = SECTION_TREE\r
+ Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.HeaderLength: Rel_Offset+Section_Info.Size]\r
+ Section_Info.DOffset = Section_Offset + Section_Info.HeaderLength + Rel_Whole_Offset\r
+ Section_Info.HOffset = Section_Offset + Rel_Whole_Offset\r
+ Section_Info.ROffset = Rel_Offset\r
+ if Section_Info.Header.Type == 0:\r
+ break\r
+ # The final Section in Ffs does not need to add padding, else must be 4-bytes align with Ffs start offset\r
+ Pad_Size = 0\r
+ if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Data) != Data_Size):\r
+ Pad_Size = GetPadSize(Section_Info.Size, SECTION_COMMON_ALIGNMENT)\r
+ Section_Info.PadData = Pad_Size * b'\x00'\r
+ if Section_Info.Header.Type == 0x02:\r
+ Section_Info.DOffset = Section_Offset + Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset\r
+ Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: Rel_Offset+Section_Info.Size]\r
+ # If Section is Version or UI type, it saves the version and UI info of its parent Ffs.\r
+ if Section_Info.Header.Type == 0x14:\r
+ ParTree.Data.Version = Section_Info.ExtHeader.GetVersionString()\r
+ if Section_Info.Header.Type == 0x15:\r
+ ParTree.Data.UiName = Section_Info.ExtHeader.GetUiString()\r
+ if Section_Info.Header.Type == 0x19:\r
+ if Section_Info.Data.replace(b'\x00', b'') == b'':\r
+ Section_Info.IsPadSection = True\r
+ Section_Offset += Section_Info.Size + Pad_Size\r
+ Rel_Offset += Section_Info.Size + Pad_Size\r
+ Section_Tree.Data = Section_Info\r
+ ParTree.insertChild(Section_Tree)\r
+\r
+class FvProduct(BinaryProduct):\r
+ ## ParserFv / GetFfs\r
+ def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:\r
+ Ffs_Offset = 0\r
+ Rel_Offset = 0\r
+ # Get the Data from parent tree, if do not have the tree then get it from the whole_data.\r
+ if ParTree.Data != None:\r
+ Data_Size = len(ParTree.Data.Data)\r
+ Ffs_Offset = ParTree.Data.DOffset\r
+ Whole_Data = ParTree.Data.Data\r
+ else:\r
+ Data_Size = len(Whole_Data)\r
+ # Parser all the data to collect all the Ffs recorded in Fv.\r
+ while Rel_Offset < Data_Size:\r
+ # Create a FfsNode and set it as the FFsTree's Data\r
+ if Data_Size - Rel_Offset < 24:\r
+ Ffs_Tree = BIOSTREE('Free_Space')\r
+ Ffs_Tree.type = FFS_FREE_SPACE\r
+ Ffs_Tree.Data = FreeSpaceNode(Whole_Data[Rel_Offset:])\r
+ Ffs_Tree.Data.HOffset = Ffs_Offset + Rel_Whole_Offset\r
+ Ffs_Tree.Data.DOffset = Ffs_Tree.Data.HOffset\r
+ ParTree.Data.Free_Space = Data_Size - Rel_Offset\r
+ ParTree.insertChild(Ffs_Tree)\r
+ Rel_Offset = Data_Size\r
+ else:\r
+ Ffs_Info = FfsNode(Whole_Data[Rel_Offset:])\r
+ Ffs_Tree = BIOSTREE(Ffs_Info.Name)\r
+ Ffs_Info.HOffset = Ffs_Offset + Rel_Whole_Offset\r
+ Ffs_Info.DOffset = Ffs_Offset + Ffs_Info.Header.HeaderLength + Rel_Whole_Offset\r
+ Ffs_Info.ROffset = Rel_Offset\r
+ if Ffs_Info.Name == PADVECTOR:\r
+ Ffs_Tree.type = FFS_PAD\r
+ Ffs_Info.Data = Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: Rel_Offset+Ffs_Info.Size]\r
+ Ffs_Info.Size = len(Ffs_Info.Data) + Ffs_Info.Header.HeaderLength\r
+ # if current Ffs is the final ffs of Fv and full of b'\xff', define it with Free_Space\r
+ if struct2stream(Ffs_Info.Header).replace(b'\xff', b'') == b'':\r
+ Ffs_Tree.type = FFS_FREE_SPACE\r
+ Ffs_Info.Data = Whole_Data[Rel_Offset:]\r
+ Ffs_Info.Size = len(Ffs_Info.Data)\r
+ ParTree.Data.Free_Space = Ffs_Info.Size\r
+ else:\r
+ Ffs_Tree.type = FFS_TREE\r
+ Ffs_Info.Data = Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: Rel_Offset+Ffs_Info.Size]\r
+ # The final Ffs in Fv does not need to add padding, else must be 8-bytes align with Fv start offset\r
+ Pad_Size = 0\r
+ if Ffs_Tree.type != FFS_FREE_SPACE and (Rel_Offset+Ffs_Info.Header.HeaderLength+len(Ffs_Info.Data) != Data_Size):\r
+ Pad_Size = GetPadSize(Ffs_Info.Size, FFS_COMMON_ALIGNMENT)\r
+ Ffs_Info.PadData = Pad_Size * b'\xff'\r
+ Ffs_Offset += Ffs_Info.Size + Pad_Size\r
+ Rel_Offset += Ffs_Info.Size + Pad_Size\r
+ Ffs_Tree.Data = Ffs_Info\r
+ ParTree.insertChild(Ffs_Tree)\r
+\r
+class FdProduct(BinaryProduct):\r
+ type = [ROOT_FV_TREE, ROOT_TREE]\r
+\r
+ ## Create DataTree with first level /fv Info, then parser each Fv.\r
+ def ParserData(self, WholeFvTree, whole_data: bytes=b'', offset: int=0) -> None:\r
+ # Get all Fv image in Fd with offset and length\r
+ Fd_Struct = self.GetFvFromFd(whole_data)\r
+ data_size = len(whole_data)\r
+ Binary_count = 0\r
+ global Fv_count\r
+ # If the first Fv image is the Binary Fv, add it into the tree.\r
+ if Fd_Struct[0][1] != 0:\r
+ Binary_node = BIOSTREE('BINARY'+ str(Binary_count))\r
+ Binary_node.type = BINARY_DATA\r
+ Binary_node.Data = BinaryNode(str(Binary_count))\r
+ Binary_node.Data.Data = whole_data[:Fd_Struct[0][1]]\r
+ Binary_node.Data.Size = len(Binary_node.Data.Data)\r
+ Binary_node.Data.HOffset = 0 + offset\r
+ WholeFvTree.insertChild(Binary_node)\r
+ Binary_count += 1\r
+ # Add the first collected Fv image into the tree.\r
+ Cur_node = BIOSTREE(Fd_Struct[0][0]+ str(Fv_count))\r
+ Cur_node.type = Fd_Struct[0][0]\r
+ Cur_node.Data = FvNode(Fv_count, whole_data[Fd_Struct[0][1]:Fd_Struct[0][1]+Fd_Struct[0][2][0]])\r
+ Cur_node.Data.HOffset = Fd_Struct[0][1] + offset\r
+ Cur_node.Data.DOffset = Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength\r
+ Cur_node.Data.Data = whole_data[Fd_Struct[0][1]+Cur_node.Data.Header.HeaderLength:Fd_Struct[0][1]+Cur_node.Data.Size]\r
+ WholeFvTree.insertChild(Cur_node)\r
+ Fv_count += 1\r
+ Fv_num = len(Fd_Struct)\r
+ # Add all the collected Fv image and the Binary Fv image between them into the tree.\r
+ for i in range(Fv_num-1):\r
+ if Fd_Struct[i][1]+Fd_Struct[i][2][0] != Fd_Struct[i+1][1]:\r
+ Binary_node = BIOSTREE('BINARY'+ str(Binary_count))\r
+ Binary_node.type = BINARY_DATA\r
+ Binary_node.Data = BinaryNode(str(Binary_count))\r
+ Binary_node.Data.Data = whole_data[Fd_Struct[i][1]+Fd_Struct[i][2][0]:Fd_Struct[i+1][1]]\r
+ Binary_node.Data.Size = len(Binary_node.Data.Data)\r
+ Binary_node.Data.HOffset = Fd_Struct[i][1]+Fd_Struct[i][2][0] + offset\r
+ WholeFvTree.insertChild(Binary_node)\r
+ Binary_count += 1\r
+ Cur_node = BIOSTREE(Fd_Struct[i+1][0]+ str(Fv_count))\r
+ Cur_node.type = Fd_Struct[i+1][0]\r
+ Cur_node.Data = FvNode(Fv_count, whole_data[Fd_Struct[i+1][1]:Fd_Struct[i+1][1]+Fd_Struct[i+1][2][0]])\r
+ Cur_node.Data.HOffset = Fd_Struct[i+1][1] + offset\r
+ Cur_node.Data.DOffset = Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength\r
+ Cur_node.Data.Data = whole_data[Fd_Struct[i+1][1]+Cur_node.Data.Header.HeaderLength:Fd_Struct[i+1][1]+Cur_node.Data.Size]\r
+ WholeFvTree.insertChild(Cur_node)\r
+ Fv_count += 1\r
+ # If the final Fv image is the Binary Fv, add it into the tree\r
+ if Fd_Struct[-1][1] + Fd_Struct[-1][2][0] != data_size:\r
+ Binary_node = BIOSTREE('BINARY'+ str(Binary_count))\r
+ Binary_node.type = BINARY_DATA\r
+ Binary_node.Data = BinaryNode(str(Binary_count))\r
+ Binary_node.Data.Data = whole_data[Fd_Struct[-1][1]+Fd_Struct[-1][2][0]:]\r
+ Binary_node.Data.Size = len(Binary_node.Data.Data)\r
+ Binary_node.Data.HOffset = Fd_Struct[-1][1]+Fd_Struct[-1][2][0] + offset\r
+ WholeFvTree.insertChild(Binary_node)\r
+ Binary_count += 1\r
+\r
+ ## Get the first level Fv from Fd file.\r
+ def GetFvFromFd(self, whole_data: bytes=b'') -> list:\r
+ Fd_Struct = []\r
+ data_size = len(whole_data)\r
+ cur_index = 0\r
+ # Get all the EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE FV image offset and length.\r
+ while cur_index < data_size:\r
+ if EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE in whole_data[cur_index:]:\r
+ target_index = whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE) + cur_index\r
+ if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:\r
+ Fd_Struct.append([FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])\r
+ cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]\r
+ else:\r
+ cur_index = target_index + 16\r
+ else:\r
+ cur_index = data_size\r
+ cur_index = 0\r
+ # Get all the EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE FV image offset and length.\r
+ while cur_index < data_size:\r
+ if EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE in whole_data[cur_index:]:\r
+ target_index = whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE) + cur_index\r
+ if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:\r
+ Fd_Struct.append([FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])\r
+ cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]\r
+ else:\r
+ cur_index = target_index + 16\r
+ else:\r
+ cur_index = data_size\r
+ cur_index = 0\r
+ # Get all the EFI_SYSTEM_NVDATA_FV_GUID_BYTE FV image offset and length.\r
+ while cur_index < data_size:\r
+ if EFI_SYSTEM_NVDATA_FV_GUID_BYTE in whole_data[cur_index:]:\r
+ target_index = whole_data[cur_index:].index(EFI_SYSTEM_NVDATA_FV_GUID_BYTE) + cur_index\r
+ if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:\r
+ Fd_Struct.append([DATA_FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])\r
+ cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]\r
+ else:\r
+ cur_index = target_index + 16\r
+ else:\r
+ cur_index = data_size\r
+ # Sort all the collect Fv image with offset.\r
+ Fd_Struct.sort(key=lambda x:x[1])\r
+ tmp_struct = copy.deepcopy(Fd_Struct)\r
+ tmp_index = 0\r
+ Fv_num = len(Fd_Struct)\r
+ # Remove the Fv image included in another Fv image.\r
+ for i in range(1,Fv_num):\r
+ if tmp_struct[i][1]+tmp_struct[i][2][0] < tmp_struct[i-1][1]+tmp_struct[i-1][2][0]:\r
+ Fd_Struct.remove(Fd_Struct[i-tmp_index])\r
+ tmp_index += 1\r
+ return Fd_Struct\r
+\r
+class ParserEntry():\r
+ FactoryTable:dict = {\r
+ SECTION_TREE: SectionFactory,\r
+ ROOT_SECTION_TREE: FfsFactory,\r
+ FFS_TREE: FfsFactory,\r
+ ROOT_FFS_TREE: FvFactory,\r
+ FV_TREE: FvFactory,\r
+ SEC_FV_TREE: FvFactory,\r
+ ROOT_FV_TREE: FdFactory,\r
+ ROOT_TREE: FdFactory,\r
+ }\r
+\r
+ def GetTargetFactory(self, Tree_type: str) -> BinaryFactory:\r
+ if Tree_type in self.FactoryTable:\r
+ return self.FactoryTable[Tree_type]\r
+\r
+ def Generate_Product(self, TargetFactory: BinaryFactory, Tree, Data: bytes, Offset: int) -> None:\r
+ New_Product = TargetFactory.Create_Product()\r
+ New_Product.ParserData(Tree, Data, Offset)\r
+\r
+ def DataParser(self, Tree, Data: bytes, Offset: int) -> None:\r
+ TargetFactory = self.GetTargetFactory(Tree.type)\r
+ if TargetFactory:\r
+ self.Generate_Product(TargetFactory, Tree, Data, Offset)\r
\ No newline at end of file