]> git.proxmox.com Git - mirror_edk2.git/commitdiff
BaseTools: Add FMMT Python Tool
authorChen, Christine <Yuwei.Chen@intel.com>
Thu, 28 Apr 2022 12:49:37 +0000 (20:49 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Fri, 6 May 2022 04:22:21 +0000 (04:22 +0000)
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
Link: https://edk2.groups.io/g/devel/message/82877
Link: https://github.com/tianocore/edk2-staging/tree/PyFMMT
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Signed-off-by: Yuwei Chen <yuwei.chen@intel.com>
Reviewed-by: Bob Feng <bob.c.feng@intel.com>
Acked-by: Liming Gao <gaoliming@byosoft.com.cn>
22 files changed:
BaseTools/BinWrappers/PosixLike/FMMT [new file with mode: 0755]
BaseTools/BinWrappers/WindowsLike/FMMT.bat [new file with mode: 0644]
BaseTools/Source/Python/FMMT/FMMT.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/FmmtConf.ini [new file with mode: 0644]
BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png [new file with mode: 0644]
BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png [new file with mode: 0644]
BaseTools/Source/Python/FMMT/README.md [new file with mode: 0644]
BaseTools/Source/Python/FMMT/__init__.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/BiosTree.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/BiosTreeNode.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/FMMTOperation.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/FMMTParser.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/FvHandler.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/core/GuidTools.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/utils/FmmtLogger.py [new file with mode: 0644]
BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py [new file with mode: 0644]
BaseTools/Source/Python/FirmwareStorageFormat/Common.py [new file with mode: 0644]
BaseTools/Source/Python/FirmwareStorageFormat/FfsFileHeader.py [new file with mode: 0644]
BaseTools/Source/Python/FirmwareStorageFormat/FvHeader.py [new file with mode: 0644]
BaseTools/Source/Python/FirmwareStorageFormat/SectionHeader.py [new file with mode: 0644]
BaseTools/Source/Python/FirmwareStorageFormat/__init__.py [new file with mode: 0644]

diff --git a/BaseTools/BinWrappers/PosixLike/FMMT b/BaseTools/BinWrappers/PosixLike/FMMT
new file mode 100755 (executable)
index 0000000..86b2c65
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#python `dirname $0`/RunToolFromSource.py `basename $0` $*
+
+# If a ${PYTHON_COMMAND} command is available, use it in preference to python
+if command -v ${PYTHON_COMMAND} >/dev/null 2>&1; then
+    python_exe=${PYTHON_COMMAND}
+fi
+
+full_cmd=${BASH_SOURCE:-$0} # see http://mywiki.wooledge.org/BashFAQ/028 for a discussion of why $0 is not a good choice here
+dir=$(dirname "$full_cmd")
+cmd=${full_cmd##*/}
+
+export PYTHONPATH="$dir/../../Source/Python:$dir/../../Source/Python/FMMT:$dir/../../Source/Python${PYTHONPATH:+:"$PYTHONPATH"}"
+exec "${python_exe:-python}" -m $cmd.$cmd "$@"
diff --git a/BaseTools/BinWrappers/WindowsLike/FMMT.bat b/BaseTools/BinWrappers/WindowsLike/FMMT.bat
new file mode 100644 (file)
index 0000000..f0551c4
--- /dev/null
@@ -0,0 +1,4 @@
+@setlocal\r
+@set ToolName=%~n0%\r
+@set PYTHONPATH=%PYTHONPATH%;%BASE_TOOLS_PATH%\Source\Python;%BASE_TOOLS_PATH%\Source\Python\FMMT\r
+@%PYTHON_COMMAND% -m %ToolName%.%ToolName% %*\r
diff --git a/BaseTools/Source/Python/FMMT/FMMT.py b/BaseTools/Source/Python/FMMT/FMMT.py
new file mode 100644 (file)
index 0000000..10800e7
--- /dev/null
@@ -0,0 +1,153 @@
+# @file\r
+#  Firmware Module Management Tool.\r
+#\r
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+#\r
+#  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+##\r
+\r
+# Import Modules\r
+#\r
+import argparse\r
+from core.FMMTOperation import *\r
+\r
+parser = argparse.ArgumentParser(description='''\r
+View the Binary Structure of FD/FV/Ffs/Section, and Delete/Extract/Add/Replace a Ffs from/into a FV.\r
+''')\r
+parser.add_argument("--version", action="version", version='%(prog)s Version 1.0',\r
+                    help="Print debug information.")\r
+parser.add_argument("-v", "--View", dest="View", nargs='+',\r
+                    help="View each FV and the named files within each FV: '-v inputfile outputfile, inputfiletype(.Fd/.Fv/.ffs/.sec)'")\r
+parser.add_argument("-d", "--Delete", dest="Delete", nargs='+',\r
+                    help="Delete a Ffs from FV: '-d inputfile TargetFvName(Optional) TargetFfsName outputfile\\r
+                        If not given TargetFvName, all the existed target Ffs will be deleted'")\r
+parser.add_argument("-e", "--Extract", dest="Extract", nargs='+',\r
+                    help="Extract a Ffs Info: '-e inputfile TargetFvName(Optional) TargetFfsName outputfile\\r
+                        If not given TargetFvName, the first found target Ffs will be extracted'")\r
+parser.add_argument("-a", "--Add", dest="Add", nargs='+',\r
+                    help="Add a Ffs into a FV:'-a inputfile TargetFvName newffsfile outputfile'")\r
+parser.add_argument("-r", "--Replace", dest="Replace", nargs='+',\r
+                    help="Replace a Ffs in a FV: '-r inputfile TargetFvName(Optional) TargetFfsName newffsfile outputfile\\r
+                        If not given TargetFvName, all the existed target Ffs will be replaced with new Ffs file)'")\r
+parser.add_argument("-l", "--LayoutFileName", dest="LayoutFileName", nargs='+',\r
+                    help="The output file which saves Binary layout: '-l xxx.txt'/'-l xxx.json'\\r
+                        If only provide file format as 'txt', \\r
+                        the file will be generated with default name (Layout_'InputFileName'.txt). \\r
+                        Currently supports two formats: json, txt. More formats will be added in the future")\r
+parser.add_argument("-c", "--ConfigFilePath", dest="ConfigFilePath", nargs='+',\r
+                    help="Provide the target FmmtConf.ini file path: '-c C:\Code\FmmtConf.ini' \\r
+                        FmmtConf file saves the target guidtool used in compress/uncompress process.\\r
+                        If do not provide, FMMT tool will search the inputfile folder for FmmtConf.ini firstly, if not found,\\r
+                        the FmmtConf.ini saved in FMMT tool's folder will be used as default.")\r
+\r
+def print_banner():\r
+    print("")\r
+\r
+class FMMT():\r
+    def __init__(self) -> None:\r
+        self.firmware_packet = {}\r
+\r
+    def SetConfigFilePath(self, configfilepath:str) -> str:\r
+        os.environ['FmmtConfPath'] = os.path.abspath(configfilepath)\r
+\r
+    def SetDestPath(self, inputfile:str) -> str:\r
+        os.environ['FmmtConfPath'] = ''\r
+        self.dest_path = os.path.dirname(os.path.abspath(inputfile))\r
+        old_env = os.environ['PATH']\r
+        os.environ['PATH'] = self.dest_path + os.pathsep + old_env\r
+\r
+    def CheckFfsName(self, FfsName:str) -> str:\r
+        try:\r
+            return uuid.UUID(FfsName)\r
+        except:\r
+            return FfsName\r
+\r
+    def GetFvName(self, FvName:str) -> str:\r
+        try:\r
+            return uuid.UUID(FvName)\r
+        except:\r
+            return FvName\r
+\r
+    def View(self, inputfile: str, layoutfilename: str=None, outputfile: str=None) -> None:\r
+        # ViewFile(inputfile, ROOT_TYPE, logfile, outputfile)\r
+        self.SetDestPath(inputfile)\r
+        filetype = os.path.splitext(inputfile)[1].lower()\r
+        if filetype == '.fd':\r
+            ROOT_TYPE = ROOT_TREE\r
+        elif filetype == '.fv':\r
+            ROOT_TYPE = ROOT_FV_TREE\r
+        elif filetype == '.ffs':\r
+            ROOT_TYPE = ROOT_FFS_TREE\r
+        elif filetype == '.sec':\r
+            ROOT_TYPE = ROOT_SECTION_TREE\r
+        else:\r
+            ROOT_TYPE = ROOT_TREE\r
+        ViewFile(inputfile, ROOT_TYPE, layoutfilename, outputfile)\r
+\r
+    def Delete(self, inputfile: str, TargetFfs_name: str, outputfile: str, Fv_name: str=None) -> None:\r
+        self.SetDestPath(inputfile)\r
+        if Fv_name:\r
+            DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outputfile, self.GetFvName(Fv_name))\r
+        else:\r
+            DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outputfile)\r
+\r
+    def Extract(self, inputfile: str, Ffs_name: str, outputfile: str, Fv_name: str=None) -> None:\r
+        self.SetDestPath(inputfile)\r
+        if Fv_name:\r
+            ExtractFfs(inputfile, self.CheckFfsName(Ffs_name), outputfile, self.GetFvName(Fv_name))\r
+        else:\r
+            ExtractFfs(inputfile, self.CheckFfsName(Ffs_name), outputfile)\r
+\r
+    def Add(self, inputfile: str, Fv_name: str, newffsfile: str, outputfile: str) -> None:\r
+        self.SetDestPath(inputfile)\r
+        AddNewFfs(inputfile, self.CheckFfsName(Fv_name), newffsfile, outputfile)\r
+\r
+    def Replace(self,inputfile: str, Ffs_name: str, newffsfile: str, outputfile: str, Fv_name: str=None) -> None:\r
+        self.SetDestPath(inputfile)\r
+        if Fv_name:\r
+            ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfile, outputfile, self.GetFvName(Fv_name))\r
+        else:\r
+            ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfile, outputfile)\r
+\r
+\r
+def main():\r
+    args=parser.parse_args()\r
+    status=0\r
+\r
+    try:\r
+        fmmt=FMMT()\r
+        if args.ConfigFilePath:\r
+            fmmt.SetConfigFilePath(args.ConfigFilePath[0])\r
+        if args.View:\r
+            if args.LayoutFileName:\r
+                fmmt.View(args.View[0], args.LayoutFileName[0])\r
+            else:\r
+                fmmt.View(args.View[0])\r
+        elif args.Delete:\r
+            if len(args.Delete) == 4:\r
+                fmmt.Delete(args.Delete[0],args.Delete[2],args.Delete[3],args.Delete[1])\r
+            else:\r
+                fmmt.Delete(args.Delete[0],args.Delete[1],args.Delete[2])\r
+        elif args.Extract:\r
+            if len(args.Extract) == 4:\r
+                fmmt.Extract(args.Extract[0],args.Extract[2],args.Extract[3], args.Extract[1])\r
+            else:\r
+                fmmt.Extract(args.Extract[0],args.Extract[1],args.Extract[2])\r
+        elif args.Add:\r
+            fmmt.Add(args.Add[0],args.Add[1],args.Add[2],args.Add[3])\r
+        elif args.Replace:\r
+            if len(args.Replace) == 5:\r
+                fmmt.Replace(args.Replace[0],args.Replace[2],args.Replace[3],args.Replace[4],args.Replace[1])\r
+            else:\r
+                fmmt.Replace(args.Replace[0],args.Replace[1],args.Replace[2],args.Replace[3])\r
+        else:\r
+            parser.print_help()\r
+    except Exception as e:\r
+        print(e)\r
+\r
+    return status\r
+\r
+\r
+if __name__ == "__main__":\r
+    exit(main())\r
diff --git a/BaseTools/Source/Python/FMMT/FmmtConf.ini b/BaseTools/Source/Python/FMMT/FmmtConf.ini
new file mode 100644 (file)
index 0000000..aa2444f
--- /dev/null
@@ -0,0 +1,11 @@
+## @file\r
+# This file is used to define the FMMT dependent external tool guid.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+a31280ad-481e-41b6-95e8-127f4c984779 TIANO TianoCompress\r
+ee4e5898-3914-4259-9d6e-dc7bd79403cf LZMA LzmaCompress\r
+fc1bcdb0-7d31-49aa-936a-a4600d9dd083 CRC32 GenCrc32\r
+d42ae6bd-1352-4bfb-909a-ca72a6eae889 LZMAF86 LzmaF86Compress\r
+3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress\r
diff --git a/BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png b/BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png
new file mode 100644 (file)
index 0000000..7cc806a
Binary files /dev/null and b/BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png differ
diff --git a/BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png b/BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png
new file mode 100644 (file)
index 0000000..6335653
Binary files /dev/null and b/BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png differ
diff --git a/BaseTools/Source/Python/FMMT/README.md b/BaseTools/Source/Python/FMMT/README.md
new file mode 100644 (file)
index 0000000..87cbff8
--- /dev/null
@@ -0,0 +1,184 @@
+# FMMT\r
+## Overview\r
+This FMMT tool is the python implementation of the edk2 FMMT tool which locates at https://github.com/tianocore/edk2-staging/tree/FceFmmt.\r
+This implementation has the same usage as the edk2 FMMT, but it's more readable and relaiable.\r
+\r
+# FMMT User Guide\r
+\r
+#### Last updated April 28, 2022\r
+\r
+Important Changes and Updates:\r
+\r
+- Oct 13, 2021 Initial Draft of FMMT Python Tool\r
+- Apr 28, 2022 Optimize functions & Command line\r
+\r
+#### Note:\r
+\r
+- FMMT Python Tool keeps same function with origin FMMT C Tool. It is much easier to maintain and extend other functions.\r
+\r
+#### Known issue:\r
+\r
+- Currently, FMMT Python tool does not support PEIM rebase feature, this feature will be added in future update.\r
+\r
+# 1. Introduction\r
+\r
+## 1.1  Overview\r
+\r
+The Firmware Device is a persistent physical repository that contains firmware code and/or data. The firmware code and/or data stored in Firmware Volumes. Detail layout of Firmware Volumes is described in ?Figure 1. The Firmware Volume Format?.\r
+\r
+![](Img/FirmwareVolumeFormat.png)\r
+\r
+?                                                   Figure 1. The Firmware Volume Format\r
+\r
+In firmware development, binary file has its firmware layout following the Platform-Initialization Specification. Thus, operation on FV file / FFS file (Firmware File) is an efficient and convenient way for firmware function testing and developing. FMMT Python tool is used for firmware files operation.\r
+\r
+## 1.2  Tool Capabilities\r
+\r
+The FMMT tool is capable of:\r
+\r
+- Parse a FD (Firmware Device) / FV (Firmware Volume) / FFS (Firmware Files)\r
+\r
+- Add a new FFS into a FV file (both included in a FD file or not)\r
+\r
+- Replace an FFS in a FV file with a new FFS file\r
+\r
+- Delete an FFS in a FV file (both included in a FD file or not)\r
+\r
+-  Extract the FFS from a FV file (both included in a FD file or not)\r
+\r
+## 1.3  References\r
+\r
+| Document                                         |\r
+| ------------------------------------------------ |\r
+| UEFI Platform Initialization (PI)  Specification |\r
+\r
+# 2. FMMT Python Tool Usage\r
+\r
+## 2.1  Required Files\r
+\r
+### 2.1.1  Independent use\r
+\r
+When independent use the FMMT Python Tool, the following files and settings are required:\r
+\r
+- GuidTool executable files used for Decompress/Compress Firmware data.\r
+\r
+- Environment variables path with GuidTool path setting.\r
+\r
+### 2.1.2  Use with Build System\r
+\r
+When use the FMMT Python Tool with Build System:\r
+\r
+-  If only use Edk2 based GuidTool, do not need other preparation.\r
+\r
+- If use other customized GuidTool, need prepare the config file with GuidTool info. The syntax for GuidTool definition shown as follow:\r
+\r
+  ***ToolsGuid ShortName Command***\r
+\r
+  -- Example:  ***3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress***\r
+\r
+## 2.2  Syntax\r
+\r
+### 2.2.1  Syntax for Parse file\r
+\r
+  ***-v < Inputfile > < Outputfile > -l < LogFileType > -c < ConfigFilePath >***\r
+\r
+- Parse *Inputfile*, show its firmware layout with log file. *Outputfile* is optional, if inputs, the *Inputfile* will be encapsulated into *Outputfile* following the parsed firmware layout. *"-l LogFileType"* is optional, it decides the format of log file which saves Binary layout. Currently supports: json, txt. More formats will be added in the future. *"-c ConfigFilePath "* is optional, target FmmtConf.ini file can be selected with this parameter. If not provided, default FmmtConf.ini file will be used.\r
+- Ex: py -3 FMMT.py -v test.fd\r
+\r
+### 2.2.2  Syntax for Add a new FFS\r
+\r
+  ***-a  < Inputfile > < TargetFvName/TargetFvGuid > < NewFfsFile > < Outputfile >***\r
+\r
+- Add the *NewFfsFile* into *Inputfile*. *TargetFvName/TargetFvGuid* (Name or Guid) is the TargetFv which *NewFfsFile* will be added into.\r
+- Ex: py -3 FMMT.py -a Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 NewAdd.ffs output.fd\r
+\r
+### 2.2.3  Syntax for Delete an FFS\r
+\r
+  ***-d  < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < Outputfile >***\r
+\r
+- Delete the Ffs from *Inputfile*. TargetFfsName (Guid) is the TargetFfs which will be deleted. *TargetFvName/TargetFvGuid* is optional, which is the parent of TargetFfs*.*\r
+- Ex: py -3 FMMT.py -d Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei output.fd\r
+\r
+### 2.2.4  Syntax for Replace an FFS\r
+\r
+?  ***-r  < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < NewFfsFile > < Outputfile >***\r
+\r
+- Replace the Ffs with the NewFfsFile. TargetFfsName (Guid) is the TargetFfs which will be replaced. *TargetFvName/TargetFvGuid* is optional, which is the parent of TargetFfs*.*\r
+- Ex: py -3 FMMT.py -r Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei NewS3Resume2Pei.ffs output.fd\r
+\r
+### 2.2.5  Syntax for Extract an FFS\r
+\r
+  ***-e  < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < Outputfile >***\r
+\r
+- Extract the Ffs from the Inputfile. TargetFfsName (Guid) is the TargetFfs which will be extracted. *TargetFvName/TargetFvGuid* is optional, which is the parent of TargetFfs*.*\r
+- Ex: py -3 FMMT.py -e Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei output.fd\r
+\r
+# 3. FMMT Python Tool Design\r
+\r
+FMMT Python Tool uses the NodeTree saves whole Firmware layout. Each Node have its Data field, which saves the FirmwareClass(FD/FV/FFS/SECTION/BINARY) Data. All the parse/add/delete/replace/extract operations are based on the NodeTree (adjusting the layout and data).\r
+\r
+## 3.1  NodeTree\r
+\r
+A whole NodeTree saves all the Firmware info.\r
+\r
+- Parent & Child relationship figured out the Firmware layout.\r
+\r
+- Each Node have several fields. ?Data? field saves an FirmwareClass instance which contains all the data info of the info.\r
+\r
+### 3.1.1  NodeTree Format\r
+\r
+The NodeTree will be created with parse function. When parse a file, a Root Node will be initialized firstly. The Data split and Tree construction process is described with an FD file shown as ?Figure 2. The NodeTree format?:\r
+\r
+- A Root Node is initialized.\r
+\r
+- Use the ?FV Signature? as FV key to split Whole FD Data. ?FV0?, ?FV1?, ?FV2?? Node created.\r
+\r
+- After FV level Node created, use the ?Ffs Data Size? as FFS key to split each FV Data. ?Ffs0?...Node created.\r
+\r
+- After FFS level Node created, use the ?Section Data Size? as Section key to split each Ffs Data. ?Section0?...Node created.\r
+\r
+- If some of Section includes other Sections, continue use the ?Section Data Size? as Section key to split each Section Data.\r
+\r
+- After all Node created, the whole NodeTree saves all the info. (Can be used in other functions or print the whole firmware layout into log file)\r
+\r
+![](Img/NodeTreeFormat.png)\r
+\r
+?                                                         Figure 2. The NodeTree format\r
+\r
+### 3.1.2  Node Factory and Product\r
+\r
+As 3.1.1, Each Node is created by data split and recognition. To extend the NodeTree usage, Factory pattern is used in Node created process.\r
+\r
+Each Node have its Factory to create Product and use Product ParserData function to deal with the data.\r
+\r
+## 3.2  GuidTool\r
+\r
+There are two ways to set the GuidTool. One from Config file, another from environment variables.\r
+\r
+Current GuidTool first check if has Config file.\r
+\r
+- If have, load the config GuidTool Information.\r
+\r
+- Else get from environment variables.\r
+\r
+### 3.2.1  Get from Config file\r
+\r
+- Config file should in same folder with FMMT.py or the path in environment variables.\r
+\r
+- Content should follow the format:\r
+\r
+  ***ToolsGuid ShortName Command***\r
+\r
+### 3.2.2  Get from Environment Variables\r
+\r
+- The GuidTool Command used must be set in environment variables.\r
+\r
+### 3.2.3  Edk2 Based GuidTool\r
+\r
+| ***Guid***                                 | ***ShortName*** | ***Command***         |\r
+| ------------------------------------------ | --------------- | --------------------- |\r
+| ***a31280ad-481e-41b6-95e8-127f4c984779*** | ***TIANO***     | ***TianoCompress***   |\r
+| ***ee4e5898-3914-4259-9d6e-dc7bd79403cf*** | ***LZMA***      | ***LzmaCompress***    |\r
+| ***fc1bcdb0-7d31-49aa-936a-a4600d9dd083*** | ***CRC32***     | ***GenCrc32***        |\r
+| ***d42ae6bd-1352-4bfb-909a-ca72a6eae889*** | ***LZMAF86***   | ***LzmaF86Compress*** |\r
+| ***3d532050-5cda-4fd0-879e-0f7f630d5afb*** | ***BROTLI***    | ***BrotliCompress***  |\r
\ No newline at end of file
diff --git a/BaseTools/Source/Python/FMMT/__init__.py b/BaseTools/Source/Python/FMMT/__init__.py
new file mode 100644 (file)
index 0000000..04e6ec0
--- /dev/null
@@ -0,0 +1,6 @@
+## @file\r
+# This file is used to define the FMMT dependent external tool.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
\ No newline at end of file
diff --git a/BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py b/BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py
new file mode 100644 (file)
index 0000000..2d4e6d9
--- /dev/null
@@ -0,0 +1,380 @@
+## @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
diff --git a/BaseTools/Source/Python/FMMT/core/BiosTree.py b/BaseTools/Source/Python/FMMT/core/BiosTree.py
new file mode 100644 (file)
index 0000000..d8fa474
--- /dev/null
@@ -0,0 +1,198 @@
+## @file\r
+# This file is used to define the Bios layout tree structure and related operations.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import collections\r
+from FirmwareStorageFormat.Common import *\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
+\r
+RootType = [ROOT_TREE, ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE]\r
+FvType = [FV_TREE, SEC_FV_TREE]\r
+FfsType = FFS_TREE\r
+SecType = SECTION_TREE\r
+\r
+class BIOSTREE:\r
+    def __init__(self, NodeName: str) -> None:\r
+        self.key = NodeName\r
+        self.type = None\r
+        self.Data = None\r
+        self.Child = []\r
+        self.Findlist = []\r
+        self.Parent = None\r
+        self.NextRel = None\r
+        self.LastRel = None\r
+\r
+    def HasChild(self) -> bool:\r
+        if self.Child == []:\r
+            return False\r
+        else:\r
+            return True\r
+\r
+    def isFinalChild(self) -> bool:\r
+        ParTree = self.Parent\r
+        if ParTree:\r
+            if ParTree.Child[-1] == self:\r
+                return True\r
+        return False\r
+\r
+    # FvTree.insertChild()\r
+    def insertChild(self, newNode, pos: int=None) -> None:\r
+        if len(self.Child) == 0:\r
+            self.Child.append(newNode)\r
+        else:\r
+            if not pos:\r
+                LastTree = self.Child[-1]\r
+                self.Child.append(newNode)\r
+                LastTree.NextRel = newNode\r
+                newNode.LastRel = LastTree\r
+            else:\r
+                newNode.NextRel = self.Child[pos-1].NextRel\r
+                newNode.LastRel = self.Child[pos].LastRel\r
+                self.Child[pos-1].NextRel = newNode\r
+                self.Child[pos].LastRel = newNode\r
+                self.Child.insert(pos, newNode)\r
+        newNode.Parent = self\r
+\r
+    # lastNode.insertRel(newNode)\r
+    def insertRel(self, newNode) -> None:\r
+        if self.Parent:\r
+            parentTree = self.Parent\r
+            new_index = parentTree.Child.index(self) + 1\r
+            parentTree.Child.insert(new_index, newNode)\r
+        self.NextRel = newNode\r
+        newNode.LastRel = self\r
+\r
+    def deleteNode(self, deletekey: str) -> None:\r
+        FindStatus, DeleteTree = self.FindNode(deletekey)\r
+        if FindStatus:\r
+            parentTree = DeleteTree.Parent\r
+            lastTree = DeleteTree.LastRel\r
+            nextTree = DeleteTree.NextRel\r
+            if parentTree:\r
+                index = parentTree.Child.index(DeleteTree)\r
+                del parentTree.Child[index]\r
+            if lastTree and nextTree:\r
+                lastTree.NextRel = nextTree\r
+                nextTree.LastRel = lastTree\r
+            elif lastTree:\r
+                lastTree.NextRel = None\r
+            elif nextTree:\r
+                nextTree.LastRel = None\r
+            return DeleteTree\r
+        else:\r
+            logger.error('Could not find the target tree')\r
+            return None\r
+\r
+    def FindNode(self, key: str, Findlist: list) -> None:\r
+        if self.key == key or (self.Data and self.Data.Name == key) or (self.type == FFS_TREE and self.Data.UiName == key):\r
+            Findlist.append(self)\r
+        for item in self.Child:\r
+            item.FindNode(key, Findlist)\r
+\r
+    def GetTreePath(self):\r
+        BiosTreePath = [self]\r
+        while self.Parent:\r
+            BiosTreePath.insert(0, self.Parent)\r
+            self = self.Parent\r
+        return BiosTreePath\r
+\r
+    def parserTree(self, TargetDict: dict=None, Info: list=None, space: int=0, ParFvId="") -> None:\r
+        Key = list(TargetDict.keys())[0]\r
+        if TargetDict[Key]["Type"] in RootType:\r
+            Info.append("Image File: {}".format(Key))\r
+            Info.append("FilesNum: {}".format(TargetDict.get(Key).get('FilesNum')))\r
+            Info.append("\n")\r
+        elif TargetDict[Key]["Type"] in FvType:\r
+            space += 2\r
+            if TargetDict[Key]["Type"] == SEC_FV_TREE:\r
+                Info.append("{}Child FV named {} of {}".format(space*" ", Key, ParFvId))\r
+                space += 2\r
+            else:\r
+                Info.append("FvId: {}".format(Key))\r
+                ParFvId = Key\r
+            Info.append("{}FvNameGuid: {}".format(space*" ", TargetDict.get(Key).get('FvNameGuid')))\r
+            Info.append("{}Attributes: {}".format(space*" ", TargetDict.get(Key).get('Attributes')))\r
+            Info.append("{}Total Volume Size: {}".format(space*" ", TargetDict.get(Key).get('Size')))\r
+            Info.append("{}Free Volume Size: {}".format(space*" ", TargetDict.get(Key).get('FreeSize')))\r
+            Info.append("{}Volume Offset: {}".format(space*" ", TargetDict.get(Key).get('Offset')))\r
+            Info.append("{}FilesNum: {}".format(space*" ", TargetDict.get(Key).get('FilesNum')))\r
+        elif TargetDict[Key]["Type"] in FfsType:\r
+            space += 2\r
+            if TargetDict.get(Key).get('UiName') != "b''":\r
+                Info.append("{}File: {} / {}".format(space*" ", Key, TargetDict.get(Key).get('UiName')))\r
+            else:\r
+                Info.append("{}File: {}".format(space*" ", Key))\r
+        if "Files" in list(TargetDict[Key].keys()):\r
+            for item in TargetDict[Key]["Files"]:\r
+                self.parserTree(item, Info, space, ParFvId)\r
+\r
+    def ExportTree(self,TreeInfo: dict=None) -> dict:\r
+        if TreeInfo is None:\r
+            TreeInfo =collections.OrderedDict()\r
+\r
+        if self.type == ROOT_TREE or self.type == ROOT_FV_TREE or self.type == ROOT_FFS_TREE or self.type == ROOT_SECTION_TREE:\r
+            key = str(self.key)\r
+            TreeInfo[self.key] = collections.OrderedDict()\r
+            TreeInfo[self.key]["Name"] = key\r
+            TreeInfo[self.key]["Type"] = self.type\r
+            TreeInfo[self.key]["FilesNum"] = len(self.Child)\r
+        elif self.type == FV_TREE or  self.type == SEC_FV_TREE:\r
+            key = str(self.Data.FvId)\r
+            TreeInfo[key] = collections.OrderedDict()\r
+            TreeInfo[key]["Name"] = key\r
+            if self.Data.FvId != self.Data.Name:\r
+                TreeInfo[key]["FvNameGuid"] = str(self.Data.Name)\r
+            TreeInfo[key]["Type"] = self.type\r
+            TreeInfo[key]["Attributes"] = hex(self.Data.Header.Attributes)\r
+            TreeInfo[key]["Size"] = hex(self.Data.Header.FvLength)\r
+            TreeInfo[key]["FreeSize"] = hex(self.Data.Free_Space)\r
+            TreeInfo[key]["Offset"] = hex(self.Data.HOffset)\r
+            TreeInfo[key]["FilesNum"] = len(self.Child)\r
+        elif self.type == FFS_TREE:\r
+            key = str(self.Data.Name)\r
+            TreeInfo[key] = collections.OrderedDict()\r
+            TreeInfo[key]["Name"] = key\r
+            TreeInfo[key]["UiName"] = '{}'.format(self.Data.UiName)\r
+            TreeInfo[key]["Version"] = '{}'.format(self.Data.Version)\r
+            TreeInfo[key]["Type"] = self.type\r
+            TreeInfo[key]["Size"] = hex(self.Data.Size)\r
+            TreeInfo[key]["Offset"] = hex(self.Data.HOffset)\r
+            TreeInfo[key]["FilesNum"] = len(self.Child)\r
+        elif self.type == SECTION_TREE and self.Data.Type == 0x02:\r
+            key = str(self.Data.Name)\r
+            TreeInfo[key] = collections.OrderedDict()\r
+            TreeInfo[key]["Name"] = key\r
+            TreeInfo[key]["Type"] = self.type\r
+            TreeInfo[key]["Size"] = hex(len(self.Data.OriData) + self.Data.HeaderLength)\r
+            TreeInfo[key]["DecompressedSize"] = hex(self.Data.Size)\r
+            TreeInfo[key]["Offset"] = hex(self.Data.HOffset)\r
+            TreeInfo[key]["FilesNum"] = len(self.Child)\r
+        elif self is not None:\r
+            key = str(self.Data.Name)\r
+            TreeInfo[key] = collections.OrderedDict()\r
+            TreeInfo[key]["Name"] = key\r
+            TreeInfo[key]["Type"] = self.type\r
+            TreeInfo[key]["Size"] = hex(self.Data.Size)\r
+            TreeInfo[key]["Offset"] = hex(self.Data.HOffset)\r
+            TreeInfo[key]["FilesNum"] = len(self.Child)\r
+\r
+        for item in self.Child:\r
+            TreeInfo[key].setdefault('Files',[]).append( item.ExportTree())\r
+\r
+        return TreeInfo\r
\ No newline at end of file
diff --git a/BaseTools/Source/Python/FMMT/core/BiosTreeNode.py b/BaseTools/Source/Python/FMMT/core/BiosTreeNode.py
new file mode 100644 (file)
index 0000000..2044776
--- /dev/null
@@ -0,0 +1,194 @@
+## @file\r
+# This file is used to define the BIOS Tree Node.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from FirmwareStorageFormat.FvHeader import *\r
+from FirmwareStorageFormat.FfsFileHeader import *\r
+from FirmwareStorageFormat.SectionHeader import *\r
+from FirmwareStorageFormat.Common import *\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+import uuid\r
+\r
+SectionHeaderType = {\r
+    0x01:'EFI_COMPRESSION_SECTION',\r
+    0x02:'EFI_GUID_DEFINED_SECTION',\r
+    0x03:'EFI_SECTION_DISPOSABLE',\r
+    0x10:'EFI_SECTION_PE32',\r
+    0x11:'EFI_SECTION_PIC',\r
+    0x12:'EFI_SECTION_TE',\r
+    0x13:'EFI_SECTION_DXE_DEPEX',\r
+    0x14:'EFI_SECTION_VERSION',\r
+    0x15:'EFI_SECTION_USER_INTERFACE',\r
+    0x16:'EFI_SECTION_COMPATIBILITY16',\r
+    0x17:'EFI_SECTION_FIRMWARE_VOLUME_IMAGE',\r
+    0x18:'EFI_FREEFORM_SUBTYPE_GUID_SECTION',\r
+    0x19:'EFI_SECTION_RAW',\r
+    0x1B:'EFI_SECTION_PEI_DEPEX',\r
+    0x1C:'EFI_SECTION_MM_DEPEX'\r
+}\r
+HeaderType = [0x01, 0x02, 0x14, 0x15, 0x18]\r
+\r
+class BinaryNode:\r
+    def __init__(self, name: str) -> None:\r
+        self.Size = 0\r
+        self.Name = "BINARY" + str(name)\r
+        self.HOffset = 0\r
+        self.Data = b''\r
+\r
+class FvNode:\r
+    def __init__(self, name, buffer: bytes) -> None:\r
+        self.Header = EFI_FIRMWARE_VOLUME_HEADER.from_buffer_copy(buffer)\r
+        Map_num = (self.Header.HeaderLength - 56)//8\r
+        self.Header = Refine_FV_Header(Map_num).from_buffer_copy(buffer)\r
+        self.FvId = "FV" + str(name)\r
+        self.Name = "FV" + str(name)\r
+        if self.Header.ExtHeaderOffset:\r
+            self.ExtHeader = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer_copy(buffer[self.Header.ExtHeaderOffset:])\r
+            self.Name =  uuid.UUID(bytes_le=struct2stream(self.ExtHeader.FvName))\r
+            self.ExtEntryOffset = self.Header.ExtHeaderOffset + 20\r
+            if self.ExtHeader.ExtHeaderSize != 20:\r
+                self.ExtEntryExist = 1\r
+                self.ExtEntry = EFI_FIRMWARE_VOLUME_EXT_ENTRY.from_buffer_copy(buffer[self.ExtEntryOffset:])\r
+                self.ExtTypeExist = 1\r
+                if self.ExtEntry.ExtEntryType == 0x01:\r
+                    nums = (self.ExtEntry.ExtEntrySize - 8) // 16\r
+                    self.ExtEntry = Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums).from_buffer_copy(buffer[self.ExtEntryOffset:])\r
+                elif self.ExtEntry.ExtEntryType == 0x02:\r
+                    nums = self.ExtEntry.ExtEntrySize - 20\r
+                    self.ExtEntry = Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums).from_buffer_copy(buffer[self.ExtEntryOffset:])\r
+                elif self.ExtEntry.ExtEntryType == 0x03:\r
+                    self.ExtEntry = EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE.from_buffer_copy(buffer[self.ExtEntryOffset:])\r
+                else:\r
+                    self.ExtTypeExist = 0\r
+            else:\r
+                self.ExtEntryExist = 0\r
+        self.Size = self.Header.FvLength\r
+        self.HeaderLength = self.Header.HeaderLength\r
+        self.HOffset = 0\r
+        self.DOffset = 0\r
+        self.ROffset = 0\r
+        self.Data = b''\r
+        if self.Header.Signature != 1213613663:\r
+            logger.error('Invalid Fv Header! Fv {} signature {} is not "_FVH".'.format(struct2stream(self.Header), self.Header.Signature))\r
+            raise Exception("Process Failed: Fv Header Signature!")\r
+        self.PadData = b''\r
+        self.Free_Space = 0\r
+        self.ModCheckSum()\r
+\r
+    def ModCheckSum(self) -> None:\r
+        # Fv Header Sums to 0.\r
+        Header = struct2stream(self.Header)[::-1]\r
+        Size = self.HeaderLength // 2\r
+        Sum = 0\r
+        for i in range(Size):\r
+            Sum += int(Header[i*2: i*2 + 2].hex(), 16)\r
+        if Sum & 0xffff:\r
+            self.Header.Checksum = 0x10000 - (Sum - self.Header.Checksum) % 0x10000\r
+\r
+    def ModFvExt(self) -> None:\r
+        # If used space changes and self.ExtEntry.UsedSize exists, self.ExtEntry.UsedSize need to be changed.\r
+        if self.Header.ExtHeaderOffset and self.ExtEntryExist and self.ExtTypeExist and self.ExtEntry.Hdr.ExtEntryType == 0x03:\r
+            self.ExtEntry.UsedSize = self.Header.FvLength - self.Free_Space\r
+\r
+    def ModFvSize(self) -> None:\r
+        # If Fv Size changed, self.Header.FvLength and self.Header.BlockMap[i].NumBlocks need to be changed.\r
+        BlockMapNum = len(self.Header.BlockMap)\r
+        for i in range(BlockMapNum):\r
+            if self.Header.BlockMap[i].Length:\r
+                self.Header.BlockMap[i].NumBlocks = self.Header.FvLength // self.Header.BlockMap[i].Length\r
+\r
+    def ModExtHeaderData(self) -> None:\r
+        if self.Header.ExtHeaderOffset:\r
+            ExtHeaderData = struct2stream(self.ExtHeader)\r
+            ExtHeaderDataOffset = self.Header.ExtHeaderOffset - self.HeaderLength\r
+            self.Data = self.Data[:ExtHeaderDataOffset] + ExtHeaderData + self.Data[ExtHeaderDataOffset+20:]\r
+        if self.Header.ExtHeaderOffset and self.ExtEntryExist:\r
+            ExtHeaderEntryData = struct2stream(self.ExtEntry)\r
+            ExtHeaderEntryDataOffset = self.Header.ExtHeaderOffset + 20 - self.HeaderLength\r
+            self.Data = self.Data[:ExtHeaderEntryDataOffset] + ExtHeaderEntryData + self.Data[ExtHeaderEntryDataOffset+len(ExtHeaderEntryData):]\r
+\r
+class FfsNode:\r
+    def __init__(self, buffer: bytes) -> None:\r
+        self.Header = EFI_FFS_FILE_HEADER.from_buffer_copy(buffer)\r
+        # self.Attributes = unpack("<B", buffer[21:22])[0]\r
+        if self.Header.FFS_FILE_SIZE != 0 and self.Header.Attributes != 0xff and self.Header.Attributes & 0x01 == 1:\r
+            logger.error('Error Ffs Header! Ffs {} Header Size and Attributes is not matched!'.format(uuid.UUID(bytes_le=struct2stream(self.Header.Name))))\r
+            raise Exception("Process Failed: Error Ffs Header!")\r
+        if self.Header.FFS_FILE_SIZE == 0 and self.Header.Attributes & 0x01 == 1:\r
+            self.Header = EFI_FFS_FILE_HEADER2.from_buffer_copy(buffer)\r
+        self.Name = uuid.UUID(bytes_le=struct2stream(self.Header.Name))\r
+        self.UiName = b''\r
+        self.Version = b''\r
+        self.Size = self.Header.FFS_FILE_SIZE\r
+        self.HeaderLength = self.Header.HeaderLength\r
+        self.HOffset = 0\r
+        self.DOffset = 0\r
+        self.ROffset = 0\r
+        self.Data = b''\r
+        self.PadData = b''\r
+        self.SectionMaxAlignment = SECTION_COMMON_ALIGNMENT  # 4-align\r
+\r
+    def ModCheckSum(self) -> None:\r
+        HeaderData = struct2stream(self.Header)\r
+        HeaderSum = 0\r
+        for item in HeaderData:\r
+            HeaderSum += item\r
+        HeaderSum -= self.Header.State\r
+        HeaderSum -= self.Header.IntegrityCheck.Checksum.File\r
+        if HeaderSum & 0xff:\r
+            Header = self.Header.IntegrityCheck.Checksum.Header + 0x100 - HeaderSum % 0x100\r
+            self.Header.IntegrityCheck.Checksum.Header = Header % 0x100\r
+\r
+class SectionNode:\r
+    def __init__(self, buffer: bytes) -> None:\r
+        if buffer[0:3] != b'\xff\xff\xff':\r
+            self.Header = EFI_COMMON_SECTION_HEADER.from_buffer_copy(buffer)\r
+        else:\r
+            self.Header = EFI_COMMON_SECTION_HEADER2.from_buffer_copy(buffer)\r
+        if self.Header.Type in SectionHeaderType:\r
+            self.Name = SectionHeaderType[self.Header.Type]\r
+        elif self.Header.Type == 0:\r
+            self.Name = "EFI_SECTION_ALL"\r
+        else:\r
+            self.Name = "SECTION"\r
+        if self.Header.Type in HeaderType:\r
+            self.ExtHeader = self.GetExtHeader(self.Header.Type, buffer[self.Header.Common_Header_Size():], (self.Header.SECTION_SIZE-self.Header.Common_Header_Size()))\r
+            self.HeaderLength = self.Header.Common_Header_Size() + self.ExtHeader.ExtHeaderSize()\r
+        else:\r
+            self.ExtHeader = None\r
+            self.HeaderLength = self.Header.Common_Header_Size()\r
+        self.Size = self.Header.SECTION_SIZE\r
+        self.Type = self.Header.Type\r
+        self.HOffset = 0\r
+        self.DOffset = 0\r
+        self.ROffset = 0\r
+        self.Data = b''\r
+        self.OriData = b''\r
+        self.OriHeader = b''\r
+        self.PadData = b''\r
+        self.IsPadSection = False\r
+        self.SectionMaxAlignment = SECTION_COMMON_ALIGNMENT  # 4-align\r
+\r
+    def GetExtHeader(self, Type: int, buffer: bytes, nums: int=0) -> None:\r
+        if Type == 0x01:\r
+            return EFI_COMPRESSION_SECTION.from_buffer_copy(buffer)\r
+        elif Type == 0x02:\r
+            return EFI_GUID_DEFINED_SECTION.from_buffer_copy(buffer)\r
+        elif Type == 0x14:\r
+            return Get_VERSION_Header((nums - 2)//2).from_buffer_copy(buffer)\r
+        elif Type == 0x15:\r
+            return Get_USER_INTERFACE_Header(nums//2).from_buffer_copy(buffer)\r
+        elif Type == 0x18:\r
+            return EFI_FREEFORM_SUBTYPE_GUID_SECTION.from_buffer_copy(buffer)\r
+\r
+class FreeSpaceNode:\r
+    def __init__(self, buffer: bytes) -> None:\r
+        self.Name = 'Free_Space'\r
+        self.Data = buffer\r
+        self.Size = len(buffer)\r
+        self.HOffset = 0\r
+        self.DOffset = 0\r
+        self.ROffset = 0\r
+        self.PadData = b''\r
\ No newline at end of file
diff --git a/BaseTools/Source/Python/FMMT/core/FMMTOperation.py b/BaseTools/Source/Python/FMMT/core/FMMTOperation.py
new file mode 100644 (file)
index 0000000..c2cc2e2
--- /dev/null
@@ -0,0 +1,197 @@
+## @file\r
+# This file is used to define the functions to operate bios binary file.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from core.FMMTParser import *\r
+from core.FvHandler import *\r
+from utils.FvLayoutPrint import *\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+\r
+global Fv_count\r
+Fv_count = 0\r
+\r
+# The ROOT_TYPE can be 'ROOT_TREE', 'ROOT_FV_TREE', 'ROOT_FFS_TREE', 'ROOT_SECTION_TREE'\r
+def ViewFile(inputfile: str, ROOT_TYPE: str, layoutfile: str=None, outputfile: str=None) -> None:\r
+    if not os.path.exists(inputfile):\r
+        logger.error("Invalid inputfile, can not open {}.".format(inputfile))\r
+        raise Exception("Process Failed: Invalid inputfile!")\r
+    # 1. Data Prepare\r
+    with open(inputfile, "rb") as f:\r
+        whole_data = f.read()\r
+    FmmtParser = FMMTParser(inputfile, ROOT_TYPE)\r
+    # 2. DataTree Create\r
+    logger.debug('Parsing inputfile data......')\r
+    FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)\r
+    logger.debug('Done!')\r
+    # 3. Log Output\r
+    InfoDict = FmmtParser.WholeFvTree.ExportTree()\r
+    logger.debug('BinaryTree created, start parsing BinaryTree data......')\r
+    FmmtParser.WholeFvTree.parserTree(InfoDict, FmmtParser.BinaryInfo)\r
+    logger.debug('Done!')\r
+    GetFormatter("").LogPrint(FmmtParser.BinaryInfo)\r
+    if layoutfile:\r
+        if os.path.splitext(layoutfile)[1]:\r
+            layoutfilename = layoutfile\r
+            layoutfileformat = os.path.splitext(layoutfile)[1][1:].lower()\r
+        else:\r
+            layoutfilename = "Layout_{}{}".format(os.path.basename(inputfile),".{}".format(layoutfile.lower()))\r
+            layoutfileformat = layoutfile.lower()\r
+        GetFormatter(layoutfileformat).dump(InfoDict, FmmtParser.BinaryInfo, layoutfilename)\r
+    # 4. Data Encapsulation\r
+    if outputfile:\r
+        logger.debug('Start encapsulating data......')\r
+        FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)\r
+        with open(outputfile, "wb") as f:\r
+            f.write(FmmtParser.FinalData)\r
+        logger.debug('Encapsulated data is saved in {}.'.format(outputfile))\r
+\r
+def DeleteFfs(inputfile: str, TargetFfs_name: str, outputfile: str, Fv_name: str=None) -> None:\r
+    if not os.path.exists(inputfile):\r
+        logger.error("Invalid inputfile, can not open {}.".format(inputfile))\r
+        raise Exception("Process Failed: Invalid inputfile!")\r
+    # 1. Data Prepare\r
+    with open(inputfile, "rb") as f:\r
+        whole_data = f.read()\r
+    FmmtParser = FMMTParser(inputfile, ROOT_TREE)\r
+    # 2. DataTree Create\r
+    logger.debug('Parsing inputfile data......')\r
+    FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)\r
+    logger.debug('Done!')\r
+    # 3. Data Modify\r
+    FmmtParser.WholeFvTree.FindNode(TargetFfs_name, FmmtParser.WholeFvTree.Findlist)\r
+    # Choose the Specfic DeleteFfs with Fv info\r
+    if Fv_name:\r
+        for item in FmmtParser.WholeFvTree.Findlist:\r
+            if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:\r
+                FmmtParser.WholeFvTree.Findlist.remove(item)\r
+    Status = False\r
+    if FmmtParser.WholeFvTree.Findlist != []:\r
+        for Delete_Ffs in FmmtParser.WholeFvTree.Findlist:\r
+            FfsMod = FvHandler(None, Delete_Ffs)\r
+            Status = FfsMod.DeleteFfs()\r
+    else:\r
+        logger.error('Target Ffs not found!!!')\r
+    # 4. Data Encapsulation\r
+    if Status:\r
+        logger.debug('Start encapsulating data......')\r
+        FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)\r
+        with open(outputfile, "wb") as f:\r
+            f.write(FmmtParser.FinalData)\r
+        logger.debug('Encapsulated data is saved in {}.'.format(outputfile))\r
+\r
+def AddNewFfs(inputfile: str, Fv_name: str, newffsfile: str, outputfile: str) -> None:\r
+    if not os.path.exists(inputfile):\r
+        logger.error("Invalid inputfile, can not open {}.".format(inputfile))\r
+        raise Exception("Process Failed: Invalid inputfile!")\r
+    if not os.path.exists(newffsfile):\r
+        logger.error("Invalid ffsfile, can not open {}.".format(newffsfile))\r
+        raise Exception("Process Failed: Invalid ffs file!")\r
+    # 1. Data Prepare\r
+    with open(inputfile, "rb") as f:\r
+        whole_data = f.read()\r
+    FmmtParser = FMMTParser(inputfile, ROOT_TREE)\r
+    # 2. DataTree Create\r
+    logger.debug('Parsing inputfile data......')\r
+    FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)\r
+    logger.debug('Done!')\r
+    # Get Target Fv and Target Ffs_Pad\r
+    FmmtParser.WholeFvTree.FindNode(Fv_name, FmmtParser.WholeFvTree.Findlist)\r
+    # Create new ffs Tree\r
+    with open(newffsfile, "rb") as f:\r
+        new_ffs_data = f.read()\r
+    NewFmmtParser = FMMTParser(newffsfile, ROOT_FFS_TREE)\r
+    Status = False\r
+    # 3. Data Modify\r
+    if FmmtParser.WholeFvTree.Findlist:\r
+        for TargetFv in FmmtParser.WholeFvTree.Findlist:\r
+            TargetFfsPad = TargetFv.Child[-1]\r
+            logger.debug('Parsing newffsfile data......')\r
+            if TargetFfsPad.type == FFS_FREE_SPACE:\r
+                NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, new_ffs_data, TargetFfsPad.Data.HOffset)\r
+            else:\r
+                NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, new_ffs_data, TargetFfsPad.Data.HOffset+TargetFfsPad.Data.Size)\r
+            logger.debug('Done!')\r
+            FfsMod = FvHandler(NewFmmtParser.WholeFvTree.Child[0], TargetFfsPad)\r
+            Status = FfsMod.AddFfs()\r
+    else:\r
+        logger.error('Target Fv not found!!!')\r
+    # 4. Data Encapsulation\r
+    if Status:\r
+        logger.debug('Start encapsulating data......')\r
+        FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)\r
+        with open(outputfile, "wb") as f:\r
+            f.write(FmmtParser.FinalData)\r
+        logger.debug('Encapsulated data is saved in {}.'.format(outputfile))\r
+\r
+def ReplaceFfs(inputfile: str, Ffs_name: str, newffsfile: str, outputfile: str, Fv_name: str=None) -> None:\r
+    if not os.path.exists(inputfile):\r
+        logger.error("Invalid inputfile, can not open {}.".format(inputfile))\r
+        raise Exception("Process Failed: Invalid inputfile!")\r
+    # 1. Data Prepare\r
+    with open(inputfile, "rb") as f:\r
+        whole_data = f.read()\r
+    FmmtParser = FMMTParser(inputfile, ROOT_TREE)\r
+    # 2. DataTree Create\r
+    logger.debug('Parsing inputfile data......')\r
+    FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)\r
+    logger.debug('Done!')\r
+    with open(newffsfile, "rb") as f:\r
+        new_ffs_data = f.read()\r
+    newFmmtParser = FMMTParser(newffsfile, FV_TREE)\r
+    logger.debug('Parsing newffsfile data......')\r
+    newFmmtParser.ParserFromRoot(newFmmtParser.WholeFvTree, new_ffs_data)\r
+    logger.debug('Done!')\r
+    Status = False\r
+    # 3. Data Modify\r
+    new_ffs = newFmmtParser.WholeFvTree.Child[0]\r
+    new_ffs.Data.PadData = GetPadSize(new_ffs.Data.Size, FFS_COMMON_ALIGNMENT) * b'\xff'\r
+    FmmtParser.WholeFvTree.FindNode(Ffs_name, FmmtParser.WholeFvTree.Findlist)\r
+    if Fv_name:\r
+        for item in FmmtParser.WholeFvTree.Findlist:\r
+            if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:\r
+                FmmtParser.WholeFvTree.Findlist.remove(item)\r
+    if FmmtParser.WholeFvTree.Findlist != []:\r
+        for TargetFfs in FmmtParser.WholeFvTree.Findlist:\r
+            FfsMod = FvHandler(newFmmtParser.WholeFvTree.Child[0], TargetFfs)\r
+            Status = FfsMod.ReplaceFfs()\r
+    else:\r
+        logger.error('Target Ffs not found!!!')\r
+    # 4. Data Encapsulation\r
+    if Status:\r
+        logger.debug('Start encapsulating data......')\r
+        FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)\r
+        with open(outputfile, "wb") as f:\r
+            f.write(FmmtParser.FinalData)\r
+        logger.debug('Encapsulated data is saved in {}.'.format(outputfile))\r
+\r
+def ExtractFfs(inputfile: str, Ffs_name: str, outputfile: str, Fv_name: str=None) -> None:\r
+    if not os.path.exists(inputfile):\r
+        logger.error("Invalid inputfile, can not open {}.".format(inputfile))\r
+        raise Exception("Process Failed: Invalid inputfile!")\r
+    # 1. Data Prepare\r
+    with open(inputfile, "rb") as f:\r
+        whole_data = f.read()\r
+    FmmtParser = FMMTParser(inputfile, ROOT_TREE)\r
+    # 2. DataTree Create\r
+    logger.debug('Parsing inputfile data......')\r
+    FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)\r
+    logger.debug('Done!')\r
+    FmmtParser.WholeFvTree.FindNode(Ffs_name, FmmtParser.WholeFvTree.Findlist)\r
+    if Fv_name:\r
+        for item in FmmtParser.WholeFvTree.Findlist:\r
+            if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:\r
+                FmmtParser.WholeFvTree.Findlist.remove(item)\r
+    if FmmtParser.WholeFvTree.Findlist != []:\r
+        TargetNode = FmmtParser.WholeFvTree.Findlist[0]\r
+        TargetFv = TargetNode.Parent\r
+        if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:\r
+            TargetNode.Data.Header.State = c_uint8(\r
+                ~TargetNode.Data.Header.State)\r
+        FinalData = struct2stream(TargetNode.Data.Header) + TargetNode.Data.Data\r
+        with open(outputfile, "wb") as f:\r
+            f.write(FinalData)\r
+        logger.debug('Extract ffs data is saved in {}.'.format(outputfile))\r
+    else:\r
+        logger.error('Target Ffs not found!!!')\r
diff --git a/BaseTools/Source/Python/FMMT/core/FMMTParser.py b/BaseTools/Source/Python/FMMT/core/FMMTParser.py
new file mode 100644 (file)
index 0000000..e76ac51
--- /dev/null
@@ -0,0 +1,87 @@
+## @file\r
+# This file is used to define the interface of Bios Parser.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from FirmwareStorageFormat.Common import *\r
+from core.BinaryFactoryProduct import ParserEntry\r
+from core.BiosTreeNode import *\r
+from core.BiosTree import *\r
+from core.GuidTools import *\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+\r
+class FMMTParser:\r
+    def __init__(self, name: str, TYPE: str) -> None:\r
+        self.WholeFvTree = BIOSTREE(name)\r
+        self.WholeFvTree.type = TYPE\r
+        self.FinalData = b''\r
+        self.BinaryInfo = []\r
+\r
+    ## Parser the nodes in WholeTree.\r
+    def ParserFromRoot(self, WholeFvTree=None, whole_data: bytes=b'', Reloffset: int=0) -> None:\r
+        if WholeFvTree.type == ROOT_TREE or WholeFvTree.type == ROOT_FV_TREE:\r
+            ParserEntry().DataParser(self.WholeFvTree, whole_data, Reloffset)\r
+        else:\r
+            ParserEntry().DataParser(WholeFvTree, whole_data, Reloffset)\r
+        for Child in WholeFvTree.Child:\r
+            self.ParserFromRoot(Child, "")\r
+\r
+    ## Encapuslation all the data in tree into self.FinalData\r
+    def Encapsulation(self, rootTree, CompressStatus: bool) -> None:\r
+        # If current node is Root node, skip it.\r
+        if rootTree.type == ROOT_TREE or rootTree.type == ROOT_FV_TREE or rootTree.type == ROOT_FFS_TREE or rootTree.type == ROOT_SECTION_TREE:\r
+            logger.debug('Encapsulated successfully!')\r
+        # If current node do not have Header, just add Data.\r
+        elif rootTree.type == BINARY_DATA or rootTree.type == FFS_FREE_SPACE:\r
+            self.FinalData += rootTree.Data.Data\r
+            rootTree.Child = []\r
+        # If current node do not have Child and ExtHeader, just add its Header and Data.\r
+        elif rootTree.type == DATA_FV_TREE or rootTree.type == FFS_PAD:\r
+            self.FinalData += struct2stream(rootTree.Data.Header) + rootTree.Data.Data + rootTree.Data.PadData\r
+            if rootTree.isFinalChild():\r
+                ParTree = rootTree.Parent\r
+                if ParTree.type != 'ROOT':\r
+                    self.FinalData += ParTree.Data.PadData\r
+            rootTree.Child = []\r
+        # If current node is not Section node and may have Child and ExtHeader, add its Header,ExtHeader. If do not have Child, add its Data.\r
+        elif rootTree.type == FV_TREE or rootTree.type == FFS_TREE or rootTree.type == SEC_FV_TREE:\r
+            if rootTree.HasChild():\r
+                self.FinalData += struct2stream(rootTree.Data.Header)\r
+            else:\r
+                self.FinalData += struct2stream(rootTree.Data.Header) + rootTree.Data.Data + rootTree.Data.PadData\r
+                if rootTree.isFinalChild():\r
+                    ParTree = rootTree.Parent\r
+                    if ParTree.type != 'ROOT':\r
+                        self.FinalData += ParTree.Data.PadData\r
+        # If current node is Section, need to consider its ExtHeader, Child and Compressed Status.\r
+        elif rootTree.type == SECTION_TREE:\r
+            # Not compressed section\r
+            if rootTree.Data.OriData == b'' or (rootTree.Data.OriData != b'' and CompressStatus):\r
+                if rootTree.HasChild():\r
+                    if rootTree.Data.ExtHeader:\r
+                        self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader)\r
+                    else:\r
+                        self.FinalData += struct2stream(rootTree.Data.Header)\r
+                else:\r
+                    Data = rootTree.Data.Data\r
+                    if rootTree.Data.ExtHeader:\r
+                        self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData\r
+                    else:\r
+                        self.FinalData += struct2stream(rootTree.Data.Header) + Data + rootTree.Data.PadData\r
+                    if rootTree.isFinalChild():\r
+                        ParTree = rootTree.Parent\r
+                        self.FinalData += ParTree.Data.PadData\r
+            # If compressed section\r
+            else:\r
+                Data = rootTree.Data.OriData\r
+                rootTree.Child = []\r
+                if rootTree.Data.ExtHeader:\r
+                    self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData\r
+                else:\r
+                    self.FinalData += struct2stream(rootTree.Data.Header) + Data + rootTree.Data.PadData\r
+                if rootTree.isFinalChild():\r
+                    ParTree = rootTree.Parent\r
+                    self.FinalData += ParTree.Data.PadData\r
+        for Child in rootTree.Child:\r
+            self.Encapsulation(Child, CompressStatus)\r
diff --git a/BaseTools/Source/Python/FMMT/core/FvHandler.py b/BaseTools/Source/Python/FMMT/core/FvHandler.py
new file mode 100644 (file)
index 0000000..c81541e
--- /dev/null
@@ -0,0 +1,641 @@
+## @file\r
+# This file is used to the implementation of Bios layout handler.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import os\r
+from core.BiosTree import *\r
+from core.GuidTools import GUIDTools\r
+from core.BiosTreeNode import *\r
+from FirmwareStorageFormat.Common import *\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+\r
+EFI_FVB2_ERASE_POLARITY = 0x00000800\r
+\r
+def ChangeSize(TargetTree, size_delta: int=0) -> None:\r
+    # If Size increase delta, then should be: size_delta = -delta\r
+    if type(TargetTree.Data.Header) == type(EFI_FFS_FILE_HEADER2()) or type(TargetTree.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()):\r
+        TargetTree.Data.Size -= size_delta\r
+        TargetTree.Data.Header.ExtendedSize -= size_delta\r
+    elif TargetTree.type == SECTION_TREE and TargetTree.Data.OriData:\r
+        OriSize = TargetTree.Data.Header.SECTION_SIZE\r
+        OriSize -= size_delta\r
+        TargetTree.Data.Header.Size[0] = OriSize % (16**2)\r
+        TargetTree.Data.Header.Size[1] = OriSize % (16**4) //(16**2)\r
+        TargetTree.Data.Header.Size[2] = OriSize // (16**4)\r
+    else:\r
+        TargetTree.Data.Size -= size_delta\r
+        TargetTree.Data.Header.Size[0] = TargetTree.Data.Size % (16**2)\r
+        TargetTree.Data.Header.Size[1] = TargetTree.Data.Size % (16**4) //(16**2)\r
+        TargetTree.Data.Header.Size[2] = TargetTree.Data.Size // (16**4)\r
+\r
+def ModifyFfsType(TargetFfs) -> None:\r
+    if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER()) and TargetFfs.Data.Size > 0xFFFFFF:\r
+        ExtendSize = TargetFfs.Data.Header.FFS_FILE_SIZE + 8\r
+        New_Header = EFI_FFS_FILE_HEADER2()\r
+        New_Header.Name = TargetFfs.Data.Header.Name\r
+        New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck\r
+        New_Header.Type = TargetFfs.Data.Header.Type\r
+        New_Header.Attributes = TargetFfs.Data.Header.Attributes | 0x01  # set the Attribute with FFS_ATTRIB_LARGE_FILE (0x01)\r
+        NewSize = 0\r
+        New_Header.Size[0] = NewSize % (16**2)    # minus the delta size of Header\r
+        New_Header.Size[1] = NewSize % (16**4) //(16**2)\r
+        New_Header.Size[2] = NewSize // (16**4)\r
+        New_Header.State = TargetFfs.Data.Header.State\r
+        New_Header.ExtendedSize = ExtendSize\r
+        TargetFfs.Data.Header = New_Header\r
+        TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE\r
+        TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength\r
+        TargetFfs.Data.ModCheckSum()\r
+    elif type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()) and TargetFfs.Data.Size <= 0xFFFFFF:\r
+        New_Header = EFI_FFS_FILE_HEADER()\r
+        New_Header.Name = TargetFfs.Data.Header.Name\r
+        New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck\r
+        New_Header.Type = TargetFfs.Data.Header.Type\r
+        New_Header.Attributes = TargetFfs.Data.Header.Attributes - 1  # remove the FFS_ATTRIB_LARGE_FILE (0x01) from Attribute\r
+        New_Header.Size[0] = (TargetFfs.Data.Size - 8) % (16**2)    # minus the delta size of Header\r
+        New_Header.Size[1] = (TargetFfs.Data.Size - 8) % (16**4) //(16**2)\r
+        New_Header.Size[2] = (TargetFfs.Data.Size - 8) // (16**4)\r
+        New_Header.State = TargetFfs.Data.Header.State\r
+        TargetFfs.Data.Header = New_Header\r
+        TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE\r
+        TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength\r
+        TargetFfs.Data.ModCheckSum()\r
+        if struct2stream(TargetFfs.Parent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE:\r
+            NeedChange = True\r
+            for item in TargetFfs.Parent.Child:\r
+                if type(item.Data.Header) == type(EFI_FFS_FILE_HEADER2()):\r
+                    NeedChange = False\r
+            if NeedChange:\r
+                TargetFfs.Parent.Data.Header.FileSystemGuid = ModifyGuidFormat("8c8ce578-8a3d-4f1c-9935-896185c32dd3")\r
+\r
+    if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()):\r
+        TarParent = TargetFfs.Parent\r
+        while TarParent:\r
+            if TarParent.type == FV_TREE and struct2stream(TarParent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:\r
+                TarParent.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")\r
+            TarParent = TarParent.Parent\r
+\r
+def PadSectionModify(PadSection, Offset) -> None:\r
+    # Offset > 0, Size decrease; Offset < 0, Size increase;\r
+    ChangeSize(PadSection, Offset)\r
+    PadSection.Data.Data = (PadSection.Data.Size - PadSection.Data.HeaderLength) * b'\xff'\r
+\r
+def ModifySectionType(TargetSection) -> None:\r
+    # If Section Size is increased larger than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER to EFI_COMMON_SECTION_HEADER2.\r
+    if type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER()) and TargetSection.Data.Size >= 0xFFFFFF:\r
+        New_Header = EFI_COMMON_SECTION_HEADER2()\r
+        New_Header.Type = TargetSection.Data.Header.Type\r
+        NewSize = 0xFFFFFF\r
+        New_Header.Size[0] = NewSize % (16**2)    # minus the delta size of Header\r
+        New_Header.Size[1] = NewSize % (16**4) //(16**2)\r
+        New_Header.Size[2] = NewSize // (16**4)\r
+        New_Header.ExtendedSize = TargetSection.Data.Size + 4\r
+        TargetSection.Data.Header = New_Header\r
+        TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE\r
+        # Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.\r
+        if TargetSection.LastRel.Data.IsPadSection:\r
+            PadSectionModify(TargetSection.LastRel, -4)\r
+        else:\r
+            SecParent = TargetSection.Parent\r
+            Target_index = SecParent.Child.index(TargetSection)\r
+            NewPadSection = SectionNode(b'\x00\x00\x00\x19')\r
+            SecParent.insertChild(NewPadSection, Target_index)\r
+    # If Section Size is decreased smaller than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER2 to EFI_COMMON_SECTION_HEADER.\r
+    elif type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()) and TargetSection.Data.Size < 0xFFFFFF:\r
+        New_Header = EFI_COMMON_SECTION_HEADER()\r
+        New_Header.Type = TargetSection.Data.Header.Type\r
+        New_Header.Size[0] = (TargetSection.Data.Size - 4) % (16**2)    # minus the delta size of Header\r
+        New_Header.Size[1] = (TargetSection.Data.Size - 4) % (16**4) //(16**2)\r
+        New_Header.Size[2] = (TargetSection.Data.Size - 4) // (16**4)\r
+        TargetSection.Data.Header = New_Header\r
+        TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE\r
+        # Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.\r
+        if TargetSection.LastRel.Data.IsPadSection:\r
+            PadSectionModify(TargetSection.LastRel, -4)\r
+        else:\r
+            SecParent = TargetSection.Parent\r
+            Target_index = SecParent.Child.index(TargetSection)\r
+            NewPadSection = SectionNode(b'\x00\x00\x00\x19')\r
+            SecParent.insertChild(NewPadSection, Target_index)\r
+\r
+def ModifyFvExtData(TreeNode) -> None:\r
+    FvExtData = b''\r
+    if TreeNode.Data.Header.ExtHeaderOffset:\r
+        FvExtHeader = struct2stream(TreeNode.Data.ExtHeader)\r
+        FvExtData += FvExtHeader\r
+    if TreeNode.Data.Header.ExtHeaderOffset and TreeNode.Data.ExtEntryExist:\r
+        FvExtEntry = struct2stream(TreeNode.Data.ExtEntry)\r
+        FvExtData += FvExtEntry\r
+    if FvExtData:\r
+        InfoNode = TreeNode.Child[0]\r
+        InfoNode.Data.Data = FvExtData + InfoNode.Data.Data[TreeNode.Data.ExtHeader.ExtHeaderSize:]\r
+        InfoNode.Data.ModCheckSum()\r
+\r
+def ModifyFvSystemGuid(TargetFv) -> None:\r
+    if struct2stream(TargetFv.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:\r
+        TargetFv.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")\r
+    TargetFv.Data.ModCheckSum()\r
+    TargetFv.Data.Data = b''\r
+    for item in TargetFv.Child:\r
+        if item.type == FFS_FREE_SPACE:\r
+            TargetFv.Data.Data += item.Data.Data + item.Data.PadData\r
+        else:\r
+            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+\r
+class FvHandler:\r
+    def __init__(self, NewFfs, TargetFfs) -> None:\r
+        self.NewFfs = NewFfs\r
+        self.TargetFfs = TargetFfs\r
+        self.Status = False\r
+        self.Remain_New_Free_Space = 0\r
+\r
+    ## Use for Compress the Section Data\r
+    def CompressData(self, TargetTree) -> None:\r
+        TreePath = TargetTree.GetTreePath()\r
+        pos = len(TreePath)\r
+        self.Status = False\r
+        while pos:\r
+            if not self.Status:\r
+                if TreePath[pos-1].type == SECTION_TREE and TreePath[pos-1].Data.Type == 0x02:\r
+                    self.CompressSectionData(TreePath[pos-1], None, TreePath[pos-1].Data.ExtHeader.SectionDefinitionGuid)\r
+                else:\r
+                    if pos == len(TreePath):\r
+                        self.CompressSectionData(TreePath[pos-1], pos)\r
+                    else:\r
+                        self.CompressSectionData(TreePath[pos-1], None)\r
+            pos -= 1\r
+\r
+    def CompressSectionData(self, TargetTree, pos: int, GuidTool=None) -> None:\r
+        NewData = b''\r
+        temp_save_child = TargetTree.Child\r
+        if TargetTree.Data:\r
+            # Update current node data as adding all the header and data of its child node.\r
+            for item in temp_save_child:\r
+                if item.type == SECTION_TREE and not item.Data.OriData and item.Data.ExtHeader:\r
+                    NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData\r
+                elif item.type == SECTION_TREE and item.Data.OriData and not item.Data.ExtHeader:\r
+                    NewData += struct2stream(item.Data.Header) + item.Data.OriData + item.Data.PadData\r
+                elif item.type == SECTION_TREE and item.Data.OriData and item.Data.ExtHeader:\r
+                    NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData\r
+                elif item.type == FFS_FREE_SPACE:\r
+                    NewData += item.Data.Data + item.Data.PadData\r
+                else:\r
+                    NewData += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData\r
+            # If node is FFS_TREE, update Pad data and Header info.\r
+            # Remain_New_Free_Space is used for move more free space into lst level Fv.\r
+            if TargetTree.type == FFS_TREE:\r
+                New_Pad_Size = GetPadSize(len(NewData), 8)\r
+                Size_delta = len(NewData) - len(TargetTree.Data.Data)\r
+                ChangeSize(TargetTree, -Size_delta)\r
+                Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size\r
+                self.Remain_New_Free_Space += Delta_Pad_Size\r
+                TargetTree.Data.PadData = b'\xff' * New_Pad_Size\r
+                TargetTree.Data.ModCheckSum()\r
+            # If node is FV_TREE, update Pad data and Header info.\r
+            # Consume Remain_New_Free_Space is used for move more free space into lst level Fv.\r
+            elif TargetTree.type == FV_TREE or TargetTree.type == SEC_FV_TREE and not pos:\r
+                if self.Remain_New_Free_Space:\r
+                    if TargetTree.Data.Free_Space:\r
+                        TargetTree.Data.Free_Space += self.Remain_New_Free_Space\r
+                        NewData += self.Remain_New_Free_Space * b'\xff'\r
+                        TargetTree.Child[-1].Data.Data += self.Remain_New_Free_Space * b'\xff'\r
+                    else:\r
+                        TargetTree.Data.Data += self.Remain_New_Free_Space * b'\xff'\r
+                        New_Free_Space = BIOSTREE('FREE_SPACE')\r
+                        New_Free_Space.type = FFS_FREE_SPACE\r
+                        New_Free_Space.Data = FreeSpaceNode(b'\xff' * self.Remain_New_Free_Space)\r
+                        TargetTree.insertChild(New_Free_Space)\r
+                    self.Remain_New_Free_Space = 0\r
+                if TargetTree.type == SEC_FV_TREE:\r
+                    Size_delta = len(NewData) + self.Remain_New_Free_Space - len(TargetTree.Data.Data)\r
+                    TargetTree.Data.Header.FvLength += Size_delta\r
+                TargetTree.Data.ModFvExt()\r
+                TargetTree.Data.ModFvSize()\r
+                TargetTree.Data.ModExtHeaderData()\r
+                ModifyFvExtData(TargetTree)\r
+                TargetTree.Data.ModCheckSum()\r
+            # If node is SECTION_TREE and not guided section, update Pad data and Header info.\r
+            # Remain_New_Free_Space is used for move more free space into lst level Fv.\r
+            elif TargetTree.type == SECTION_TREE and TargetTree.Data.Type != 0x02:\r
+                New_Pad_Size = GetPadSize(len(NewData), 4)\r
+                Size_delta = len(NewData) - len(TargetTree.Data.Data)\r
+                ChangeSize(TargetTree, -Size_delta)\r
+                if TargetTree.NextRel:\r
+                    Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size\r
+                    self.Remain_New_Free_Space += Delta_Pad_Size\r
+                    TargetTree.Data.PadData = b'\x00' * New_Pad_Size\r
+            TargetTree.Data.Data = NewData\r
+        if GuidTool:\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, TargetTree.Parent.Data.Name))\r
+                raise Exception("Process Failed: GuidTool not found!")\r
+            CompressedData = guidtool.pack(TargetTree.Data.Data)\r
+            if len(CompressedData) < len(TargetTree.Data.OriData):\r
+                New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)\r
+                Size_delta = len(CompressedData) - len(TargetTree.Data.OriData)\r
+                ChangeSize(TargetTree, -Size_delta)\r
+                if TargetTree.NextRel:\r
+                    TargetTree.Data.PadData = b'\x00' * New_Pad_Size\r
+                    self.Remain_New_Free_Space = len(TargetTree.Data.OriData) + len(TargetTree.Data.PadData) - len(CompressedData) - New_Pad_Size\r
+                else:\r
+                    TargetTree.Data.PadData = b''\r
+                    self.Remain_New_Free_Space = len(TargetTree.Data.OriData) - len(CompressedData)\r
+                TargetTree.Data.OriData = CompressedData\r
+            elif len(CompressedData) == len(TargetTree.Data.OriData):\r
+                TargetTree.Data.OriData = CompressedData\r
+            elif len(CompressedData) > len(TargetTree.Data.OriData):\r
+                New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)\r
+                self.Remain_New_Free_Space = len(CompressedData) + New_Pad_Size - len(TargetTree.Data.OriData) - len(TargetTree.Data.PadData)\r
+                self.ModifyTest(TargetTree, self.Remain_New_Free_Space)\r
+                self.Status = True\r
+\r
+    def ModifyTest(self, ParTree, Needed_Space: int) -> None:\r
+        # If have needed space, will find if there have free space in parent tree, meanwhile update the node data.\r
+        if Needed_Space > 0:\r
+            # If current node is a Fv node\r
+            if ParTree.type == FV_TREE or ParTree.type == SEC_FV_TREE:\r
+                ParTree.Data.Data = b''\r
+                # First check if Fv free space is enough for needed space.\r
+                # If so, use the current Fv free space;\r
+                # Else, use all the Free space, and recalculate needed space, continue finding in its parent node.\r
+                Needed_Space = Needed_Space - ParTree.Data.Free_Space\r
+                if Needed_Space < 0:\r
+                    ParTree.Child[-1].Data.Data = b'\xff' * (-Needed_Space)\r
+                    ParTree.Data.Free_Space = (-Needed_Space)\r
+                    self.Status = True\r
+                else:\r
+                    if ParTree.type == FV_TREE:\r
+                        self.Status = False\r
+                    else:\r
+                        BlockSize = ParTree.Data.Header.BlockMap[0].Length\r
+                        New_Add_Len = BlockSize - Needed_Space%BlockSize\r
+                        if New_Add_Len % BlockSize:\r
+                            ParTree.Child[-1].Data.Data = b'\xff' * New_Add_Len\r
+                            ParTree.Data.Free_Space = New_Add_Len\r
+                            Needed_Space += New_Add_Len\r
+                        else:\r
+                            ParTree.Child.remove(ParTree.Child[-1])\r
+                            ParTree.Data.Free_Space = 0\r
+                        ParTree.Data.Size += Needed_Space\r
+                        ParTree.Data.Header.Fvlength = ParTree.Data.Size\r
+                ModifyFvSystemGuid(ParTree)\r
+                for item in ParTree.Child:\r
+                    if item.type == FFS_FREE_SPACE:\r
+                        ParTree.Data.Data += item.Data.Data + item.Data.PadData\r
+                    else:\r
+                        ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+                ParTree.Data.ModFvExt()\r
+                ParTree.Data.ModFvSize()\r
+                ParTree.Data.ModExtHeaderData()\r
+                ModifyFvExtData(ParTree)\r
+                ParTree.Data.ModCheckSum()\r
+            # If current node is a Ffs node\r
+            elif ParTree.type == FFS_TREE:\r
+                ParTree.Data.Data = b''\r
+                OriHeaderLen = ParTree.Data.HeaderLength\r
+                # Update its data as adding all the header and data of its child node.\r
+                for item in ParTree.Child:\r
+                    if item.Data.OriData:\r
+                        if item.Data.ExtHeader:\r
+                            ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData\r
+                        else:\r
+                            ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.OriData + item.Data.PadData\r
+                    else:\r
+                        if item.Data.ExtHeader:\r
+                            ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData\r
+                        else:\r
+                            ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+                ChangeSize(ParTree, -Needed_Space)\r
+                ModifyFfsType(ParTree)\r
+                # Recalculate pad data, update needed space with Delta_Pad_Size.\r
+                Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen\r
+                New_Pad_Size = GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)\r
+                Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)\r
+                Needed_Space += Delta_Pad_Size\r
+                ParTree.Data.PadData = b'\xff' * GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)\r
+                ParTree.Data.ModCheckSum()\r
+            # If current node is a Section node\r
+            elif ParTree.type == SECTION_TREE:\r
+                OriData = ParTree.Data.Data\r
+                OriHeaderLen = ParTree.Data.HeaderLength\r
+                ParTree.Data.Data = b''\r
+                # Update its data as adding all the header and data of its child node.\r
+                for item in ParTree.Child:\r
+                    if item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type != 0x02:\r
+                        ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData\r
+                    elif item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type == 0x02:\r
+                        ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData\r
+                    else:\r
+                        ParTree.Data.Data += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData\r
+                # If the current section is guided section\r
+                if ParTree.Data.Type == 0x02:\r
+                    guidtool = GUIDTools().__getitem__(struct2stream(ParTree.Data.ExtHeader.SectionDefinitionGuid))\r
+                    if not guidtool.ifexist:\r
+                        logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, ParTree.Parent.Data.Name))\r
+                        raise Exception("Process Failed: GuidTool not found!")\r
+                    # Recompress current data, and recalculate the needed space\r
+                    CompressedData = guidtool.pack(ParTree.Data.Data)\r
+                    Needed_Space = len(CompressedData) - len(ParTree.Data.OriData)\r
+                    ParTree.Data.OriData = CompressedData\r
+                    New_Size = ParTree.Data.HeaderLength + len(CompressedData)\r
+                    ParTree.Data.Header.Size[0] = New_Size % (16**2)\r
+                    ParTree.Data.Header.Size[1] = New_Size % (16**4) //(16**2)\r
+                    ParTree.Data.Header.Size[2] = New_Size // (16**4)\r
+                    ParTree.Data.Size = ParTree.Data.Header.SECTION_SIZE\r
+                    ModifySectionType(ParTree)\r
+                    Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen\r
+                    # Update needed space with Delta_Pad_Size\r
+                    if ParTree.NextRel:\r
+                        New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)\r
+                        Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)\r
+                        ParTree.Data.PadData = b'\x00' * New_Pad_Size\r
+                        Needed_Space += Delta_Pad_Size\r
+                    else:\r
+                        ParTree.Data.PadData = b''\r
+                    if Needed_Space < 0:\r
+                        self.Remain_New_Free_Space = len(ParTree.Data.OriData) - len(CompressedData)\r
+                # If current section is not guided section\r
+                elif Needed_Space:\r
+                    ChangeSize(ParTree, -Needed_Space)\r
+                    ModifySectionType(ParTree)\r
+                    # Update needed space with Delta_Pad_Size\r
+                    Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen\r
+                    New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)\r
+                    Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)\r
+                    Needed_Space += Delta_Pad_Size\r
+                    ParTree.Data.PadData = b'\x00' * New_Pad_Size\r
+            NewParTree = ParTree.Parent\r
+            ROOT_TYPE = [ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE, ROOT_TREE]\r
+            if NewParTree and NewParTree.type not in ROOT_TYPE:\r
+                self.ModifyTest(NewParTree, Needed_Space)\r
+        # If current node have enough space, will recompress all the related node data, return true.\r
+        else:\r
+            self.CompressData(ParTree)\r
+            self.Status = True\r
+\r
+    def ReplaceFfs(self) -> bool:\r
+        logger.debug('Start Replacing Process......')\r
+        TargetFv = self.TargetFfs.Parent\r
+        # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.\r
+        if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:\r
+                self.NewFfs.Data.Header.State = c_uint8(\r
+                    ~self.NewFfs.Data.Header.State)\r
+        # NewFfs parsing will not calculate the PadSize, thus recalculate.\r
+        self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)\r
+        if self.NewFfs.Data.Size >= self.TargetFfs.Data.Size:\r
+            Needed_Space = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)\r
+            # If TargetFv have enough free space, just move part of the free space to NewFfs.\r
+            if TargetFv.Data.Free_Space >= Needed_Space:\r
+                # Modify TargetFv Child info and BiosTree.\r
+                TargetFv.Child[-1].Data.Data = b'\xff' * (TargetFv.Data.Free_Space - Needed_Space)\r
+                TargetFv.Data.Free_Space -= Needed_Space\r
+                Target_index = TargetFv.Child.index(self.TargetFfs)\r
+                TargetFv.Child.remove(self.TargetFfs)\r
+                TargetFv.insertChild(self.NewFfs, Target_index)\r
+                # Modify TargetFv Header and ExtHeader info.\r
+                TargetFv.Data.ModFvExt()\r
+                TargetFv.Data.ModFvSize()\r
+                TargetFv.Data.ModExtHeaderData()\r
+                ModifyFvExtData(TargetFv)\r
+                TargetFv.Data.ModCheckSum()\r
+                # Recompress from the Fv node to update all the related node data.\r
+                self.CompressData(TargetFv)\r
+                # return the Status\r
+                self.Status = True\r
+            # If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.\r
+            else:\r
+                if TargetFv.type == FV_TREE:\r
+                    self.Status = False\r
+                else:\r
+                    # Recalculate TargetFv needed space to keep it match the BlockSize setting.\r
+                    Needed_Space -= TargetFv.Data.Free_Space\r
+                    BlockSize = TargetFv.Data.Header.BlockMap[0].Length\r
+                    New_Add_Len = BlockSize - Needed_Space%BlockSize\r
+                    Target_index = TargetFv.Child.index(self.TargetFfs)\r
+                    if New_Add_Len % BlockSize:\r
+                        TargetFv.Child[-1].Data.Data = b'\xff' * New_Add_Len\r
+                        TargetFv.Data.Free_Space = New_Add_Len\r
+                        Needed_Space += New_Add_Len\r
+                        TargetFv.insertChild(self.NewFfs, Target_index)\r
+                        TargetFv.Child.remove(self.TargetFfs)\r
+                    else:\r
+                        TargetFv.Child.remove(self.TargetFfs)\r
+                        TargetFv.Data.Free_Space = 0\r
+                        TargetFv.insertChild(self.NewFfs)\r
+                    # Encapsulate the Fv Data for update.\r
+                    TargetFv.Data.Data = b''\r
+                    for item in TargetFv.Child:\r
+                        if item.type == FFS_FREE_SPACE:\r
+                            TargetFv.Data.Data += item.Data.Data + item.Data.PadData\r
+                        else:\r
+                            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+                    TargetFv.Data.Size += Needed_Space\r
+                    # Modify TargetFv Data Header and ExtHeader info.\r
+                    TargetFv.Data.Header.FvLength = TargetFv.Data.Size\r
+                    TargetFv.Data.ModFvExt()\r
+                    TargetFv.Data.ModFvSize()\r
+                    TargetFv.Data.ModExtHeaderData()\r
+                    ModifyFvExtData(TargetFv)\r
+                    TargetFv.Data.ModCheckSum()\r
+                    # Start free space calculating and moving process.\r
+                    self.ModifyTest(TargetFv.Parent, Needed_Space)\r
+        else:\r
+            New_Free_Space = self.TargetFfs.Data.Size - self.NewFfs.Data.Size\r
+            # If TargetFv already have free space, move the new free space into it.\r
+            if TargetFv.Data.Free_Space:\r
+                TargetFv.Child[-1].Data.Data += b'\xff' * New_Free_Space\r
+                TargetFv.Data.Free_Space += New_Free_Space\r
+                Target_index = TargetFv.Child.index(self.TargetFfs)\r
+                TargetFv.Child.remove(self.TargetFfs)\r
+                TargetFv.insertChild(self.NewFfs, Target_index)\r
+                self.Status = True\r
+            # If TargetFv do not have free space, create free space for Fv.\r
+            else:\r
+                New_Free_Space_Tree = BIOSTREE('FREE_SPACE')\r
+                New_Free_Space_Tree.type = FFS_FREE_SPACE\r
+                New_Free_Space_Tree.Data = FfsNode(b'\xff' * New_Free_Space)\r
+                TargetFv.Data.Free_Space = New_Free_Space\r
+                TargetFv.insertChild(New_Free_Space)\r
+                Target_index = TargetFv.Child.index(self.TargetFfs)\r
+                TargetFv.Child.remove(self.TargetFfs)\r
+                TargetFv.insertChild(self.NewFfs, Target_index)\r
+                self.Status = True\r
+            # Modify TargetFv Header and ExtHeader info.\r
+            TargetFv.Data.ModFvExt()\r
+            TargetFv.Data.ModFvSize()\r
+            TargetFv.Data.ModExtHeaderData()\r
+            ModifyFvExtData(TargetFv)\r
+            TargetFv.Data.ModCheckSum()\r
+            # Recompress from the Fv node to update all the related node data.\r
+            self.CompressData(TargetFv)\r
+        logger.debug('Done!')\r
+        return self.Status\r
+\r
+    def AddFfs(self) -> bool:\r
+        logger.debug('Start Adding Process......')\r
+        # NewFfs parsing will not calculate the PadSize, thus recalculate.\r
+        self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)\r
+        if self.TargetFfs.type == FFS_FREE_SPACE:\r
+            TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)\r
+            TargetFv = self.TargetFfs.Parent\r
+            # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.\r
+            if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:\r
+                self.NewFfs.Data.Header.State = c_uint8(\r
+                    ~self.NewFfs.Data.Header.State)\r
+            # If TargetFv have enough free space, just move part of the free space to NewFfs, split free space to NewFfs and new free space.\r
+            if TargetLen < 0:\r
+                self.Status = True\r
+                self.TargetFfs.Data.Data = b'\xff' * (-TargetLen)\r
+                TargetFv.Data.Free_Space = (-TargetLen)\r
+                TargetFv.Data.ModFvExt()\r
+                TargetFv.Data.ModExtHeaderData()\r
+                ModifyFvExtData(TargetFv)\r
+                TargetFv.Data.ModCheckSum()\r
+                TargetFv.insertChild(self.NewFfs, -1)\r
+                ModifyFfsType(self.NewFfs)\r
+                # Recompress from the Fv node to update all the related node data.\r
+                self.CompressData(TargetFv)\r
+            elif TargetLen == 0:\r
+                self.Status = True\r
+                TargetFv.Child.remove(self.TargetFfs)\r
+                TargetFv.insertChild(self.NewFfs)\r
+                ModifyFfsType(self.NewFfs)\r
+                # Recompress from the Fv node to update all the related node data.\r
+                self.CompressData(TargetFv)\r
+            # If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.\r
+            else:\r
+                if TargetFv.type == FV_TREE:\r
+                    self.Status = False\r
+                elif TargetFv.type == SEC_FV_TREE:\r
+                    # Recalculate TargetFv needed space to keep it match the BlockSize setting.\r
+                    BlockSize = TargetFv.Data.Header.BlockMap[0].Length\r
+                    New_Add_Len = BlockSize - TargetLen%BlockSize\r
+                    if New_Add_Len % BlockSize:\r
+                        self.TargetFfs.Data.Data = b'\xff' * New_Add_Len\r
+                        self.TargetFfs.Data.Size = New_Add_Len\r
+                        TargetLen += New_Add_Len\r
+                        TargetFv.insertChild(self.NewFfs, -1)\r
+                        TargetFv.Data.Free_Space = New_Add_Len\r
+                    else:\r
+                        TargetFv.Child.remove(self.TargetFfs)\r
+                        TargetFv.insertChild(self.NewFfs)\r
+                        TargetFv.Data.Free_Space = 0\r
+                    ModifyFfsType(self.NewFfs)\r
+                    ModifyFvSystemGuid(TargetFv)\r
+                    TargetFv.Data.Data = b''\r
+                    for item in TargetFv.Child:\r
+                        if item.type == FFS_FREE_SPACE:\r
+                            TargetFv.Data.Data += item.Data.Data + item.Data.PadData\r
+                        else:\r
+                            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+                    # Encapsulate the Fv Data for update.\r
+                    TargetFv.Data.Size += TargetLen\r
+                    TargetFv.Data.Header.FvLength = TargetFv.Data.Size\r
+                    TargetFv.Data.ModFvExt()\r
+                    TargetFv.Data.ModFvSize()\r
+                    TargetFv.Data.ModExtHeaderData()\r
+                    ModifyFvExtData(TargetFv)\r
+                    TargetFv.Data.ModCheckSum()\r
+                    # Start free space calculating and moving process.\r
+                    self.ModifyTest(TargetFv.Parent, TargetLen)\r
+        else:\r
+            # If TargetFv do not have free space, need directly move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.\r
+            TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData)\r
+            TargetFv = self.TargetFfs.Parent\r
+            if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:\r
+                self.NewFfs.Data.Header.State = c_uint8(\r
+                    ~self.NewFfs.Data.Header.State)\r
+            if TargetFv.type == FV_TREE:\r
+                self.Status = False\r
+            elif TargetFv.type == SEC_FV_TREE:\r
+                BlockSize = TargetFv.Data.Header.BlockMap[0].Length\r
+                New_Add_Len = BlockSize - TargetLen%BlockSize\r
+                if New_Add_Len % BlockSize:\r
+                    New_Free_Space = BIOSTREE('FREE_SPACE')\r
+                    New_Free_Space.type = FFS_FREE_SPACE\r
+                    New_Free_Space.Data = FreeSpaceNode(b'\xff' * New_Add_Len)\r
+                    TargetLen += New_Add_Len\r
+                    TargetFv.Data.Free_Space = New_Add_Len\r
+                    TargetFv.insertChild(self.NewFfs)\r
+                    TargetFv.insertChild(New_Free_Space)\r
+                else:\r
+                    TargetFv.insertChild(self.NewFfs)\r
+                ModifyFfsType(self.NewFfs)\r
+                ModifyFvSystemGuid(TargetFv)\r
+                TargetFv.Data.Data = b''\r
+                for item in TargetFv.Child:\r
+                    if item.type == FFS_FREE_SPACE:\r
+                        TargetFv.Data.Data += item.Data.Data + item.Data.PadData\r
+                    else:\r
+                        TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData\r
+                TargetFv.Data.Size += TargetLen\r
+                TargetFv.Data.Header.FvLength = TargetFv.Data.Size\r
+                TargetFv.Data.ModFvExt()\r
+                TargetFv.Data.ModFvSize()\r
+                TargetFv.Data.ModExtHeaderData()\r
+                ModifyFvExtData(TargetFv)\r
+                TargetFv.Data.ModCheckSum()\r
+                self.ModifyTest(TargetFv.Parent, TargetLen)\r
+        logger.debug('Done!')\r
+        return self.Status\r
+\r
+    def DeleteFfs(self) -> bool:\r
+        logger.debug('Start Deleting Process......')\r
+        Delete_Ffs = self.TargetFfs\r
+        Delete_Fv = Delete_Ffs.Parent\r
+        # Calculate free space\r
+        Add_Free_Space = Delete_Ffs.Data.Size + len(Delete_Ffs.Data.PadData)\r
+        # If Ffs parent Fv have free space, follow the rules to merge the new free space.\r
+        if Delete_Fv.Data.Free_Space:\r
+            # If Fv is a Section fv, free space need to be recalculated to keep align with BlockSize.\r
+            # Other free space saved in self.Remain_New_Free_Space, will be moved to the 1st level Fv.\r
+            if Delete_Fv.type == SEC_FV_TREE:\r
+                Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space\r
+                BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length\r
+                New_Free_Space = BlockSize - Used_Size % BlockSize\r
+                self.Remain_New_Free_Space += Delete_Fv.Data.Free_Space + Add_Free_Space - New_Free_Space\r
+                Delete_Fv.Child[-1].Data.Data = New_Free_Space * b'\xff'\r
+                Delete_Fv.Data.Free_Space = New_Free_Space\r
+            # If Fv is lst level Fv, new free space will be merged with origin free space.\r
+            else:\r
+                Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space\r
+                Delete_Fv.Child[-1].Data.Data += Add_Free_Space * b'\xff'\r
+                Delete_Fv.Data.Free_Space += Add_Free_Space\r
+                New_Free_Space = Delete_Fv.Data.Free_Space\r
+        # If Ffs parent Fv not have free space, will create new free space node to save the free space.\r
+        else:\r
+            # If Fv is a Section fv, new free space need to be recalculated to keep align with BlockSize.\r
+            # Then create a Free spcae node to save the 0xff data, and insert into the Fv.\r
+            # If have more space left, move to 1st level fv.\r
+            if Delete_Fv.type == SEC_FV_TREE:\r
+                Used_Size = Delete_Fv.Data.Size - Add_Free_Space\r
+                BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length\r
+                New_Free_Space = BlockSize - Used_Size % BlockSize\r
+                self.Remain_New_Free_Space += Add_Free_Space - New_Free_Space\r
+                Add_Free_Space = New_Free_Space\r
+            # If Fv is lst level Fv, new free space node will be created to save the free space.\r
+            else:\r
+                Used_Size = Delete_Fv.Data.Size - Add_Free_Space\r
+                New_Free_Space = Add_Free_Space\r
+            New_Free_Space_Info = FfsNode(Add_Free_Space * b'\xff')\r
+            New_Free_Space_Info.Data = Add_Free_Space * b'\xff'\r
+            New_Ffs_Tree = BIOSTREE(New_Free_Space_Info.Name)\r
+            New_Ffs_Tree.type = FFS_FREE_SPACE\r
+            New_Ffs_Tree.Data = New_Free_Space_Info\r
+            Delete_Fv.insertChild(New_Ffs_Tree)\r
+            Delete_Fv.Data.Free_Space = Add_Free_Space\r
+        Delete_Fv.Child.remove(Delete_Ffs)\r
+        Delete_Fv.Data.Header.FvLength = Used_Size + New_Free_Space\r
+        Delete_Fv.Data.ModFvExt()\r
+        Delete_Fv.Data.ModFvSize()\r
+        Delete_Fv.Data.ModExtHeaderData()\r
+        ModifyFvExtData(Delete_Fv)\r
+        Delete_Fv.Data.ModCheckSum()\r
+        # Recompress from the Fv node to update all the related node data.\r
+        self.CompressData(Delete_Fv)\r
+        self.Status = True\r
+        logger.debug('Done!')\r
+        return self.Status\r
diff --git a/BaseTools/Source/Python/FMMT/core/GuidTools.py b/BaseTools/Source/Python/FMMT/core/GuidTools.py
new file mode 100644 (file)
index 0000000..a256817
--- /dev/null
@@ -0,0 +1,179 @@
+## @file\r
+# This file is used to define the FMMT dependent external tool management class.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+import glob\r
+import logging\r
+import os\r
+import shutil\r
+import sys\r
+import tempfile\r
+import uuid\r
+from FirmwareStorageFormat.Common import *\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+import subprocess\r
+\r
+def ExecuteCommand(cmd: list) -> None:\r
+    subprocess.run(cmd,stdout=subprocess.DEVNULL)\r
+\r
+class GUIDTool:\r
+    def __init__(self, guid: str, short_name: str, command: str) -> None:\r
+        self.guid: str = guid\r
+        self.short_name: str = short_name\r
+        self.command: str = command\r
+        self.ifexist: bool = False\r
+\r
+    def pack(self, buffer: bytes) -> bytes:\r
+        """\r
+        compress file.\r
+        """\r
+        tool = self.command\r
+        if tool:\r
+            tmp = tempfile.mkdtemp(dir=os.environ.get('tmp'))\r
+            ToolInputFile = os.path.join(tmp, "pack_uncompress_sec_file")\r
+            ToolOuputFile = os.path.join(tmp, "pack_sec_file")\r
+            try:\r
+                file = open(ToolInputFile, "wb")\r
+                file.write(buffer)\r
+                file.close()\r
+                command = [tool, '-e', '-o', ToolOuputFile,\r
+                                  ToolInputFile]\r
+                ExecuteCommand(command)\r
+                buf = open(ToolOuputFile, "rb")\r
+                res_buffer = buf.read()\r
+            except Exception as msg:\r
+                logger.error(msg)\r
+                return ""\r
+            else:\r
+                buf.close()\r
+                if os.path.exists(tmp):\r
+                    shutil.rmtree(tmp)\r
+                return res_buffer\r
+        else:\r
+            logger.error(\r
+                "Error parsing section: EFI_SECTION_GUID_DEFINED cannot be parsed at this time.")\r
+            logger.info("Its GUID is: %s" % self.guid)\r
+            return ""\r
+\r
+\r
+    def unpack(self, buffer: bytes) -> bytes:\r
+        """\r
+        buffer: remove common header\r
+        uncompress file\r
+        """\r
+        tool = self.command\r
+        if tool:\r
+            tmp = tempfile.mkdtemp(dir=os.environ.get('tmp'))\r
+            ToolInputFile = os.path.join(tmp, "unpack_sec_file")\r
+            ToolOuputFile = os.path.join(tmp, "unpack_uncompress_sec_file")\r
+            try:\r
+                file = open(ToolInputFile, "wb")\r
+                file.write(buffer)\r
+                file.close()\r
+                command = [tool, '-d', '-o', ToolOuputFile, ToolInputFile]\r
+                ExecuteCommand(command)\r
+                buf = open(ToolOuputFile, "rb")\r
+                res_buffer = buf.read()\r
+            except Exception as msg:\r
+                logger.error(msg)\r
+                return ""\r
+            else:\r
+                buf.close()\r
+                if os.path.exists(tmp):\r
+                    shutil.rmtree(tmp)\r
+                return res_buffer\r
+        else:\r
+            logger.error("Error parsing section: EFI_SECTION_GUID_DEFINED cannot be parsed at this time.")\r
+            logger.info("Its GUID is: %s" % self.guid)\r
+            return ""\r
+\r
+class GUIDTools:\r
+    '''\r
+    GUIDTools is responsible for reading FMMTConfig.ini, verify the tools and provide interfaces to access those tools.\r
+    '''\r
+    default_tools = {\r
+        struct2stream(ModifyGuidFormat("a31280ad-481e-41b6-95e8-127f4c984779")): GUIDTool("a31280ad-481e-41b6-95e8-127f4c984779", "TIANO", "TianoCompress"),\r
+        struct2stream(ModifyGuidFormat("ee4e5898-3914-4259-9d6e-dc7bd79403cf")): GUIDTool("ee4e5898-3914-4259-9d6e-dc7bd79403cf", "LZMA", "LzmaCompress"),\r
+        struct2stream(ModifyGuidFormat("fc1bcdb0-7d31-49aa-936a-a4600d9dd083")): GUIDTool("fc1bcdb0-7d31-49aa-936a-a4600d9dd083", "CRC32", "GenCrc32"),\r
+        struct2stream(ModifyGuidFormat("d42ae6bd-1352-4bfb-909a-ca72a6eae889")): GUIDTool("d42ae6bd-1352-4bfb-909a-ca72a6eae889", "LZMAF86", "LzmaF86Compress"),\r
+        struct2stream(ModifyGuidFormat("3d532050-5cda-4fd0-879e-0f7f630d5afb")): GUIDTool("3d532050-5cda-4fd0-879e-0f7f630d5afb", "BROTLI", "BrotliCompress"),\r
+    }\r
+\r
+    def __init__(self, tooldef_file: str=None) -> None:\r
+        self.dir = os.path.join(os.path.dirname(__file__), "..")\r
+        self.tooldef_file = tooldef_file if tooldef_file else os.path.join(self.dir, "FmmtConf.ini")\r
+        self.tooldef = dict()\r
+\r
+    def SetConfigFile(self) -> None:\r
+        if os.environ['FmmtConfPath']:\r
+            self.tooldef_file = os.path.join(os.environ['FmmtConfPath'], 'FmmtConf.ini')\r
+        else:\r
+            PathList = os.environ['PATH']\r
+            for CurrentPath in PathList:\r
+                if os.path.exists(os.path.join(CurrentPath, 'FmmtConf.ini')):\r
+                    self.tooldef_file = os.path.join(CurrentPath, 'FmmtConf.ini')\r
+                    break\r
+\r
+    def VerifyTools(self, guidtool) -> None:\r
+        """\r
+        Verify Tools and Update Tools path.\r
+        """\r
+        path_env = os.environ.get("PATH")\r
+        path_env_list = path_env.split(os.pathsep)\r
+        path_env_list.append(os.path.dirname(__file__))\r
+        path_env_list = list(set(path_env_list))\r
+        cmd = guidtool.command\r
+        if os.path.isabs(cmd):\r
+            if not os.path.exists(cmd):\r
+                if guidtool not in self.default_tools:\r
+                    logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)\r
+                    logger.error("Please goto edk2 repo in current console, run 'edksetup.bat rebuild' command, and try again.\n")\r
+                else:\r
+                    logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)\r
+            else:\r
+                guidtool.ifexist = True\r
+        else:\r
+            for syspath in path_env_list:\r
+                if glob.glob(os.path.join(syspath, cmd+"*")):\r
+                    guidtool.ifexist = True\r
+                    break\r
+            else:\r
+                if guidtool not in self.default_tools:\r
+                    logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)\r
+                    logger.error("Please goto edk2 repo in current console, run 'edksetup.bat rebuild' command, and try again.\n")\r
+                else:\r
+                    logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)\r
+\r
+    def LoadingTools(self) -> None:\r
+        self.SetConfigFile()\r
+        if os.path.exists(self.tooldef_file):\r
+            with open(self.tooldef_file, "r") as fd:\r
+                config_data = fd.readlines()\r
+            for line in config_data:\r
+                try:\r
+                    if not line.startswith("#"):\r
+                        guid, short_name, command = line.split()\r
+                        new_format_guid = struct2stream(ModifyGuidFormat(guid.strip()))\r
+                        self.tooldef[new_format_guid] = GUIDTool(\r
+                            guid.strip(), short_name.strip(), command.strip())\r
+                except:\r
+                    logger.error("GuidTool load error!")\r
+                    continue\r
+        else:\r
+            self.tooldef.update(self.default_tools)\r
+\r
+    def __getitem__(self, guid):\r
+        if not self.tooldef:\r
+            self.LoadingTools()\r
+        guid_tool = self.tooldef.get(guid)\r
+        if guid_tool:\r
+            self.VerifyTools(guid_tool)\r
+            return guid_tool\r
+        else:\r
+            logger.error("{} GuidTool is not defined!".format(guid))\r
+            raise Exception("Process Failed: is not defined!")\r
+\r
+guidtools = GUIDTools()\r
+\r
diff --git a/BaseTools/Source/Python/FMMT/utils/FmmtLogger.py b/BaseTools/Source/Python/FMMT/utils/FmmtLogger.py
new file mode 100644 (file)
index 0000000..385f098
--- /dev/null
@@ -0,0 +1,31 @@
+## @file\r
+# This file is used to define the Fmmt Logger.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+##\r
+\r
+import logging\r
+import sys\r
+import os\r
+\r
+logfile = 'FMMT_Build.log'\r
+if os.path.exists(logfile):\r
+    os.remove(logfile)\r
+\r
+FmmtLogger = logging.getLogger('FMMT')\r
+FmmtLogger.setLevel(logging.DEBUG)\r
+\r
+log_stream_handler=logging.StreamHandler(sys.stdout)\r
+log_file_handler=logging.FileHandler(logfile)\r
+log_stream_handler.setLevel(logging.INFO)\r
+\r
+stream_format=logging.Formatter("%(levelname)-8s: %(message)s")\r
+file_format=logging.Formatter("%(levelname)-8s: %(message)s")\r
+\r
+log_stream_handler.setFormatter(stream_format)\r
+log_file_handler.setFormatter(file_format)\r
+\r
+FmmtLogger.addHandler(log_stream_handler)\r
+FmmtLogger.addHandler(log_file_handler)\r
diff --git a/BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py b/BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py
new file mode 100644 (file)
index 0000000..7dafcae
--- /dev/null
@@ -0,0 +1,55 @@
+## @file\r
+# This file is used to define the printer for Bios layout.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from utils.FmmtLogger import FmmtLogger as logger\r
+\r
+def GetFormatter(layout_format: str):\r
+    if layout_format == 'json':\r
+        return JsonFormatter()\r
+    elif layout_format == 'yaml':\r
+        return YamlFormatter()\r
+    elif layout_format == 'html':\r
+        return HtmlFormatter()\r
+    else:\r
+        return TxtFormatter()\r
+\r
+class Formatter(object):\r
+    def dump(self, layoutdict, layoutlist, outputfile: str=None) -> None:\r
+        raise NotImplemented\r
+\r
+class JsonFormatter(Formatter):\r
+    def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=None) -> None:\r
+        try:\r
+            import json\r
+        except:\r
+            TxtFormatter().dump(layoutdict, layoutlist, outputfile)\r
+            return\r
+        print(outputfile)\r
+        if outputfile:\r
+            with open(outputfile,"w") as fw:\r
+                json.dump(layoutdict, fw, indent=2)\r
+        else:\r
+            print(json.dumps(layoutdict,indent=2))\r
+\r
+class TxtFormatter(Formatter):\r
+    def LogPrint(self,layoutlist: list) -> None:\r
+        for item in layoutlist:\r
+            print(item)\r
+        print('\n')\r
+\r
+    def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=None) -> None:\r
+        logger.info('Binary Layout Info is saved in {} file.'.format(outputfile))\r
+        with open(outputfile, "w") as f:\r
+            for item in layoutlist:\r
+                f.writelines(item + '\n')\r
+\r
+class YamlFormatter(Formatter):\r
+    def dump(self,layoutdict, layoutlist, outputfile = None):\r
+        TxtFormatter().dump(layoutdict, layoutlist, outputfile)\r
+\r
+class HtmlFormatter(Formatter):\r
+    def dump(self,layoutdict, layoutlist, outputfile = None):\r
+        TxtFormatter().dump(layoutdict, layoutlist, outputfile)\r
\ No newline at end of file
diff --git a/BaseTools/Source/Python/FirmwareStorageFormat/Common.py b/BaseTools/Source/Python/FirmwareStorageFormat/Common.py
new file mode 100644 (file)
index 0000000..5082268
--- /dev/null
@@ -0,0 +1,85 @@
+## @file\r
+# This file is used to define the common C struct and functions.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from ctypes import *\r
+import uuid\r
+\r
+# ZeroGuid = uuid.UUID('{00000000-0000-0000-0000-000000000000}')\r
+# EFI_FIRMWARE_FILE_SYSTEM2_GUID = uuid.UUID('{8C8CE578-8A3D-4f1c-9935-896185C32DD3}')\r
+# EFI_FIRMWARE_FILE_SYSTEM3_GUID = uuid.UUID('{5473C07A-3DCB-4dca-BD6F-1E9689E7349A}')\r
+# EFI_FFS_VOLUME_TOP_FILE_GUID = uuid.UUID('{1BA0062E-C779-4582-8566-336AE8F78F09}')\r
+\r
+EFI_FIRMWARE_FILE_SYSTEM2_GUID = uuid.UUID("8c8ce578-8a3d-4f1c-9935-896185c32dd3")\r
+EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE = b'x\xe5\x8c\x8c=\x8a\x1cO\x995\x89a\x85\xc3-\xd3'\r
+# EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE = EFI_FIRMWARE_FILE_SYSTEM2_GUID.bytes\r
+EFI_FIRMWARE_FILE_SYSTEM3_GUID = uuid.UUID("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")\r
+# EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE = b'x\xe5\x8c\x8c=\x8a\x1cO\x995\x89a\x85\xc3-\xd3'\r
+EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE = b'z\xc0sT\xcb=\xcaM\xbdo\x1e\x96\x89\xe74\x9a'\r
+EFI_SYSTEM_NVDATA_FV_GUID = uuid.UUID("fff12b8d-7696-4c8b-a985-2747075b4f50")\r
+EFI_SYSTEM_NVDATA_FV_GUID_BYTE = b"\x8d+\xf1\xff\x96v\x8bL\xa9\x85'G\x07[OP"\r
+EFI_FFS_VOLUME_TOP_FILE_GUID = uuid.UUID("1ba0062e-c779-4582-8566-336ae8f78f09")\r
+EFI_FFS_VOLUME_TOP_FILE_GUID_BYTE = b'.\x06\xa0\x1by\xc7\x82E\x85f3j\xe8\xf7\x8f\t'\r
+ZEROVECTOR_BYTE = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\r
+PADVECTOR = uuid.UUID("ffffffff-ffff-ffff-ffff-ffffffffffff")\r
+FVH_SIGNATURE = b'_FVH'\r
+\r
+#Alignment\r
+SECTION_COMMON_ALIGNMENT = 4\r
+FFS_COMMON_ALIGNMENT = 8\r
+\r
+class GUID(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Guid1',            c_uint32),\r
+        ('Guid2',            c_uint16),\r
+        ('Guid3',            c_uint16),\r
+        ('Guid4',            ARRAY(c_uint8, 8)),\r
+    ]\r
+\r
+    def from_list(self, listformat: list) -> None:\r
+        self.Guid1 = listformat[0]\r
+        self.Guid2 = listformat[1]\r
+        self.Guid3 = listformat[2]\r
+        for i in range(8):\r
+            self.Guid4[i] = listformat[i+3]\r
+\r
+    def __cmp__(self, otherguid) -> bool:\r
+        if not isinstance(otherguid, GUID):\r
+            return 'Input is not the GUID instance!'\r
+        rt = False\r
+        if self.Guid1 == otherguid.Guid1 and self.Guid2 == otherguid.Guid2 and self.Guid3 == otherguid.Guid3:\r
+            rt = True\r
+            for i in range(8):\r
+                rt = rt & (self.Guid4[i] == otherguid.Guid4[i])\r
+        return rt\r
+\r
+def ModifyGuidFormat(target_guid: str) -> GUID:\r
+    target_guid = target_guid.replace('-', '')\r
+    target_list = []\r
+    start = [0,8,12,16,18,20,22,24,26,28,30]\r
+    end = [8,12,16,18,20,22,24,26,28,30,32]\r
+    num = len(start)\r
+    for pos in range(num):\r
+        new_value = int(target_guid[start[pos]:end[pos]], 16)\r
+        target_list.append(new_value)\r
+    new_format = GUID()\r
+    new_format.from_list(target_list)\r
+    return new_format\r
+\r
+\r
+# Get data from ctypes to bytes.\r
+def struct2stream(s) -> bytes:\r
+    length = sizeof(s)\r
+    p = cast(pointer(s), POINTER(c_char * length))\r
+    return p.contents.raw\r
+\r
+\r
+\r
+def GetPadSize(Size: int, alignment: int) -> int:\r
+    if Size % alignment == 0:\r
+        return 0\r
+    Pad_Size = alignment - Size % alignment\r
+    return Pad_Size\r
diff --git a/BaseTools/Source/Python/FirmwareStorageFormat/FfsFileHeader.py b/BaseTools/Source/Python/FirmwareStorageFormat/FfsFileHeader.py
new file mode 100644 (file)
index 0000000..e9c619d
--- /dev/null
@@ -0,0 +1,66 @@
+## @file\r
+# This file is used to define the Ffs Header C Struct.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from struct import *\r
+from ctypes import *\r
+from FirmwareStorageFormat.Common import *\r
+\r
+EFI_FFS_FILE_HEADER_LEN = 24\r
+EFI_FFS_FILE_HEADER2_LEN = 32\r
+\r
+class CHECK_SUM(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Header',                   c_uint8),\r
+        ('File',                     c_uint8),\r
+    ]\r
+\r
+class EFI_FFS_INTEGRITY_CHECK(Union):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Checksum',                 CHECK_SUM),\r
+        ('Checksum16',               c_uint16),\r
+    ]\r
+\r
+\r
+class EFI_FFS_FILE_HEADER(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Name',                     GUID),\r
+        ('IntegrityCheck',           EFI_FFS_INTEGRITY_CHECK),\r
+        ('Type',                     c_uint8),\r
+        ('Attributes',               c_uint8),\r
+        ('Size',                     ARRAY(c_uint8, 3)),\r
+        ('State',                    c_uint8),\r
+    ]\r
+\r
+    @property\r
+    def FFS_FILE_SIZE(self) -> int:\r
+        return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16\r
+\r
+    @property\r
+    def HeaderLength(self) -> int:\r
+        return 24\r
+\r
+class EFI_FFS_FILE_HEADER2(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Name',                     GUID),\r
+        ('IntegrityCheck',           EFI_FFS_INTEGRITY_CHECK),\r
+        ('Type',                     c_uint8),\r
+        ('Attributes',               c_uint8),\r
+        ('Size',                     ARRAY(c_uint8, 3)),\r
+        ('State',                    c_uint8),\r
+        ('ExtendedSize',             c_uint64),\r
+    ]\r
+\r
+    @property\r
+    def FFS_FILE_SIZE(self) -> int:\r
+        return self.ExtendedSize\r
+\r
+    @property\r
+    def HeaderLength(self) -> int:\r
+        return 32\r
diff --git a/BaseTools/Source/Python/FirmwareStorageFormat/FvHeader.py b/BaseTools/Source/Python/FirmwareStorageFormat/FvHeader.py
new file mode 100644 (file)
index 0000000..078beda
--- /dev/null
@@ -0,0 +1,112 @@
+## @file\r
+# This file is used to define the FV Header C Struct.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from ast import Str\r
+from struct import *\r
+from ctypes import *\r
+from FirmwareStorageFormat.Common import *\r
+\r
+class EFI_FV_BLOCK_MAP_ENTRY(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('NumBlocks',            c_uint32),\r
+        ('Length',               c_uint32),\r
+    ]\r
+\r
+\r
+class EFI_FIRMWARE_VOLUME_HEADER(Structure):\r
+    _fields_ = [\r
+        ('ZeroVector',           ARRAY(c_uint8, 16)),\r
+        ('FileSystemGuid',       GUID),\r
+        ('FvLength',             c_uint64),\r
+        ('Signature',            c_uint32),\r
+        ('Attributes',           c_uint32),\r
+        ('HeaderLength',         c_uint16),\r
+        ('Checksum',             c_uint16),\r
+        ('ExtHeaderOffset',      c_uint16),\r
+        ('Reserved',             c_uint8),\r
+        ('Revision',             c_uint8),\r
+        ('BlockMap',             ARRAY(EFI_FV_BLOCK_MAP_ENTRY, 1)),\r
+        ]\r
+\r
+def Refine_FV_Header(nums):\r
+    class EFI_FIRMWARE_VOLUME_HEADER(Structure):\r
+        _fields_ = [\r
+            ('ZeroVector',           ARRAY(c_uint8, 16)),\r
+            ('FileSystemGuid',       GUID),\r
+            ('FvLength',             c_uint64),\r
+            ('Signature',            c_uint32),\r
+            ('Attributes',           c_uint32),\r
+            ('HeaderLength',         c_uint16),\r
+            ('Checksum',             c_uint16),\r
+            ('ExtHeaderOffset',      c_uint16),\r
+            ('Reserved',             c_uint8),\r
+            ('Revision',             c_uint8),\r
+            ('BlockMap',             ARRAY(EFI_FV_BLOCK_MAP_ENTRY, nums)),\r
+            ]\r
+    return EFI_FIRMWARE_VOLUME_HEADER\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):\r
+    _fields_ = [\r
+        ('FvName',               GUID),\r
+        ('ExtHeaderSize',        c_uint32)\r
+        ]\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY(Structure):\r
+    _fields_ = [\r
+        ('ExtEntrySize',         c_uint16),\r
+        ('ExtEntryType',         c_uint16)\r
+        ]\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE_0(Structure):\r
+    _fields_ = [\r
+        ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+        ('TypeMask',             c_uint32)\r
+        ]\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure):\r
+    _fields_ = [\r
+        ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+        ('TypeMask',             c_uint32),\r
+        ('Types',                ARRAY(GUID, 1))\r
+        ]\r
+\r
+def Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums: int) -> EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE:\r
+    class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure):\r
+        _fields_ = [\r
+            ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+            ('TypeMask',             c_uint32),\r
+            ('Types',                ARRAY(GUID, nums))\r
+        ]\r
+    return EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure)\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE_0(Structure):\r
+    _fields_ = [\r
+        ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+        ('FormatType',           GUID)\r
+        ]\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure):\r
+    _fields_ = [\r
+        ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+        ('FormatType',           GUID),\r
+        ('Data',                 ARRAY(c_uint8, 1))\r
+        ]\r
+\r
+def Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums: int) -> EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE:\r
+    class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure):\r
+        _fields_ = [\r
+            ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+            ('FormatType',           GUID),\r
+            ('Data',                 ARRAY(c_uint8, nums))\r
+        ]\r
+    return EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure)\r
+\r
+class EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE(Structure):\r
+    _fields_ = [\r
+        ('Hdr',                  EFI_FIRMWARE_VOLUME_EXT_ENTRY),\r
+        ('UsedSize',             c_uint32)\r
+        ]\r
diff --git a/BaseTools/Source/Python/FirmwareStorageFormat/SectionHeader.py b/BaseTools/Source/Python/FirmwareStorageFormat/SectionHeader.py
new file mode 100644 (file)
index 0000000..ee6a636
--- /dev/null
@@ -0,0 +1,110 @@
+## @file\r
+# This file is used to define the Section Header C Struct.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
+from struct import *\r
+from ctypes import *\r
+from FirmwareStorageFormat.Common import *\r
+\r
+EFI_COMMON_SECTION_HEADER_LEN = 4\r
+EFI_COMMON_SECTION_HEADER2_LEN = 8\r
+\r
+class EFI_COMMON_SECTION_HEADER(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Size',                     ARRAY(c_uint8, 3)),\r
+        ('Type',                     c_uint8),\r
+    ]\r
+\r
+    @property\r
+    def SECTION_SIZE(self) -> int:\r
+        return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16\r
+\r
+    def Common_Header_Size(self) -> int:\r
+        return 4\r
+\r
+class EFI_COMMON_SECTION_HEADER2(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('Size',                     ARRAY(c_uint8, 3)),\r
+        ('Type',                     c_uint8),\r
+        ('ExtendedSize',             c_uint32),\r
+    ]\r
+\r
+    @property\r
+    def SECTION_SIZE(self) -> int:\r
+        return self.ExtendedSize\r
+\r
+    def Common_Header_Size(self) -> int:\r
+        return 8\r
+\r
+class EFI_COMPRESSION_SECTION(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('UncompressedLength',       c_uint32),\r
+        ('CompressionType',          c_uint8),\r
+    ]\r
+\r
+    def ExtHeaderSize(self) -> int:\r
+        return 5\r
+\r
+class EFI_FREEFORM_SUBTYPE_GUID_SECTION(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('SubTypeGuid',              GUID),\r
+    ]\r
+\r
+    def ExtHeaderSize(self) -> int:\r
+        return 16\r
+\r
+class EFI_GUID_DEFINED_SECTION(Structure):\r
+    _pack_ = 1\r
+    _fields_ = [\r
+        ('SectionDefinitionGuid',    GUID),\r
+        ('DataOffset',               c_uint16),\r
+        ('Attributes',               c_uint16),\r
+    ]\r
+\r
+    def ExtHeaderSize(self) -> int:\r
+        return 20\r
+\r
+def Get_USER_INTERFACE_Header(nums: int):\r
+    class EFI_SECTION_USER_INTERFACE(Structure):\r
+        _pack_ = 1\r
+        _fields_ = [\r
+            ('FileNameString',       ARRAY(c_uint16, nums)),\r
+        ]\r
+\r
+        def ExtHeaderSize(self) -> int:\r
+            return 2 * nums\r
+\r
+        def GetUiString(self) -> str:\r
+            UiString = ''\r
+            for i in range(nums):\r
+                if self.FileNameString[i]:\r
+                    UiString += chr(self.FileNameString[i])\r
+            return UiString\r
+\r
+    return EFI_SECTION_USER_INTERFACE\r
+\r
+def Get_VERSION_Header(nums: int):\r
+    class EFI_SECTION_VERSION(Structure):\r
+        _pack_ = 1\r
+        _fields_ = [\r
+            ('BuildNumber',          c_uint16),\r
+            ('VersionString',        ARRAY(c_uint16, nums)),\r
+        ]\r
+\r
+        def ExtHeaderSize(self) -> int:\r
+            return 2 * (nums+1)\r
+\r
+        def GetVersionString(self) -> str:\r
+            VersionString = ''\r
+            for i in range(nums):\r
+                if self.VersionString[i]:\r
+                    VersionString += chr(self.VersionString[i])\r
+            return VersionString\r
+\r
+    return EFI_SECTION_VERSION\r
diff --git a/BaseTools/Source/Python/FirmwareStorageFormat/__init__.py b/BaseTools/Source/Python/FirmwareStorageFormat/__init__.py
new file mode 100644 (file)
index 0000000..335653c
--- /dev/null
@@ -0,0 +1,6 @@
+## @file\r
+# This file is used to define the Firmware Storage Format.\r
+#\r
+# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+##\r
\ No newline at end of file