mirror of
https://github.com/acidanthera/audk.git
synced 2025-04-08 17:05:09 +02:00
BaseTools: Add FMMT Python Tool
The FMMT python tool is used for firmware files operation, which has the Fv/FFs-based 'View'&'Add'&'Delete'&'Replace' operation function: 1.Parse a FD(Firmware Device) / FV(Firmware Volume) / FFS(Firmware Files) 2.Add a new FFS into a FV file (both included in a FD file or not) 3.Replace an FFS in a FV file with a new FFS file 4.Delete an FFS in a FV file (both included in a FD file or not) 5.Extract the FFS from a FV file (both included in a FD file or not) This version of FMMT Python tool does not support PEIM rebase feature, this feature will be added in future update. Currently the FMMT C tool is saved in edk2-staging repo, but its quality and coding style can't meet the Edk2 quality, which is hard to maintain (Hard/Duplicate Code; Regression bugs; Restrict usage). The new Python version keeps same functions with origin C version. It has higher quality and better coding style, and it is much easier to extend new functions and to maintain. REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1847 RFC Link: https://edk2.groups.io/g/devel/message/82877 Staging Link: https://github.com/tianocore/edk2-staging/tree/PyFMMT Cc: Bob Feng <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>
This commit is contained in:
parent
101f4c7892
commit
a64b944942
14
BaseTools/BinWrappers/PosixLike/FMMT
Executable file
14
BaseTools/BinWrappers/PosixLike/FMMT
Executable file
@ -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 "$@"
|
4
BaseTools/BinWrappers/WindowsLike/FMMT.bat
Normal file
4
BaseTools/BinWrappers/WindowsLike/FMMT.bat
Normal file
@ -0,0 +1,4 @@
|
||||
@setlocal
|
||||
@set ToolName=%~n0%
|
||||
@set PYTHONPATH=%PYTHONPATH%;%BASE_TOOLS_PATH%\Source\Python;%BASE_TOOLS_PATH%\Source\Python\FMMT
|
||||
@%PYTHON_COMMAND% -m %ToolName%.%ToolName% %*
|
153
BaseTools/Source/Python/FMMT/FMMT.py
Normal file
153
BaseTools/Source/Python/FMMT/FMMT.py
Normal file
@ -0,0 +1,153 @@
|
||||
# @file
|
||||
# Firmware Module Management Tool.
|
||||
#
|
||||
# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
#
|
||||
##
|
||||
|
||||
# Import Modules
|
||||
#
|
||||
import argparse
|
||||
from core.FMMTOperation import *
|
||||
|
||||
parser = argparse.ArgumentParser(description='''
|
||||
View the Binary Structure of FD/FV/Ffs/Section, and Delete/Extract/Add/Replace a Ffs from/into a FV.
|
||||
''')
|
||||
parser.add_argument("--version", action="version", version='%(prog)s Version 1.0',
|
||||
help="Print debug information.")
|
||||
parser.add_argument("-v", "--View", dest="View", nargs='+',
|
||||
help="View each FV and the named files within each FV: '-v inputfile outputfile, inputfiletype(.Fd/.Fv/.ffs/.sec)'")
|
||||
parser.add_argument("-d", "--Delete", dest="Delete", nargs='+',
|
||||
help="Delete a Ffs from FV: '-d inputfile TargetFvName(Optional) TargetFfsName outputfile\
|
||||
If not given TargetFvName, all the existed target Ffs will be deleted'")
|
||||
parser.add_argument("-e", "--Extract", dest="Extract", nargs='+',
|
||||
help="Extract a Ffs Info: '-e inputfile TargetFvName(Optional) TargetFfsName outputfile\
|
||||
If not given TargetFvName, the first found target Ffs will be extracted'")
|
||||
parser.add_argument("-a", "--Add", dest="Add", nargs='+',
|
||||
help="Add a Ffs into a FV:'-a inputfile TargetFvName newffsfile outputfile'")
|
||||
parser.add_argument("-r", "--Replace", dest="Replace", nargs='+',
|
||||
help="Replace a Ffs in a FV: '-r inputfile TargetFvName(Optional) TargetFfsName newffsfile outputfile\
|
||||
If not given TargetFvName, all the existed target Ffs will be replaced with new Ffs file)'")
|
||||
parser.add_argument("-l", "--LayoutFileName", dest="LayoutFileName", nargs='+',
|
||||
help="The output file which saves Binary layout: '-l xxx.txt'/'-l xxx.json'\
|
||||
If only provide file format as 'txt', \
|
||||
the file will be generated with default name (Layout_'InputFileName'.txt). \
|
||||
Currently supports two formats: json, txt. More formats will be added in the future")
|
||||
parser.add_argument("-c", "--ConfigFilePath", dest="ConfigFilePath", nargs='+',
|
||||
help="Provide the target FmmtConf.ini file path: '-c C:\Code\FmmtConf.ini' \
|
||||
FmmtConf file saves the target guidtool used in compress/uncompress process.\
|
||||
If do not provide, FMMT tool will search the inputfile folder for FmmtConf.ini firstly, if not found,\
|
||||
the FmmtConf.ini saved in FMMT tool's folder will be used as default.")
|
||||
|
||||
def print_banner():
|
||||
print("")
|
||||
|
||||
class FMMT():
|
||||
def __init__(self) -> None:
|
||||
self.firmware_packet = {}
|
||||
|
||||
def SetConfigFilePath(self, configfilepath:str) -> str:
|
||||
os.environ['FmmtConfPath'] = os.path.abspath(configfilepath)
|
||||
|
||||
def SetDestPath(self, inputfile:str) -> str:
|
||||
os.environ['FmmtConfPath'] = ''
|
||||
self.dest_path = os.path.dirname(os.path.abspath(inputfile))
|
||||
old_env = os.environ['PATH']
|
||||
os.environ['PATH'] = self.dest_path + os.pathsep + old_env
|
||||
|
||||
def CheckFfsName(self, FfsName:str) -> str:
|
||||
try:
|
||||
return uuid.UUID(FfsName)
|
||||
except:
|
||||
return FfsName
|
||||
|
||||
def GetFvName(self, FvName:str) -> str:
|
||||
try:
|
||||
return uuid.UUID(FvName)
|
||||
except:
|
||||
return FvName
|
||||
|
||||
def View(self, inputfile: str, layoutfilename: str=None, outputfile: str=None) -> None:
|
||||
# ViewFile(inputfile, ROOT_TYPE, logfile, outputfile)
|
||||
self.SetDestPath(inputfile)
|
||||
filetype = os.path.splitext(inputfile)[1].lower()
|
||||
if filetype == '.fd':
|
||||
ROOT_TYPE = ROOT_TREE
|
||||
elif filetype == '.fv':
|
||||
ROOT_TYPE = ROOT_FV_TREE
|
||||
elif filetype == '.ffs':
|
||||
ROOT_TYPE = ROOT_FFS_TREE
|
||||
elif filetype == '.sec':
|
||||
ROOT_TYPE = ROOT_SECTION_TREE
|
||||
else:
|
||||
ROOT_TYPE = ROOT_TREE
|
||||
ViewFile(inputfile, ROOT_TYPE, layoutfilename, outputfile)
|
||||
|
||||
def Delete(self, inputfile: str, TargetFfs_name: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
self.SetDestPath(inputfile)
|
||||
if Fv_name:
|
||||
DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outputfile, self.GetFvName(Fv_name))
|
||||
else:
|
||||
DeleteFfs(inputfile, self.CheckFfsName(TargetFfs_name), outputfile)
|
||||
|
||||
def Extract(self, inputfile: str, Ffs_name: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
self.SetDestPath(inputfile)
|
||||
if Fv_name:
|
||||
ExtractFfs(inputfile, self.CheckFfsName(Ffs_name), outputfile, self.GetFvName(Fv_name))
|
||||
else:
|
||||
ExtractFfs(inputfile, self.CheckFfsName(Ffs_name), outputfile)
|
||||
|
||||
def Add(self, inputfile: str, Fv_name: str, newffsfile: str, outputfile: str) -> None:
|
||||
self.SetDestPath(inputfile)
|
||||
AddNewFfs(inputfile, self.CheckFfsName(Fv_name), newffsfile, outputfile)
|
||||
|
||||
def Replace(self,inputfile: str, Ffs_name: str, newffsfile: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
self.SetDestPath(inputfile)
|
||||
if Fv_name:
|
||||
ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfile, outputfile, self.GetFvName(Fv_name))
|
||||
else:
|
||||
ReplaceFfs(inputfile, self.CheckFfsName(Ffs_name), newffsfile, outputfile)
|
||||
|
||||
|
||||
def main():
|
||||
args=parser.parse_args()
|
||||
status=0
|
||||
|
||||
try:
|
||||
fmmt=FMMT()
|
||||
if args.ConfigFilePath:
|
||||
fmmt.SetConfigFilePath(args.ConfigFilePath[0])
|
||||
if args.View:
|
||||
if args.LayoutFileName:
|
||||
fmmt.View(args.View[0], args.LayoutFileName[0])
|
||||
else:
|
||||
fmmt.View(args.View[0])
|
||||
elif args.Delete:
|
||||
if len(args.Delete) == 4:
|
||||
fmmt.Delete(args.Delete[0],args.Delete[2],args.Delete[3],args.Delete[1])
|
||||
else:
|
||||
fmmt.Delete(args.Delete[0],args.Delete[1],args.Delete[2])
|
||||
elif args.Extract:
|
||||
if len(args.Extract) == 4:
|
||||
fmmt.Extract(args.Extract[0],args.Extract[2],args.Extract[3], args.Extract[1])
|
||||
else:
|
||||
fmmt.Extract(args.Extract[0],args.Extract[1],args.Extract[2])
|
||||
elif args.Add:
|
||||
fmmt.Add(args.Add[0],args.Add[1],args.Add[2],args.Add[3])
|
||||
elif args.Replace:
|
||||
if len(args.Replace) == 5:
|
||||
fmmt.Replace(args.Replace[0],args.Replace[2],args.Replace[3],args.Replace[4],args.Replace[1])
|
||||
else:
|
||||
fmmt.Replace(args.Replace[0],args.Replace[1],args.Replace[2],args.Replace[3])
|
||||
else:
|
||||
parser.print_help()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return status
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
11
BaseTools/Source/Python/FMMT/FmmtConf.ini
Normal file
11
BaseTools/Source/Python/FMMT/FmmtConf.ini
Normal file
@ -0,0 +1,11 @@
|
||||
## @file
|
||||
# This file is used to define the FMMT dependent external tool guid.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
a31280ad-481e-41b6-95e8-127f4c984779 TIANO TianoCompress
|
||||
ee4e5898-3914-4259-9d6e-dc7bd79403cf LZMA LzmaCompress
|
||||
fc1bcdb0-7d31-49aa-936a-a4600d9dd083 CRC32 GenCrc32
|
||||
d42ae6bd-1352-4bfb-909a-ca72a6eae889 LZMAF86 LzmaF86Compress
|
||||
3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress
|
BIN
BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png
Normal file
BIN
BaseTools/Source/Python/FMMT/Img/FirmwareVolumeFormat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png
Normal file
BIN
BaseTools/Source/Python/FMMT/Img/NodeTreeFormat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
184
BaseTools/Source/Python/FMMT/README.md
Normal file
184
BaseTools/Source/Python/FMMT/README.md
Normal file
@ -0,0 +1,184 @@
|
||||
# FMMT
|
||||
## Overview
|
||||
This FMMT tool is the python implementation of the edk2 FMMT tool which locates at https://github.com/tianocore/edk2-staging/tree/FceFmmt.
|
||||
This implementation has the same usage as the edk2 FMMT, but it's more readable and relaiable.
|
||||
|
||||
# FMMT User Guide
|
||||
|
||||
#### Last updated April 28, 2022
|
||||
|
||||
Important Changes and Updates:
|
||||
|
||||
- Oct 13, 2021 Initial Draft of FMMT Python Tool
|
||||
- Apr 28, 2022 Optimize functions & Command line
|
||||
|
||||
#### Note:
|
||||
|
||||
- FMMT Python Tool keeps same function with origin FMMT C Tool. It is much easier to maintain and extend other functions.
|
||||
|
||||
#### Known issue:
|
||||
|
||||
- Currently, FMMT Python tool does not support PEIM rebase feature, this feature will be added in future update.
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
## 1.1 Overview
|
||||
|
||||
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?.
|
||||
|
||||

|
||||
|
||||
? Figure 1. The Firmware Volume Format
|
||||
|
||||
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.
|
||||
|
||||
## 1.2 Tool Capabilities
|
||||
|
||||
The FMMT tool is capable of:
|
||||
|
||||
- Parse a FD (Firmware Device) / FV (Firmware Volume) / FFS (Firmware Files)
|
||||
|
||||
- Add a new FFS into a FV file (both included in a FD file or not)
|
||||
|
||||
- Replace an FFS in a FV file with a new FFS file
|
||||
|
||||
- Delete an FFS in a FV file (both included in a FD file or not)
|
||||
|
||||
- Extract the FFS from a FV file (both included in a FD file or not)
|
||||
|
||||
## 1.3 References
|
||||
|
||||
| Document |
|
||||
| ------------------------------------------------ |
|
||||
| UEFI Platform Initialization (PI) Specification |
|
||||
|
||||
# 2. FMMT Python Tool Usage
|
||||
|
||||
## 2.1 Required Files
|
||||
|
||||
### 2.1.1 Independent use
|
||||
|
||||
When independent use the FMMT Python Tool, the following files and settings are required:
|
||||
|
||||
- GuidTool executable files used for Decompress/Compress Firmware data.
|
||||
|
||||
- Environment variables path with GuidTool path setting.
|
||||
|
||||
### 2.1.2 Use with Build System
|
||||
|
||||
When use the FMMT Python Tool with Build System:
|
||||
|
||||
- If only use Edk2 based GuidTool, do not need other preparation.
|
||||
|
||||
- If use other customized GuidTool, need prepare the config file with GuidTool info. The syntax for GuidTool definition shown as follow:
|
||||
|
||||
***ToolsGuid ShortName Command***
|
||||
|
||||
-- Example: ***3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress***
|
||||
|
||||
## 2.2 Syntax
|
||||
|
||||
### 2.2.1 Syntax for Parse file
|
||||
|
||||
***-v < Inputfile > < Outputfile > -l < LogFileType > -c < ConfigFilePath >***
|
||||
|
||||
- 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.
|
||||
- Ex: py -3 FMMT.py -v test.fd
|
||||
|
||||
### 2.2.2 Syntax for Add a new FFS
|
||||
|
||||
***-a < Inputfile > < TargetFvName/TargetFvGuid > < NewFfsFile > < Outputfile >***
|
||||
|
||||
- Add the *NewFfsFile* into *Inputfile*. *TargetFvName/TargetFvGuid* (Name or Guid) is the TargetFv which *NewFfsFile* will be added into.
|
||||
- Ex: py -3 FMMT.py -a Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 NewAdd.ffs output.fd
|
||||
|
||||
### 2.2.3 Syntax for Delete an FFS
|
||||
|
||||
***-d < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < Outputfile >***
|
||||
|
||||
- Delete the Ffs from *Inputfile*. TargetFfsName (Guid) is the TargetFfs which will be deleted. *TargetFvName/TargetFvGuid* is optional, which is the parent of TargetFfs*.*
|
||||
- Ex: py -3 FMMT.py -d Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei output.fd
|
||||
|
||||
### 2.2.4 Syntax for Replace an FFS
|
||||
|
||||
? ***-r < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < NewFfsFile > < Outputfile >***
|
||||
|
||||
- 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*.*
|
||||
- Ex: py -3 FMMT.py -r Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei NewS3Resume2Pei.ffs output.fd
|
||||
|
||||
### 2.2.5 Syntax for Extract an FFS
|
||||
|
||||
***-e < Inputfile > < TargetFvName/TargetFvGuid > < TargetFfsName > < Outputfile >***
|
||||
|
||||
- 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*.*
|
||||
- Ex: py -3 FMMT.py -e Ovmf.fd 6938079b-b503-4e3d-9d24-b28337a25806 S3Resume2Pei output.fd
|
||||
|
||||
# 3. FMMT Python Tool Design
|
||||
|
||||
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).
|
||||
|
||||
## 3.1 NodeTree
|
||||
|
||||
A whole NodeTree saves all the Firmware info.
|
||||
|
||||
- Parent & Child relationship figured out the Firmware layout.
|
||||
|
||||
- Each Node have several fields. ?Data? field saves an FirmwareClass instance which contains all the data info of the info.
|
||||
|
||||
### 3.1.1 NodeTree Format
|
||||
|
||||
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?:
|
||||
|
||||
- A Root Node is initialized.
|
||||
|
||||
- Use the ?FV Signature? as FV key to split Whole FD Data. ?FV0?, ?FV1?, ?FV2?? Node created.
|
||||
|
||||
- After FV level Node created, use the ?Ffs Data Size? as FFS key to split each FV Data. ?Ffs0?...Node created.
|
||||
|
||||
- After FFS level Node created, use the ?Section Data Size? as Section key to split each Ffs Data. ?Section0?...Node created.
|
||||
|
||||
- If some of Section includes other Sections, continue use the ?Section Data Size? as Section key to split each Section Data.
|
||||
|
||||
- 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)
|
||||
|
||||

|
||||
|
||||
? Figure 2. The NodeTree format
|
||||
|
||||
### 3.1.2 Node Factory and Product
|
||||
|
||||
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.
|
||||
|
||||
Each Node have its Factory to create Product and use Product ParserData function to deal with the data.
|
||||
|
||||
## 3.2 GuidTool
|
||||
|
||||
There are two ways to set the GuidTool. One from Config file, another from environment variables.
|
||||
|
||||
Current GuidTool first check if has Config file.
|
||||
|
||||
- If have, load the config GuidTool Information.
|
||||
|
||||
- Else get from environment variables.
|
||||
|
||||
### 3.2.1 Get from Config file
|
||||
|
||||
- Config file should in same folder with FMMT.py or the path in environment variables.
|
||||
|
||||
- Content should follow the format:
|
||||
|
||||
***ToolsGuid ShortName Command***
|
||||
|
||||
### 3.2.2 Get from Environment Variables
|
||||
|
||||
- The GuidTool Command used must be set in environment variables.
|
||||
|
||||
### 3.2.3 Edk2 Based GuidTool
|
||||
|
||||
| ***Guid*** | ***ShortName*** | ***Command*** |
|
||||
| ------------------------------------------ | --------------- | --------------------- |
|
||||
| ***a31280ad-481e-41b6-95e8-127f4c984779*** | ***TIANO*** | ***TianoCompress*** |
|
||||
| ***ee4e5898-3914-4259-9d6e-dc7bd79403cf*** | ***LZMA*** | ***LzmaCompress*** |
|
||||
| ***fc1bcdb0-7d31-49aa-936a-a4600d9dd083*** | ***CRC32*** | ***GenCrc32*** |
|
||||
| ***d42ae6bd-1352-4bfb-909a-ca72a6eae889*** | ***LZMAF86*** | ***LzmaF86Compress*** |
|
||||
| ***3d532050-5cda-4fd0-879e-0f7f630d5afb*** | ***BROTLI*** | ***BrotliCompress*** |
|
6
BaseTools/Source/Python/FMMT/__init__.py
Normal file
6
BaseTools/Source/Python/FMMT/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
## @file
|
||||
# This file is used to define the FMMT dependent external tool.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
380
BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py
Normal file
380
BaseTools/Source/Python/FMMT/core/BinaryFactoryProduct.py
Normal file
@ -0,0 +1,380 @@
|
||||
## @file
|
||||
# This file is used to implement of the various bianry parser.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from re import T
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from core.BiosTreeNode import *
|
||||
from core.BiosTree import *
|
||||
from core.GuidTools import GUIDTools
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
ROOT_TREE = 'ROOT'
|
||||
ROOT_FV_TREE = 'ROOT_FV_TREE'
|
||||
ROOT_FFS_TREE = 'ROOT_FFS_TREE'
|
||||
ROOT_SECTION_TREE = 'ROOT_SECTION_TREE'
|
||||
|
||||
FV_TREE = 'FV'
|
||||
DATA_FV_TREE = 'DATA_FV'
|
||||
FFS_TREE = 'FFS'
|
||||
FFS_PAD = 'FFS_PAD'
|
||||
FFS_FREE_SPACE = 'FFS_FREE_SPACE'
|
||||
SECTION_TREE = 'SECTION'
|
||||
SEC_FV_TREE = 'SEC_FV_IMAGE'
|
||||
BINARY_DATA = 'BINARY'
|
||||
Fv_count = 0
|
||||
|
||||
## Abstract factory
|
||||
class BinaryFactory():
|
||||
type:list = []
|
||||
|
||||
def Create_Product():
|
||||
pass
|
||||
|
||||
class BinaryProduct():
|
||||
## Use GuidTool to decompress data.
|
||||
def DeCompressData(self, GuidTool, Section_Data: bytes, FileName) -> bytes:
|
||||
guidtool = GUIDTools().__getitem__(struct2stream(GuidTool))
|
||||
if not guidtool.ifexist:
|
||||
logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, FileName))
|
||||
raise Exception("Process Failed: GuidTool not found!")
|
||||
DecompressedData = guidtool.unpack(Section_Data)
|
||||
return DecompressedData
|
||||
|
||||
def ParserData():
|
||||
pass
|
||||
|
||||
class SectionFactory(BinaryFactory):
|
||||
type = [SECTION_TREE]
|
||||
|
||||
def Create_Product():
|
||||
return SectionProduct()
|
||||
|
||||
class FfsFactory(BinaryFactory):
|
||||
type = [ROOT_SECTION_TREE, FFS_TREE]
|
||||
|
||||
def Create_Product():
|
||||
return FfsProduct()
|
||||
|
||||
class FvFactory(BinaryFactory):
|
||||
type = [ROOT_FFS_TREE, FV_TREE, SEC_FV_TREE]
|
||||
|
||||
def Create_Product():
|
||||
return FvProduct()
|
||||
|
||||
class FdFactory(BinaryFactory):
|
||||
type = [ROOT_FV_TREE, ROOT_TREE]
|
||||
|
||||
def Create_Product():
|
||||
return FdProduct()
|
||||
|
||||
class SectionProduct(BinaryProduct):
|
||||
## Decompress the compressed section.
|
||||
def ParserData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
|
||||
if Section_Tree.Data.Type == 0x01:
|
||||
Section_Tree.Data.OriData = Section_Tree.Data.Data
|
||||
self.ParserSection(Section_Tree, b'')
|
||||
# Guided Define Section
|
||||
elif Section_Tree.Data.Type == 0x02:
|
||||
Section_Tree.Data.OriData = Section_Tree.Data.Data
|
||||
DeCompressGuidTool = Section_Tree.Data.ExtHeader.SectionDefinitionGuid
|
||||
Section_Tree.Data.Data = self.DeCompressData(DeCompressGuidTool, Section_Tree.Data.Data, Section_Tree.Parent.Data.Name)
|
||||
Section_Tree.Data.Size = len(Section_Tree.Data.Data) + Section_Tree.Data.HeaderLength
|
||||
self.ParserSection(Section_Tree, b'')
|
||||
elif Section_Tree.Data.Type == 0x03:
|
||||
Section_Tree.Data.OriData = Section_Tree.Data.Data
|
||||
self.ParserSection(Section_Tree, b'')
|
||||
# SEC_FV Section
|
||||
elif Section_Tree.Data.Type == 0x17:
|
||||
global Fv_count
|
||||
Sec_Fv_Info = FvNode(Fv_count, Section_Tree.Data.Data)
|
||||
Sec_Fv_Tree = BIOSTREE('FV'+ str(Fv_count))
|
||||
Sec_Fv_Tree.type = SEC_FV_TREE
|
||||
Sec_Fv_Tree.Data = Sec_Fv_Info
|
||||
Sec_Fv_Tree.Data.HOffset = Section_Tree.Data.DOffset
|
||||
Sec_Fv_Tree.Data.DOffset = Sec_Fv_Tree.Data.HOffset + Sec_Fv_Tree.Data.Header.HeaderLength
|
||||
Sec_Fv_Tree.Data.Data = Section_Tree.Data.Data[Sec_Fv_Tree.Data.Header.HeaderLength:]
|
||||
Section_Tree.insertChild(Sec_Fv_Tree)
|
||||
Fv_count += 1
|
||||
|
||||
def ParserSection(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
|
||||
Rel_Offset = 0
|
||||
Section_Offset = 0
|
||||
# Get the Data from parent tree, if do not have the tree then get it from the whole_data.
|
||||
if ParTree.Data != None:
|
||||
Data_Size = len(ParTree.Data.Data)
|
||||
Section_Offset = ParTree.Data.DOffset
|
||||
Whole_Data = ParTree.Data.Data
|
||||
else:
|
||||
Data_Size = len(Whole_Data)
|
||||
# Parser all the data to collect all the Section recorded in its Parent Section.
|
||||
while Rel_Offset < Data_Size:
|
||||
# Create a SectionNode and set it as the SectionTree's Data
|
||||
Section_Info = SectionNode(Whole_Data[Rel_Offset:])
|
||||
Section_Tree = BIOSTREE(Section_Info.Name)
|
||||
Section_Tree.type = SECTION_TREE
|
||||
Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.HeaderLength: Rel_Offset+Section_Info.Size]
|
||||
Section_Info.DOffset = Section_Offset + Section_Info.HeaderLength + Rel_Whole_Offset
|
||||
Section_Info.HOffset = Section_Offset + Rel_Whole_Offset
|
||||
Section_Info.ROffset = Rel_Offset
|
||||
if Section_Info.Header.Type == 0:
|
||||
break
|
||||
# The final Section in parent Section does not need to add padding, else must be 4-bytes align with parent Section start offset
|
||||
Pad_Size = 0
|
||||
if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Data) != Data_Size):
|
||||
Pad_Size = GetPadSize(Section_Info.Size, SECTION_COMMON_ALIGNMENT)
|
||||
Section_Info.PadData = Pad_Size * b'\x00'
|
||||
if Section_Info.Header.Type == 0x02:
|
||||
Section_Info.DOffset = Section_Offset + Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset
|
||||
Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: Rel_Offset+Section_Info.Size]
|
||||
if Section_Info.Header.Type == 0x14:
|
||||
ParTree.Data.Version = Section_Info.ExtHeader.GetVersionString()
|
||||
if Section_Info.Header.Type == 0x15:
|
||||
ParTree.Data.UiName = Section_Info.ExtHeader.GetUiString()
|
||||
if Section_Info.Header.Type == 0x19:
|
||||
if Section_Info.Data.replace(b'\x00', b'') == b'':
|
||||
Section_Info.IsPadSection = True
|
||||
Section_Offset += Section_Info.Size + Pad_Size
|
||||
Rel_Offset += Section_Info.Size + Pad_Size
|
||||
Section_Tree.Data = Section_Info
|
||||
ParTree.insertChild(Section_Tree)
|
||||
|
||||
class FfsProduct(BinaryProduct):
|
||||
# ParserFFs / GetSection
|
||||
def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
|
||||
Rel_Offset = 0
|
||||
Section_Offset = 0
|
||||
# Get the Data from parent tree, if do not have the tree then get it from the whole_data.
|
||||
if ParTree.Data != None:
|
||||
Data_Size = len(ParTree.Data.Data)
|
||||
Section_Offset = ParTree.Data.DOffset
|
||||
Whole_Data = ParTree.Data.Data
|
||||
else:
|
||||
Data_Size = len(Whole_Data)
|
||||
# Parser all the data to collect all the Section recorded in Ffs.
|
||||
while Rel_Offset < Data_Size:
|
||||
# Create a SectionNode and set it as the SectionTree's Data
|
||||
Section_Info = SectionNode(Whole_Data[Rel_Offset:])
|
||||
Section_Tree = BIOSTREE(Section_Info.Name)
|
||||
Section_Tree.type = SECTION_TREE
|
||||
Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.HeaderLength: Rel_Offset+Section_Info.Size]
|
||||
Section_Info.DOffset = Section_Offset + Section_Info.HeaderLength + Rel_Whole_Offset
|
||||
Section_Info.HOffset = Section_Offset + Rel_Whole_Offset
|
||||
Section_Info.ROffset = Rel_Offset
|
||||
if Section_Info.Header.Type == 0:
|
||||
break
|
||||
# The final Section in Ffs does not need to add padding, else must be 4-bytes align with Ffs start offset
|
||||
Pad_Size = 0
|
||||
if (Rel_Offset+Section_Info.HeaderLength+len(Section_Info.Data) != Data_Size):
|
||||
Pad_Size = GetPadSize(Section_Info.Size, SECTION_COMMON_ALIGNMENT)
|
||||
Section_Info.PadData = Pad_Size * b'\x00'
|
||||
if Section_Info.Header.Type == 0x02:
|
||||
Section_Info.DOffset = Section_Offset + Section_Info.ExtHeader.DataOffset + Rel_Whole_Offset
|
||||
Section_Info.Data = Whole_Data[Rel_Offset+Section_Info.ExtHeader.DataOffset: Rel_Offset+Section_Info.Size]
|
||||
# If Section is Version or UI type, it saves the version and UI info of its parent Ffs.
|
||||
if Section_Info.Header.Type == 0x14:
|
||||
ParTree.Data.Version = Section_Info.ExtHeader.GetVersionString()
|
||||
if Section_Info.Header.Type == 0x15:
|
||||
ParTree.Data.UiName = Section_Info.ExtHeader.GetUiString()
|
||||
if Section_Info.Header.Type == 0x19:
|
||||
if Section_Info.Data.replace(b'\x00', b'') == b'':
|
||||
Section_Info.IsPadSection = True
|
||||
Section_Offset += Section_Info.Size + Pad_Size
|
||||
Rel_Offset += Section_Info.Size + Pad_Size
|
||||
Section_Tree.Data = Section_Info
|
||||
ParTree.insertChild(Section_Tree)
|
||||
|
||||
class FvProduct(BinaryProduct):
|
||||
## ParserFv / GetFfs
|
||||
def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
|
||||
Ffs_Offset = 0
|
||||
Rel_Offset = 0
|
||||
# Get the Data from parent tree, if do not have the tree then get it from the whole_data.
|
||||
if ParTree.Data != None:
|
||||
Data_Size = len(ParTree.Data.Data)
|
||||
Ffs_Offset = ParTree.Data.DOffset
|
||||
Whole_Data = ParTree.Data.Data
|
||||
else:
|
||||
Data_Size = len(Whole_Data)
|
||||
# Parser all the data to collect all the Ffs recorded in Fv.
|
||||
while Rel_Offset < Data_Size:
|
||||
# Create a FfsNode and set it as the FFsTree's Data
|
||||
if Data_Size - Rel_Offset < 24:
|
||||
Ffs_Tree = BIOSTREE('Free_Space')
|
||||
Ffs_Tree.type = FFS_FREE_SPACE
|
||||
Ffs_Tree.Data = FreeSpaceNode(Whole_Data[Rel_Offset:])
|
||||
Ffs_Tree.Data.HOffset = Ffs_Offset + Rel_Whole_Offset
|
||||
Ffs_Tree.Data.DOffset = Ffs_Tree.Data.HOffset
|
||||
ParTree.Data.Free_Space = Data_Size - Rel_Offset
|
||||
ParTree.insertChild(Ffs_Tree)
|
||||
Rel_Offset = Data_Size
|
||||
else:
|
||||
Ffs_Info = FfsNode(Whole_Data[Rel_Offset:])
|
||||
Ffs_Tree = BIOSTREE(Ffs_Info.Name)
|
||||
Ffs_Info.HOffset = Ffs_Offset + Rel_Whole_Offset
|
||||
Ffs_Info.DOffset = Ffs_Offset + Ffs_Info.Header.HeaderLength + Rel_Whole_Offset
|
||||
Ffs_Info.ROffset = Rel_Offset
|
||||
if Ffs_Info.Name == PADVECTOR:
|
||||
Ffs_Tree.type = FFS_PAD
|
||||
Ffs_Info.Data = Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: Rel_Offset+Ffs_Info.Size]
|
||||
Ffs_Info.Size = len(Ffs_Info.Data) + Ffs_Info.Header.HeaderLength
|
||||
# if current Ffs is the final ffs of Fv and full of b'\xff', define it with Free_Space
|
||||
if struct2stream(Ffs_Info.Header).replace(b'\xff', b'') == b'':
|
||||
Ffs_Tree.type = FFS_FREE_SPACE
|
||||
Ffs_Info.Data = Whole_Data[Rel_Offset:]
|
||||
Ffs_Info.Size = len(Ffs_Info.Data)
|
||||
ParTree.Data.Free_Space = Ffs_Info.Size
|
||||
else:
|
||||
Ffs_Tree.type = FFS_TREE
|
||||
Ffs_Info.Data = Whole_Data[Rel_Offset+Ffs_Info.Header.HeaderLength: Rel_Offset+Ffs_Info.Size]
|
||||
# The final Ffs in Fv does not need to add padding, else must be 8-bytes align with Fv start offset
|
||||
Pad_Size = 0
|
||||
if Ffs_Tree.type != FFS_FREE_SPACE and (Rel_Offset+Ffs_Info.Header.HeaderLength+len(Ffs_Info.Data) != Data_Size):
|
||||
Pad_Size = GetPadSize(Ffs_Info.Size, FFS_COMMON_ALIGNMENT)
|
||||
Ffs_Info.PadData = Pad_Size * b'\xff'
|
||||
Ffs_Offset += Ffs_Info.Size + Pad_Size
|
||||
Rel_Offset += Ffs_Info.Size + Pad_Size
|
||||
Ffs_Tree.Data = Ffs_Info
|
||||
ParTree.insertChild(Ffs_Tree)
|
||||
|
||||
class FdProduct(BinaryProduct):
|
||||
type = [ROOT_FV_TREE, ROOT_TREE]
|
||||
|
||||
## Create DataTree with first level /fv Info, then parser each Fv.
|
||||
def ParserData(self, WholeFvTree, whole_data: bytes=b'', offset: int=0) -> None:
|
||||
# Get all Fv image in Fd with offset and length
|
||||
Fd_Struct = self.GetFvFromFd(whole_data)
|
||||
data_size = len(whole_data)
|
||||
Binary_count = 0
|
||||
global Fv_count
|
||||
# If the first Fv image is the Binary Fv, add it into the tree.
|
||||
if Fd_Struct[0][1] != 0:
|
||||
Binary_node = BIOSTREE('BINARY'+ str(Binary_count))
|
||||
Binary_node.type = BINARY_DATA
|
||||
Binary_node.Data = BinaryNode(str(Binary_count))
|
||||
Binary_node.Data.Data = whole_data[:Fd_Struct[0][1]]
|
||||
Binary_node.Data.Size = len(Binary_node.Data.Data)
|
||||
Binary_node.Data.HOffset = 0 + offset
|
||||
WholeFvTree.insertChild(Binary_node)
|
||||
Binary_count += 1
|
||||
# Add the first collected Fv image into the tree.
|
||||
Cur_node = BIOSTREE(Fd_Struct[0][0]+ str(Fv_count))
|
||||
Cur_node.type = Fd_Struct[0][0]
|
||||
Cur_node.Data = FvNode(Fv_count, whole_data[Fd_Struct[0][1]:Fd_Struct[0][1]+Fd_Struct[0][2][0]])
|
||||
Cur_node.Data.HOffset = Fd_Struct[0][1] + offset
|
||||
Cur_node.Data.DOffset = Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength
|
||||
Cur_node.Data.Data = whole_data[Fd_Struct[0][1]+Cur_node.Data.Header.HeaderLength:Fd_Struct[0][1]+Cur_node.Data.Size]
|
||||
WholeFvTree.insertChild(Cur_node)
|
||||
Fv_count += 1
|
||||
Fv_num = len(Fd_Struct)
|
||||
# Add all the collected Fv image and the Binary Fv image between them into the tree.
|
||||
for i in range(Fv_num-1):
|
||||
if Fd_Struct[i][1]+Fd_Struct[i][2][0] != Fd_Struct[i+1][1]:
|
||||
Binary_node = BIOSTREE('BINARY'+ str(Binary_count))
|
||||
Binary_node.type = BINARY_DATA
|
||||
Binary_node.Data = BinaryNode(str(Binary_count))
|
||||
Binary_node.Data.Data = whole_data[Fd_Struct[i][1]+Fd_Struct[i][2][0]:Fd_Struct[i+1][1]]
|
||||
Binary_node.Data.Size = len(Binary_node.Data.Data)
|
||||
Binary_node.Data.HOffset = Fd_Struct[i][1]+Fd_Struct[i][2][0] + offset
|
||||
WholeFvTree.insertChild(Binary_node)
|
||||
Binary_count += 1
|
||||
Cur_node = BIOSTREE(Fd_Struct[i+1][0]+ str(Fv_count))
|
||||
Cur_node.type = Fd_Struct[i+1][0]
|
||||
Cur_node.Data = FvNode(Fv_count, whole_data[Fd_Struct[i+1][1]:Fd_Struct[i+1][1]+Fd_Struct[i+1][2][0]])
|
||||
Cur_node.Data.HOffset = Fd_Struct[i+1][1] + offset
|
||||
Cur_node.Data.DOffset = Cur_node.Data.HOffset+Cur_node.Data.Header.HeaderLength
|
||||
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]
|
||||
WholeFvTree.insertChild(Cur_node)
|
||||
Fv_count += 1
|
||||
# If the final Fv image is the Binary Fv, add it into the tree
|
||||
if Fd_Struct[-1][1] + Fd_Struct[-1][2][0] != data_size:
|
||||
Binary_node = BIOSTREE('BINARY'+ str(Binary_count))
|
||||
Binary_node.type = BINARY_DATA
|
||||
Binary_node.Data = BinaryNode(str(Binary_count))
|
||||
Binary_node.Data.Data = whole_data[Fd_Struct[-1][1]+Fd_Struct[-1][2][0]:]
|
||||
Binary_node.Data.Size = len(Binary_node.Data.Data)
|
||||
Binary_node.Data.HOffset = Fd_Struct[-1][1]+Fd_Struct[-1][2][0] + offset
|
||||
WholeFvTree.insertChild(Binary_node)
|
||||
Binary_count += 1
|
||||
|
||||
## Get the first level Fv from Fd file.
|
||||
def GetFvFromFd(self, whole_data: bytes=b'') -> list:
|
||||
Fd_Struct = []
|
||||
data_size = len(whole_data)
|
||||
cur_index = 0
|
||||
# Get all the EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE FV image offset and length.
|
||||
while cur_index < data_size:
|
||||
if EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE in whole_data[cur_index:]:
|
||||
target_index = whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE) + cur_index
|
||||
if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:
|
||||
Fd_Struct.append([FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])
|
||||
cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]
|
||||
else:
|
||||
cur_index = target_index + 16
|
||||
else:
|
||||
cur_index = data_size
|
||||
cur_index = 0
|
||||
# Get all the EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE FV image offset and length.
|
||||
while cur_index < data_size:
|
||||
if EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE in whole_data[cur_index:]:
|
||||
target_index = whole_data[cur_index:].index(EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE) + cur_index
|
||||
if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:
|
||||
Fd_Struct.append([FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])
|
||||
cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]
|
||||
else:
|
||||
cur_index = target_index + 16
|
||||
else:
|
||||
cur_index = data_size
|
||||
cur_index = 0
|
||||
# Get all the EFI_SYSTEM_NVDATA_FV_GUID_BYTE FV image offset and length.
|
||||
while cur_index < data_size:
|
||||
if EFI_SYSTEM_NVDATA_FV_GUID_BYTE in whole_data[cur_index:]:
|
||||
target_index = whole_data[cur_index:].index(EFI_SYSTEM_NVDATA_FV_GUID_BYTE) + cur_index
|
||||
if whole_data[target_index+24:target_index+28] == FVH_SIGNATURE:
|
||||
Fd_Struct.append([DATA_FV_TREE, target_index - 16, unpack("Q", whole_data[target_index+16:target_index+24])])
|
||||
cur_index = Fd_Struct[-1][1] + Fd_Struct[-1][2][0]
|
||||
else:
|
||||
cur_index = target_index + 16
|
||||
else:
|
||||
cur_index = data_size
|
||||
# Sort all the collect Fv image with offset.
|
||||
Fd_Struct.sort(key=lambda x:x[1])
|
||||
tmp_struct = copy.deepcopy(Fd_Struct)
|
||||
tmp_index = 0
|
||||
Fv_num = len(Fd_Struct)
|
||||
# Remove the Fv image included in another Fv image.
|
||||
for i in range(1,Fv_num):
|
||||
if tmp_struct[i][1]+tmp_struct[i][2][0] < tmp_struct[i-1][1]+tmp_struct[i-1][2][0]:
|
||||
Fd_Struct.remove(Fd_Struct[i-tmp_index])
|
||||
tmp_index += 1
|
||||
return Fd_Struct
|
||||
|
||||
class ParserEntry():
|
||||
FactoryTable:dict = {
|
||||
SECTION_TREE: SectionFactory,
|
||||
ROOT_SECTION_TREE: FfsFactory,
|
||||
FFS_TREE: FfsFactory,
|
||||
ROOT_FFS_TREE: FvFactory,
|
||||
FV_TREE: FvFactory,
|
||||
SEC_FV_TREE: FvFactory,
|
||||
ROOT_FV_TREE: FdFactory,
|
||||
ROOT_TREE: FdFactory,
|
||||
}
|
||||
|
||||
def GetTargetFactory(self, Tree_type: str) -> BinaryFactory:
|
||||
if Tree_type in self.FactoryTable:
|
||||
return self.FactoryTable[Tree_type]
|
||||
|
||||
def Generate_Product(self, TargetFactory: BinaryFactory, Tree, Data: bytes, Offset: int) -> None:
|
||||
New_Product = TargetFactory.Create_Product()
|
||||
New_Product.ParserData(Tree, Data, Offset)
|
||||
|
||||
def DataParser(self, Tree, Data: bytes, Offset: int) -> None:
|
||||
TargetFactory = self.GetTargetFactory(Tree.type)
|
||||
if TargetFactory:
|
||||
self.Generate_Product(TargetFactory, Tree, Data, Offset)
|
198
BaseTools/Source/Python/FMMT/core/BiosTree.py
Normal file
198
BaseTools/Source/Python/FMMT/core/BiosTree.py
Normal file
@ -0,0 +1,198 @@
|
||||
## @file
|
||||
# This file is used to define the Bios layout tree structure and related operations.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import collections
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
ROOT_TREE = 'ROOT'
|
||||
ROOT_FV_TREE = 'ROOT_FV_TREE'
|
||||
ROOT_FFS_TREE = 'ROOT_FFS_TREE'
|
||||
ROOT_SECTION_TREE = 'ROOT_SECTION_TREE'
|
||||
|
||||
FV_TREE = 'FV'
|
||||
DATA_FV_TREE = 'DATA_FV'
|
||||
FFS_TREE = 'FFS'
|
||||
FFS_PAD = 'FFS_PAD'
|
||||
FFS_FREE_SPACE = 'FFS_FREE_SPACE'
|
||||
SECTION_TREE = 'SECTION'
|
||||
SEC_FV_TREE = 'SEC_FV_IMAGE'
|
||||
BINARY_DATA = 'BINARY'
|
||||
|
||||
RootType = [ROOT_TREE, ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE]
|
||||
FvType = [FV_TREE, SEC_FV_TREE]
|
||||
FfsType = FFS_TREE
|
||||
SecType = SECTION_TREE
|
||||
|
||||
class BIOSTREE:
|
||||
def __init__(self, NodeName: str) -> None:
|
||||
self.key = NodeName
|
||||
self.type = None
|
||||
self.Data = None
|
||||
self.Child = []
|
||||
self.Findlist = []
|
||||
self.Parent = None
|
||||
self.NextRel = None
|
||||
self.LastRel = None
|
||||
|
||||
def HasChild(self) -> bool:
|
||||
if self.Child == []:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def isFinalChild(self) -> bool:
|
||||
ParTree = self.Parent
|
||||
if ParTree:
|
||||
if ParTree.Child[-1] == self:
|
||||
return True
|
||||
return False
|
||||
|
||||
# FvTree.insertChild()
|
||||
def insertChild(self, newNode, pos: int=None) -> None:
|
||||
if len(self.Child) == 0:
|
||||
self.Child.append(newNode)
|
||||
else:
|
||||
if not pos:
|
||||
LastTree = self.Child[-1]
|
||||
self.Child.append(newNode)
|
||||
LastTree.NextRel = newNode
|
||||
newNode.LastRel = LastTree
|
||||
else:
|
||||
newNode.NextRel = self.Child[pos-1].NextRel
|
||||
newNode.LastRel = self.Child[pos].LastRel
|
||||
self.Child[pos-1].NextRel = newNode
|
||||
self.Child[pos].LastRel = newNode
|
||||
self.Child.insert(pos, newNode)
|
||||
newNode.Parent = self
|
||||
|
||||
# lastNode.insertRel(newNode)
|
||||
def insertRel(self, newNode) -> None:
|
||||
if self.Parent:
|
||||
parentTree = self.Parent
|
||||
new_index = parentTree.Child.index(self) + 1
|
||||
parentTree.Child.insert(new_index, newNode)
|
||||
self.NextRel = newNode
|
||||
newNode.LastRel = self
|
||||
|
||||
def deleteNode(self, deletekey: str) -> None:
|
||||
FindStatus, DeleteTree = self.FindNode(deletekey)
|
||||
if FindStatus:
|
||||
parentTree = DeleteTree.Parent
|
||||
lastTree = DeleteTree.LastRel
|
||||
nextTree = DeleteTree.NextRel
|
||||
if parentTree:
|
||||
index = parentTree.Child.index(DeleteTree)
|
||||
del parentTree.Child[index]
|
||||
if lastTree and nextTree:
|
||||
lastTree.NextRel = nextTree
|
||||
nextTree.LastRel = lastTree
|
||||
elif lastTree:
|
||||
lastTree.NextRel = None
|
||||
elif nextTree:
|
||||
nextTree.LastRel = None
|
||||
return DeleteTree
|
||||
else:
|
||||
logger.error('Could not find the target tree')
|
||||
return None
|
||||
|
||||
def FindNode(self, key: str, Findlist: list) -> None:
|
||||
if self.key == key or (self.Data and self.Data.Name == key) or (self.type == FFS_TREE and self.Data.UiName == key):
|
||||
Findlist.append(self)
|
||||
for item in self.Child:
|
||||
item.FindNode(key, Findlist)
|
||||
|
||||
def GetTreePath(self):
|
||||
BiosTreePath = [self]
|
||||
while self.Parent:
|
||||
BiosTreePath.insert(0, self.Parent)
|
||||
self = self.Parent
|
||||
return BiosTreePath
|
||||
|
||||
def parserTree(self, TargetDict: dict=None, Info: list=None, space: int=0, ParFvId="") -> None:
|
||||
Key = list(TargetDict.keys())[0]
|
||||
if TargetDict[Key]["Type"] in RootType:
|
||||
Info.append("Image File: {}".format(Key))
|
||||
Info.append("FilesNum: {}".format(TargetDict.get(Key).get('FilesNum')))
|
||||
Info.append("\n")
|
||||
elif TargetDict[Key]["Type"] in FvType:
|
||||
space += 2
|
||||
if TargetDict[Key]["Type"] == SEC_FV_TREE:
|
||||
Info.append("{}Child FV named {} of {}".format(space*" ", Key, ParFvId))
|
||||
space += 2
|
||||
else:
|
||||
Info.append("FvId: {}".format(Key))
|
||||
ParFvId = Key
|
||||
Info.append("{}FvNameGuid: {}".format(space*" ", TargetDict.get(Key).get('FvNameGuid')))
|
||||
Info.append("{}Attributes: {}".format(space*" ", TargetDict.get(Key).get('Attributes')))
|
||||
Info.append("{}Total Volume Size: {}".format(space*" ", TargetDict.get(Key).get('Size')))
|
||||
Info.append("{}Free Volume Size: {}".format(space*" ", TargetDict.get(Key).get('FreeSize')))
|
||||
Info.append("{}Volume Offset: {}".format(space*" ", TargetDict.get(Key).get('Offset')))
|
||||
Info.append("{}FilesNum: {}".format(space*" ", TargetDict.get(Key).get('FilesNum')))
|
||||
elif TargetDict[Key]["Type"] in FfsType:
|
||||
space += 2
|
||||
if TargetDict.get(Key).get('UiName') != "b''":
|
||||
Info.append("{}File: {} / {}".format(space*" ", Key, TargetDict.get(Key).get('UiName')))
|
||||
else:
|
||||
Info.append("{}File: {}".format(space*" ", Key))
|
||||
if "Files" in list(TargetDict[Key].keys()):
|
||||
for item in TargetDict[Key]["Files"]:
|
||||
self.parserTree(item, Info, space, ParFvId)
|
||||
|
||||
def ExportTree(self,TreeInfo: dict=None) -> dict:
|
||||
if TreeInfo is None:
|
||||
TreeInfo =collections.OrderedDict()
|
||||
|
||||
if self.type == ROOT_TREE or self.type == ROOT_FV_TREE or self.type == ROOT_FFS_TREE or self.type == ROOT_SECTION_TREE:
|
||||
key = str(self.key)
|
||||
TreeInfo[self.key] = collections.OrderedDict()
|
||||
TreeInfo[self.key]["Name"] = key
|
||||
TreeInfo[self.key]["Type"] = self.type
|
||||
TreeInfo[self.key]["FilesNum"] = len(self.Child)
|
||||
elif self.type == FV_TREE or self.type == SEC_FV_TREE:
|
||||
key = str(self.Data.FvId)
|
||||
TreeInfo[key] = collections.OrderedDict()
|
||||
TreeInfo[key]["Name"] = key
|
||||
if self.Data.FvId != self.Data.Name:
|
||||
TreeInfo[key]["FvNameGuid"] = str(self.Data.Name)
|
||||
TreeInfo[key]["Type"] = self.type
|
||||
TreeInfo[key]["Attributes"] = hex(self.Data.Header.Attributes)
|
||||
TreeInfo[key]["Size"] = hex(self.Data.Header.FvLength)
|
||||
TreeInfo[key]["FreeSize"] = hex(self.Data.Free_Space)
|
||||
TreeInfo[key]["Offset"] = hex(self.Data.HOffset)
|
||||
TreeInfo[key]["FilesNum"] = len(self.Child)
|
||||
elif self.type == FFS_TREE:
|
||||
key = str(self.Data.Name)
|
||||
TreeInfo[key] = collections.OrderedDict()
|
||||
TreeInfo[key]["Name"] = key
|
||||
TreeInfo[key]["UiName"] = '{}'.format(self.Data.UiName)
|
||||
TreeInfo[key]["Version"] = '{}'.format(self.Data.Version)
|
||||
TreeInfo[key]["Type"] = self.type
|
||||
TreeInfo[key]["Size"] = hex(self.Data.Size)
|
||||
TreeInfo[key]["Offset"] = hex(self.Data.HOffset)
|
||||
TreeInfo[key]["FilesNum"] = len(self.Child)
|
||||
elif self.type == SECTION_TREE and self.Data.Type == 0x02:
|
||||
key = str(self.Data.Name)
|
||||
TreeInfo[key] = collections.OrderedDict()
|
||||
TreeInfo[key]["Name"] = key
|
||||
TreeInfo[key]["Type"] = self.type
|
||||
TreeInfo[key]["Size"] = hex(len(self.Data.OriData) + self.Data.HeaderLength)
|
||||
TreeInfo[key]["DecompressedSize"] = hex(self.Data.Size)
|
||||
TreeInfo[key]["Offset"] = hex(self.Data.HOffset)
|
||||
TreeInfo[key]["FilesNum"] = len(self.Child)
|
||||
elif self is not None:
|
||||
key = str(self.Data.Name)
|
||||
TreeInfo[key] = collections.OrderedDict()
|
||||
TreeInfo[key]["Name"] = key
|
||||
TreeInfo[key]["Type"] = self.type
|
||||
TreeInfo[key]["Size"] = hex(self.Data.Size)
|
||||
TreeInfo[key]["Offset"] = hex(self.Data.HOffset)
|
||||
TreeInfo[key]["FilesNum"] = len(self.Child)
|
||||
|
||||
for item in self.Child:
|
||||
TreeInfo[key].setdefault('Files',[]).append( item.ExportTree())
|
||||
|
||||
return TreeInfo
|
194
BaseTools/Source/Python/FMMT/core/BiosTreeNode.py
Normal file
194
BaseTools/Source/Python/FMMT/core/BiosTreeNode.py
Normal file
@ -0,0 +1,194 @@
|
||||
## @file
|
||||
# This file is used to define the BIOS Tree Node.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from FirmwareStorageFormat.FvHeader import *
|
||||
from FirmwareStorageFormat.FfsFileHeader import *
|
||||
from FirmwareStorageFormat.SectionHeader import *
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
import uuid
|
||||
|
||||
SectionHeaderType = {
|
||||
0x01:'EFI_COMPRESSION_SECTION',
|
||||
0x02:'EFI_GUID_DEFINED_SECTION',
|
||||
0x03:'EFI_SECTION_DISPOSABLE',
|
||||
0x10:'EFI_SECTION_PE32',
|
||||
0x11:'EFI_SECTION_PIC',
|
||||
0x12:'EFI_SECTION_TE',
|
||||
0x13:'EFI_SECTION_DXE_DEPEX',
|
||||
0x14:'EFI_SECTION_VERSION',
|
||||
0x15:'EFI_SECTION_USER_INTERFACE',
|
||||
0x16:'EFI_SECTION_COMPATIBILITY16',
|
||||
0x17:'EFI_SECTION_FIRMWARE_VOLUME_IMAGE',
|
||||
0x18:'EFI_FREEFORM_SUBTYPE_GUID_SECTION',
|
||||
0x19:'EFI_SECTION_RAW',
|
||||
0x1B:'EFI_SECTION_PEI_DEPEX',
|
||||
0x1C:'EFI_SECTION_MM_DEPEX'
|
||||
}
|
||||
HeaderType = [0x01, 0x02, 0x14, 0x15, 0x18]
|
||||
|
||||
class BinaryNode:
|
||||
def __init__(self, name: str) -> None:
|
||||
self.Size = 0
|
||||
self.Name = "BINARY" + str(name)
|
||||
self.HOffset = 0
|
||||
self.Data = b''
|
||||
|
||||
class FvNode:
|
||||
def __init__(self, name, buffer: bytes) -> None:
|
||||
self.Header = EFI_FIRMWARE_VOLUME_HEADER.from_buffer_copy(buffer)
|
||||
Map_num = (self.Header.HeaderLength - 56)//8
|
||||
self.Header = Refine_FV_Header(Map_num).from_buffer_copy(buffer)
|
||||
self.FvId = "FV" + str(name)
|
||||
self.Name = "FV" + str(name)
|
||||
if self.Header.ExtHeaderOffset:
|
||||
self.ExtHeader = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer_copy(buffer[self.Header.ExtHeaderOffset:])
|
||||
self.Name = uuid.UUID(bytes_le=struct2stream(self.ExtHeader.FvName))
|
||||
self.ExtEntryOffset = self.Header.ExtHeaderOffset + 20
|
||||
if self.ExtHeader.ExtHeaderSize != 20:
|
||||
self.ExtEntryExist = 1
|
||||
self.ExtEntry = EFI_FIRMWARE_VOLUME_EXT_ENTRY.from_buffer_copy(buffer[self.ExtEntryOffset:])
|
||||
self.ExtTypeExist = 1
|
||||
if self.ExtEntry.ExtEntryType == 0x01:
|
||||
nums = (self.ExtEntry.ExtEntrySize - 8) // 16
|
||||
self.ExtEntry = Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums).from_buffer_copy(buffer[self.ExtEntryOffset:])
|
||||
elif self.ExtEntry.ExtEntryType == 0x02:
|
||||
nums = self.ExtEntry.ExtEntrySize - 20
|
||||
self.ExtEntry = Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums).from_buffer_copy(buffer[self.ExtEntryOffset:])
|
||||
elif self.ExtEntry.ExtEntryType == 0x03:
|
||||
self.ExtEntry = EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE.from_buffer_copy(buffer[self.ExtEntryOffset:])
|
||||
else:
|
||||
self.ExtTypeExist = 0
|
||||
else:
|
||||
self.ExtEntryExist = 0
|
||||
self.Size = self.Header.FvLength
|
||||
self.HeaderLength = self.Header.HeaderLength
|
||||
self.HOffset = 0
|
||||
self.DOffset = 0
|
||||
self.ROffset = 0
|
||||
self.Data = b''
|
||||
if self.Header.Signature != 1213613663:
|
||||
logger.error('Invalid Fv Header! Fv {} signature {} is not "_FVH".'.format(struct2stream(self.Header), self.Header.Signature))
|
||||
raise Exception("Process Failed: Fv Header Signature!")
|
||||
self.PadData = b''
|
||||
self.Free_Space = 0
|
||||
self.ModCheckSum()
|
||||
|
||||
def ModCheckSum(self) -> None:
|
||||
# Fv Header Sums to 0.
|
||||
Header = struct2stream(self.Header)[::-1]
|
||||
Size = self.HeaderLength // 2
|
||||
Sum = 0
|
||||
for i in range(Size):
|
||||
Sum += int(Header[i*2: i*2 + 2].hex(), 16)
|
||||
if Sum & 0xffff:
|
||||
self.Header.Checksum = 0x10000 - (Sum - self.Header.Checksum) % 0x10000
|
||||
|
||||
def ModFvExt(self) -> None:
|
||||
# If used space changes and self.ExtEntry.UsedSize exists, self.ExtEntry.UsedSize need to be changed.
|
||||
if self.Header.ExtHeaderOffset and self.ExtEntryExist and self.ExtTypeExist and self.ExtEntry.Hdr.ExtEntryType == 0x03:
|
||||
self.ExtEntry.UsedSize = self.Header.FvLength - self.Free_Space
|
||||
|
||||
def ModFvSize(self) -> None:
|
||||
# If Fv Size changed, self.Header.FvLength and self.Header.BlockMap[i].NumBlocks need to be changed.
|
||||
BlockMapNum = len(self.Header.BlockMap)
|
||||
for i in range(BlockMapNum):
|
||||
if self.Header.BlockMap[i].Length:
|
||||
self.Header.BlockMap[i].NumBlocks = self.Header.FvLength // self.Header.BlockMap[i].Length
|
||||
|
||||
def ModExtHeaderData(self) -> None:
|
||||
if self.Header.ExtHeaderOffset:
|
||||
ExtHeaderData = struct2stream(self.ExtHeader)
|
||||
ExtHeaderDataOffset = self.Header.ExtHeaderOffset - self.HeaderLength
|
||||
self.Data = self.Data[:ExtHeaderDataOffset] + ExtHeaderData + self.Data[ExtHeaderDataOffset+20:]
|
||||
if self.Header.ExtHeaderOffset and self.ExtEntryExist:
|
||||
ExtHeaderEntryData = struct2stream(self.ExtEntry)
|
||||
ExtHeaderEntryDataOffset = self.Header.ExtHeaderOffset + 20 - self.HeaderLength
|
||||
self.Data = self.Data[:ExtHeaderEntryDataOffset] + ExtHeaderEntryData + self.Data[ExtHeaderEntryDataOffset+len(ExtHeaderEntryData):]
|
||||
|
||||
class FfsNode:
|
||||
def __init__(self, buffer: bytes) -> None:
|
||||
self.Header = EFI_FFS_FILE_HEADER.from_buffer_copy(buffer)
|
||||
# self.Attributes = unpack("<B", buffer[21:22])[0]
|
||||
if self.Header.FFS_FILE_SIZE != 0 and self.Header.Attributes != 0xff and self.Header.Attributes & 0x01 == 1:
|
||||
logger.error('Error Ffs Header! Ffs {} Header Size and Attributes is not matched!'.format(uuid.UUID(bytes_le=struct2stream(self.Header.Name))))
|
||||
raise Exception("Process Failed: Error Ffs Header!")
|
||||
if self.Header.FFS_FILE_SIZE == 0 and self.Header.Attributes & 0x01 == 1:
|
||||
self.Header = EFI_FFS_FILE_HEADER2.from_buffer_copy(buffer)
|
||||
self.Name = uuid.UUID(bytes_le=struct2stream(self.Header.Name))
|
||||
self.UiName = b''
|
||||
self.Version = b''
|
||||
self.Size = self.Header.FFS_FILE_SIZE
|
||||
self.HeaderLength = self.Header.HeaderLength
|
||||
self.HOffset = 0
|
||||
self.DOffset = 0
|
||||
self.ROffset = 0
|
||||
self.Data = b''
|
||||
self.PadData = b''
|
||||
self.SectionMaxAlignment = SECTION_COMMON_ALIGNMENT # 4-align
|
||||
|
||||
def ModCheckSum(self) -> None:
|
||||
HeaderData = struct2stream(self.Header)
|
||||
HeaderSum = 0
|
||||
for item in HeaderData:
|
||||
HeaderSum += item
|
||||
HeaderSum -= self.Header.State
|
||||
HeaderSum -= self.Header.IntegrityCheck.Checksum.File
|
||||
if HeaderSum & 0xff:
|
||||
Header = self.Header.IntegrityCheck.Checksum.Header + 0x100 - HeaderSum % 0x100
|
||||
self.Header.IntegrityCheck.Checksum.Header = Header % 0x100
|
||||
|
||||
class SectionNode:
|
||||
def __init__(self, buffer: bytes) -> None:
|
||||
if buffer[0:3] != b'\xff\xff\xff':
|
||||
self.Header = EFI_COMMON_SECTION_HEADER.from_buffer_copy(buffer)
|
||||
else:
|
||||
self.Header = EFI_COMMON_SECTION_HEADER2.from_buffer_copy(buffer)
|
||||
if self.Header.Type in SectionHeaderType:
|
||||
self.Name = SectionHeaderType[self.Header.Type]
|
||||
elif self.Header.Type == 0:
|
||||
self.Name = "EFI_SECTION_ALL"
|
||||
else:
|
||||
self.Name = "SECTION"
|
||||
if self.Header.Type in HeaderType:
|
||||
self.ExtHeader = self.GetExtHeader(self.Header.Type, buffer[self.Header.Common_Header_Size():], (self.Header.SECTION_SIZE-self.Header.Common_Header_Size()))
|
||||
self.HeaderLength = self.Header.Common_Header_Size() + self.ExtHeader.ExtHeaderSize()
|
||||
else:
|
||||
self.ExtHeader = None
|
||||
self.HeaderLength = self.Header.Common_Header_Size()
|
||||
self.Size = self.Header.SECTION_SIZE
|
||||
self.Type = self.Header.Type
|
||||
self.HOffset = 0
|
||||
self.DOffset = 0
|
||||
self.ROffset = 0
|
||||
self.Data = b''
|
||||
self.OriData = b''
|
||||
self.OriHeader = b''
|
||||
self.PadData = b''
|
||||
self.IsPadSection = False
|
||||
self.SectionMaxAlignment = SECTION_COMMON_ALIGNMENT # 4-align
|
||||
|
||||
def GetExtHeader(self, Type: int, buffer: bytes, nums: int=0) -> None:
|
||||
if Type == 0x01:
|
||||
return EFI_COMPRESSION_SECTION.from_buffer_copy(buffer)
|
||||
elif Type == 0x02:
|
||||
return EFI_GUID_DEFINED_SECTION.from_buffer_copy(buffer)
|
||||
elif Type == 0x14:
|
||||
return Get_VERSION_Header((nums - 2)//2).from_buffer_copy(buffer)
|
||||
elif Type == 0x15:
|
||||
return Get_USER_INTERFACE_Header(nums//2).from_buffer_copy(buffer)
|
||||
elif Type == 0x18:
|
||||
return EFI_FREEFORM_SUBTYPE_GUID_SECTION.from_buffer_copy(buffer)
|
||||
|
||||
class FreeSpaceNode:
|
||||
def __init__(self, buffer: bytes) -> None:
|
||||
self.Name = 'Free_Space'
|
||||
self.Data = buffer
|
||||
self.Size = len(buffer)
|
||||
self.HOffset = 0
|
||||
self.DOffset = 0
|
||||
self.ROffset = 0
|
||||
self.PadData = b''
|
197
BaseTools/Source/Python/FMMT/core/FMMTOperation.py
Normal file
197
BaseTools/Source/Python/FMMT/core/FMMTOperation.py
Normal file
@ -0,0 +1,197 @@
|
||||
## @file
|
||||
# This file is used to define the functions to operate bios binary file.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from core.FMMTParser import *
|
||||
from core.FvHandler import *
|
||||
from utils.FvLayoutPrint import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
global Fv_count
|
||||
Fv_count = 0
|
||||
|
||||
# The ROOT_TYPE can be 'ROOT_TREE', 'ROOT_FV_TREE', 'ROOT_FFS_TREE', 'ROOT_SECTION_TREE'
|
||||
def ViewFile(inputfile: str, ROOT_TYPE: str, layoutfile: str=None, outputfile: str=None) -> None:
|
||||
if not os.path.exists(inputfile):
|
||||
logger.error("Invalid inputfile, can not open {}.".format(inputfile))
|
||||
raise Exception("Process Failed: Invalid inputfile!")
|
||||
# 1. Data Prepare
|
||||
with open(inputfile, "rb") as f:
|
||||
whole_data = f.read()
|
||||
FmmtParser = FMMTParser(inputfile, ROOT_TYPE)
|
||||
# 2. DataTree Create
|
||||
logger.debug('Parsing inputfile data......')
|
||||
FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)
|
||||
logger.debug('Done!')
|
||||
# 3. Log Output
|
||||
InfoDict = FmmtParser.WholeFvTree.ExportTree()
|
||||
logger.debug('BinaryTree created, start parsing BinaryTree data......')
|
||||
FmmtParser.WholeFvTree.parserTree(InfoDict, FmmtParser.BinaryInfo)
|
||||
logger.debug('Done!')
|
||||
GetFormatter("").LogPrint(FmmtParser.BinaryInfo)
|
||||
if layoutfile:
|
||||
if os.path.splitext(layoutfile)[1]:
|
||||
layoutfilename = layoutfile
|
||||
layoutfileformat = os.path.splitext(layoutfile)[1][1:].lower()
|
||||
else:
|
||||
layoutfilename = "Layout_{}{}".format(os.path.basename(inputfile),".{}".format(layoutfile.lower()))
|
||||
layoutfileformat = layoutfile.lower()
|
||||
GetFormatter(layoutfileformat).dump(InfoDict, FmmtParser.BinaryInfo, layoutfilename)
|
||||
# 4. Data Encapsulation
|
||||
if outputfile:
|
||||
logger.debug('Start encapsulating data......')
|
||||
FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)
|
||||
with open(outputfile, "wb") as f:
|
||||
f.write(FmmtParser.FinalData)
|
||||
logger.debug('Encapsulated data is saved in {}.'.format(outputfile))
|
||||
|
||||
def DeleteFfs(inputfile: str, TargetFfs_name: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
if not os.path.exists(inputfile):
|
||||
logger.error("Invalid inputfile, can not open {}.".format(inputfile))
|
||||
raise Exception("Process Failed: Invalid inputfile!")
|
||||
# 1. Data Prepare
|
||||
with open(inputfile, "rb") as f:
|
||||
whole_data = f.read()
|
||||
FmmtParser = FMMTParser(inputfile, ROOT_TREE)
|
||||
# 2. DataTree Create
|
||||
logger.debug('Parsing inputfile data......')
|
||||
FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)
|
||||
logger.debug('Done!')
|
||||
# 3. Data Modify
|
||||
FmmtParser.WholeFvTree.FindNode(TargetFfs_name, FmmtParser.WholeFvTree.Findlist)
|
||||
# Choose the Specfic DeleteFfs with Fv info
|
||||
if Fv_name:
|
||||
for item in FmmtParser.WholeFvTree.Findlist:
|
||||
if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:
|
||||
FmmtParser.WholeFvTree.Findlist.remove(item)
|
||||
Status = False
|
||||
if FmmtParser.WholeFvTree.Findlist != []:
|
||||
for Delete_Ffs in FmmtParser.WholeFvTree.Findlist:
|
||||
FfsMod = FvHandler(None, Delete_Ffs)
|
||||
Status = FfsMod.DeleteFfs()
|
||||
else:
|
||||
logger.error('Target Ffs not found!!!')
|
||||
# 4. Data Encapsulation
|
||||
if Status:
|
||||
logger.debug('Start encapsulating data......')
|
||||
FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)
|
||||
with open(outputfile, "wb") as f:
|
||||
f.write(FmmtParser.FinalData)
|
||||
logger.debug('Encapsulated data is saved in {}.'.format(outputfile))
|
||||
|
||||
def AddNewFfs(inputfile: str, Fv_name: str, newffsfile: str, outputfile: str) -> None:
|
||||
if not os.path.exists(inputfile):
|
||||
logger.error("Invalid inputfile, can not open {}.".format(inputfile))
|
||||
raise Exception("Process Failed: Invalid inputfile!")
|
||||
if not os.path.exists(newffsfile):
|
||||
logger.error("Invalid ffsfile, can not open {}.".format(newffsfile))
|
||||
raise Exception("Process Failed: Invalid ffs file!")
|
||||
# 1. Data Prepare
|
||||
with open(inputfile, "rb") as f:
|
||||
whole_data = f.read()
|
||||
FmmtParser = FMMTParser(inputfile, ROOT_TREE)
|
||||
# 2. DataTree Create
|
||||
logger.debug('Parsing inputfile data......')
|
||||
FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)
|
||||
logger.debug('Done!')
|
||||
# Get Target Fv and Target Ffs_Pad
|
||||
FmmtParser.WholeFvTree.FindNode(Fv_name, FmmtParser.WholeFvTree.Findlist)
|
||||
# Create new ffs Tree
|
||||
with open(newffsfile, "rb") as f:
|
||||
new_ffs_data = f.read()
|
||||
NewFmmtParser = FMMTParser(newffsfile, ROOT_FFS_TREE)
|
||||
Status = False
|
||||
# 3. Data Modify
|
||||
if FmmtParser.WholeFvTree.Findlist:
|
||||
for TargetFv in FmmtParser.WholeFvTree.Findlist:
|
||||
TargetFfsPad = TargetFv.Child[-1]
|
||||
logger.debug('Parsing newffsfile data......')
|
||||
if TargetFfsPad.type == FFS_FREE_SPACE:
|
||||
NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, new_ffs_data, TargetFfsPad.Data.HOffset)
|
||||
else:
|
||||
NewFmmtParser.ParserFromRoot(NewFmmtParser.WholeFvTree, new_ffs_data, TargetFfsPad.Data.HOffset+TargetFfsPad.Data.Size)
|
||||
logger.debug('Done!')
|
||||
FfsMod = FvHandler(NewFmmtParser.WholeFvTree.Child[0], TargetFfsPad)
|
||||
Status = FfsMod.AddFfs()
|
||||
else:
|
||||
logger.error('Target Fv not found!!!')
|
||||
# 4. Data Encapsulation
|
||||
if Status:
|
||||
logger.debug('Start encapsulating data......')
|
||||
FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)
|
||||
with open(outputfile, "wb") as f:
|
||||
f.write(FmmtParser.FinalData)
|
||||
logger.debug('Encapsulated data is saved in {}.'.format(outputfile))
|
||||
|
||||
def ReplaceFfs(inputfile: str, Ffs_name: str, newffsfile: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
if not os.path.exists(inputfile):
|
||||
logger.error("Invalid inputfile, can not open {}.".format(inputfile))
|
||||
raise Exception("Process Failed: Invalid inputfile!")
|
||||
# 1. Data Prepare
|
||||
with open(inputfile, "rb") as f:
|
||||
whole_data = f.read()
|
||||
FmmtParser = FMMTParser(inputfile, ROOT_TREE)
|
||||
# 2. DataTree Create
|
||||
logger.debug('Parsing inputfile data......')
|
||||
FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)
|
||||
logger.debug('Done!')
|
||||
with open(newffsfile, "rb") as f:
|
||||
new_ffs_data = f.read()
|
||||
newFmmtParser = FMMTParser(newffsfile, FV_TREE)
|
||||
logger.debug('Parsing newffsfile data......')
|
||||
newFmmtParser.ParserFromRoot(newFmmtParser.WholeFvTree, new_ffs_data)
|
||||
logger.debug('Done!')
|
||||
Status = False
|
||||
# 3. Data Modify
|
||||
new_ffs = newFmmtParser.WholeFvTree.Child[0]
|
||||
new_ffs.Data.PadData = GetPadSize(new_ffs.Data.Size, FFS_COMMON_ALIGNMENT) * b'\xff'
|
||||
FmmtParser.WholeFvTree.FindNode(Ffs_name, FmmtParser.WholeFvTree.Findlist)
|
||||
if Fv_name:
|
||||
for item in FmmtParser.WholeFvTree.Findlist:
|
||||
if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:
|
||||
FmmtParser.WholeFvTree.Findlist.remove(item)
|
||||
if FmmtParser.WholeFvTree.Findlist != []:
|
||||
for TargetFfs in FmmtParser.WholeFvTree.Findlist:
|
||||
FfsMod = FvHandler(newFmmtParser.WholeFvTree.Child[0], TargetFfs)
|
||||
Status = FfsMod.ReplaceFfs()
|
||||
else:
|
||||
logger.error('Target Ffs not found!!!')
|
||||
# 4. Data Encapsulation
|
||||
if Status:
|
||||
logger.debug('Start encapsulating data......')
|
||||
FmmtParser.Encapsulation(FmmtParser.WholeFvTree, False)
|
||||
with open(outputfile, "wb") as f:
|
||||
f.write(FmmtParser.FinalData)
|
||||
logger.debug('Encapsulated data is saved in {}.'.format(outputfile))
|
||||
|
||||
def ExtractFfs(inputfile: str, Ffs_name: str, outputfile: str, Fv_name: str=None) -> None:
|
||||
if not os.path.exists(inputfile):
|
||||
logger.error("Invalid inputfile, can not open {}.".format(inputfile))
|
||||
raise Exception("Process Failed: Invalid inputfile!")
|
||||
# 1. Data Prepare
|
||||
with open(inputfile, "rb") as f:
|
||||
whole_data = f.read()
|
||||
FmmtParser = FMMTParser(inputfile, ROOT_TREE)
|
||||
# 2. DataTree Create
|
||||
logger.debug('Parsing inputfile data......')
|
||||
FmmtParser.ParserFromRoot(FmmtParser.WholeFvTree, whole_data)
|
||||
logger.debug('Done!')
|
||||
FmmtParser.WholeFvTree.FindNode(Ffs_name, FmmtParser.WholeFvTree.Findlist)
|
||||
if Fv_name:
|
||||
for item in FmmtParser.WholeFvTree.Findlist:
|
||||
if item.Parent.key != Fv_name and item.Parent.Data.Name != Fv_name:
|
||||
FmmtParser.WholeFvTree.Findlist.remove(item)
|
||||
if FmmtParser.WholeFvTree.Findlist != []:
|
||||
TargetNode = FmmtParser.WholeFvTree.Findlist[0]
|
||||
TargetFv = TargetNode.Parent
|
||||
if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
|
||||
TargetNode.Data.Header.State = c_uint8(
|
||||
~TargetNode.Data.Header.State)
|
||||
FinalData = struct2stream(TargetNode.Data.Header) + TargetNode.Data.Data
|
||||
with open(outputfile, "wb") as f:
|
||||
f.write(FinalData)
|
||||
logger.debug('Extract ffs data is saved in {}.'.format(outputfile))
|
||||
else:
|
||||
logger.error('Target Ffs not found!!!')
|
87
BaseTools/Source/Python/FMMT/core/FMMTParser.py
Normal file
87
BaseTools/Source/Python/FMMT/core/FMMTParser.py
Normal file
@ -0,0 +1,87 @@
|
||||
## @file
|
||||
# This file is used to define the interface of Bios Parser.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from core.BinaryFactoryProduct import ParserEntry
|
||||
from core.BiosTreeNode import *
|
||||
from core.BiosTree import *
|
||||
from core.GuidTools import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
class FMMTParser:
|
||||
def __init__(self, name: str, TYPE: str) -> None:
|
||||
self.WholeFvTree = BIOSTREE(name)
|
||||
self.WholeFvTree.type = TYPE
|
||||
self.FinalData = b''
|
||||
self.BinaryInfo = []
|
||||
|
||||
## Parser the nodes in WholeTree.
|
||||
def ParserFromRoot(self, WholeFvTree=None, whole_data: bytes=b'', Reloffset: int=0) -> None:
|
||||
if WholeFvTree.type == ROOT_TREE or WholeFvTree.type == ROOT_FV_TREE:
|
||||
ParserEntry().DataParser(self.WholeFvTree, whole_data, Reloffset)
|
||||
else:
|
||||
ParserEntry().DataParser(WholeFvTree, whole_data, Reloffset)
|
||||
for Child in WholeFvTree.Child:
|
||||
self.ParserFromRoot(Child, "")
|
||||
|
||||
## Encapuslation all the data in tree into self.FinalData
|
||||
def Encapsulation(self, rootTree, CompressStatus: bool) -> None:
|
||||
# If current node is Root node, skip it.
|
||||
if rootTree.type == ROOT_TREE or rootTree.type == ROOT_FV_TREE or rootTree.type == ROOT_FFS_TREE or rootTree.type == ROOT_SECTION_TREE:
|
||||
logger.debug('Encapsulated successfully!')
|
||||
# If current node do not have Header, just add Data.
|
||||
elif rootTree.type == BINARY_DATA or rootTree.type == FFS_FREE_SPACE:
|
||||
self.FinalData += rootTree.Data.Data
|
||||
rootTree.Child = []
|
||||
# If current node do not have Child and ExtHeader, just add its Header and Data.
|
||||
elif rootTree.type == DATA_FV_TREE or rootTree.type == FFS_PAD:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + rootTree.Data.Data + rootTree.Data.PadData
|
||||
if rootTree.isFinalChild():
|
||||
ParTree = rootTree.Parent
|
||||
if ParTree.type != 'ROOT':
|
||||
self.FinalData += ParTree.Data.PadData
|
||||
rootTree.Child = []
|
||||
# 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.
|
||||
elif rootTree.type == FV_TREE or rootTree.type == FFS_TREE or rootTree.type == SEC_FV_TREE:
|
||||
if rootTree.HasChild():
|
||||
self.FinalData += struct2stream(rootTree.Data.Header)
|
||||
else:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + rootTree.Data.Data + rootTree.Data.PadData
|
||||
if rootTree.isFinalChild():
|
||||
ParTree = rootTree.Parent
|
||||
if ParTree.type != 'ROOT':
|
||||
self.FinalData += ParTree.Data.PadData
|
||||
# If current node is Section, need to consider its ExtHeader, Child and Compressed Status.
|
||||
elif rootTree.type == SECTION_TREE:
|
||||
# Not compressed section
|
||||
if rootTree.Data.OriData == b'' or (rootTree.Data.OriData != b'' and CompressStatus):
|
||||
if rootTree.HasChild():
|
||||
if rootTree.Data.ExtHeader:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader)
|
||||
else:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header)
|
||||
else:
|
||||
Data = rootTree.Data.Data
|
||||
if rootTree.Data.ExtHeader:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData
|
||||
else:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + Data + rootTree.Data.PadData
|
||||
if rootTree.isFinalChild():
|
||||
ParTree = rootTree.Parent
|
||||
self.FinalData += ParTree.Data.PadData
|
||||
# If compressed section
|
||||
else:
|
||||
Data = rootTree.Data.OriData
|
||||
rootTree.Child = []
|
||||
if rootTree.Data.ExtHeader:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + struct2stream(rootTree.Data.ExtHeader) + Data + rootTree.Data.PadData
|
||||
else:
|
||||
self.FinalData += struct2stream(rootTree.Data.Header) + Data + rootTree.Data.PadData
|
||||
if rootTree.isFinalChild():
|
||||
ParTree = rootTree.Parent
|
||||
self.FinalData += ParTree.Data.PadData
|
||||
for Child in rootTree.Child:
|
||||
self.Encapsulation(Child, CompressStatus)
|
641
BaseTools/Source/Python/FMMT/core/FvHandler.py
Normal file
641
BaseTools/Source/Python/FMMT/core/FvHandler.py
Normal file
@ -0,0 +1,641 @@
|
||||
## @file
|
||||
# This file is used to the implementation of Bios layout handler.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import os
|
||||
from core.BiosTree import *
|
||||
from core.GuidTools import GUIDTools
|
||||
from core.BiosTreeNode import *
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
EFI_FVB2_ERASE_POLARITY = 0x00000800
|
||||
|
||||
def ChangeSize(TargetTree, size_delta: int=0) -> None:
|
||||
# If Size increase delta, then should be: size_delta = -delta
|
||||
if type(TargetTree.Data.Header) == type(EFI_FFS_FILE_HEADER2()) or type(TargetTree.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()):
|
||||
TargetTree.Data.Size -= size_delta
|
||||
TargetTree.Data.Header.ExtendedSize -= size_delta
|
||||
elif TargetTree.type == SECTION_TREE and TargetTree.Data.OriData:
|
||||
OriSize = TargetTree.Data.Header.SECTION_SIZE
|
||||
OriSize -= size_delta
|
||||
TargetTree.Data.Header.Size[0] = OriSize % (16**2)
|
||||
TargetTree.Data.Header.Size[1] = OriSize % (16**4) //(16**2)
|
||||
TargetTree.Data.Header.Size[2] = OriSize // (16**4)
|
||||
else:
|
||||
TargetTree.Data.Size -= size_delta
|
||||
TargetTree.Data.Header.Size[0] = TargetTree.Data.Size % (16**2)
|
||||
TargetTree.Data.Header.Size[1] = TargetTree.Data.Size % (16**4) //(16**2)
|
||||
TargetTree.Data.Header.Size[2] = TargetTree.Data.Size // (16**4)
|
||||
|
||||
def ModifyFfsType(TargetFfs) -> None:
|
||||
if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER()) and TargetFfs.Data.Size > 0xFFFFFF:
|
||||
ExtendSize = TargetFfs.Data.Header.FFS_FILE_SIZE + 8
|
||||
New_Header = EFI_FFS_FILE_HEADER2()
|
||||
New_Header.Name = TargetFfs.Data.Header.Name
|
||||
New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck
|
||||
New_Header.Type = TargetFfs.Data.Header.Type
|
||||
New_Header.Attributes = TargetFfs.Data.Header.Attributes | 0x01 # set the Attribute with FFS_ATTRIB_LARGE_FILE (0x01)
|
||||
NewSize = 0
|
||||
New_Header.Size[0] = NewSize % (16**2) # minus the delta size of Header
|
||||
New_Header.Size[1] = NewSize % (16**4) //(16**2)
|
||||
New_Header.Size[2] = NewSize // (16**4)
|
||||
New_Header.State = TargetFfs.Data.Header.State
|
||||
New_Header.ExtendedSize = ExtendSize
|
||||
TargetFfs.Data.Header = New_Header
|
||||
TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE
|
||||
TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength
|
||||
TargetFfs.Data.ModCheckSum()
|
||||
elif type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()) and TargetFfs.Data.Size <= 0xFFFFFF:
|
||||
New_Header = EFI_FFS_FILE_HEADER()
|
||||
New_Header.Name = TargetFfs.Data.Header.Name
|
||||
New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck
|
||||
New_Header.Type = TargetFfs.Data.Header.Type
|
||||
New_Header.Attributes = TargetFfs.Data.Header.Attributes - 1 # remove the FFS_ATTRIB_LARGE_FILE (0x01) from Attribute
|
||||
New_Header.Size[0] = (TargetFfs.Data.Size - 8) % (16**2) # minus the delta size of Header
|
||||
New_Header.Size[1] = (TargetFfs.Data.Size - 8) % (16**4) //(16**2)
|
||||
New_Header.Size[2] = (TargetFfs.Data.Size - 8) // (16**4)
|
||||
New_Header.State = TargetFfs.Data.Header.State
|
||||
TargetFfs.Data.Header = New_Header
|
||||
TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE
|
||||
TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength
|
||||
TargetFfs.Data.ModCheckSum()
|
||||
if struct2stream(TargetFfs.Parent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE:
|
||||
NeedChange = True
|
||||
for item in TargetFfs.Parent.Child:
|
||||
if type(item.Data.Header) == type(EFI_FFS_FILE_HEADER2()):
|
||||
NeedChange = False
|
||||
if NeedChange:
|
||||
TargetFfs.Parent.Data.Header.FileSystemGuid = ModifyGuidFormat("8c8ce578-8a3d-4f1c-9935-896185c32dd3")
|
||||
|
||||
if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()):
|
||||
TarParent = TargetFfs.Parent
|
||||
while TarParent:
|
||||
if TarParent.type == FV_TREE and struct2stream(TarParent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:
|
||||
TarParent.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")
|
||||
TarParent = TarParent.Parent
|
||||
|
||||
def PadSectionModify(PadSection, Offset) -> None:
|
||||
# Offset > 0, Size decrease; Offset < 0, Size increase;
|
||||
ChangeSize(PadSection, Offset)
|
||||
PadSection.Data.Data = (PadSection.Data.Size - PadSection.Data.HeaderLength) * b'\xff'
|
||||
|
||||
def ModifySectionType(TargetSection) -> None:
|
||||
# If Section Size is increased larger than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER to EFI_COMMON_SECTION_HEADER2.
|
||||
if type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER()) and TargetSection.Data.Size >= 0xFFFFFF:
|
||||
New_Header = EFI_COMMON_SECTION_HEADER2()
|
||||
New_Header.Type = TargetSection.Data.Header.Type
|
||||
NewSize = 0xFFFFFF
|
||||
New_Header.Size[0] = NewSize % (16**2) # minus the delta size of Header
|
||||
New_Header.Size[1] = NewSize % (16**4) //(16**2)
|
||||
New_Header.Size[2] = NewSize // (16**4)
|
||||
New_Header.ExtendedSize = TargetSection.Data.Size + 4
|
||||
TargetSection.Data.Header = New_Header
|
||||
TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE
|
||||
# Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.
|
||||
if TargetSection.LastRel.Data.IsPadSection:
|
||||
PadSectionModify(TargetSection.LastRel, -4)
|
||||
else:
|
||||
SecParent = TargetSection.Parent
|
||||
Target_index = SecParent.Child.index(TargetSection)
|
||||
NewPadSection = SectionNode(b'\x00\x00\x00\x19')
|
||||
SecParent.insertChild(NewPadSection, Target_index)
|
||||
# If Section Size is decreased smaller than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER2 to EFI_COMMON_SECTION_HEADER.
|
||||
elif type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()) and TargetSection.Data.Size < 0xFFFFFF:
|
||||
New_Header = EFI_COMMON_SECTION_HEADER()
|
||||
New_Header.Type = TargetSection.Data.Header.Type
|
||||
New_Header.Size[0] = (TargetSection.Data.Size - 4) % (16**2) # minus the delta size of Header
|
||||
New_Header.Size[1] = (TargetSection.Data.Size - 4) % (16**4) //(16**2)
|
||||
New_Header.Size[2] = (TargetSection.Data.Size - 4) // (16**4)
|
||||
TargetSection.Data.Header = New_Header
|
||||
TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE
|
||||
# Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.
|
||||
if TargetSection.LastRel.Data.IsPadSection:
|
||||
PadSectionModify(TargetSection.LastRel, -4)
|
||||
else:
|
||||
SecParent = TargetSection.Parent
|
||||
Target_index = SecParent.Child.index(TargetSection)
|
||||
NewPadSection = SectionNode(b'\x00\x00\x00\x19')
|
||||
SecParent.insertChild(NewPadSection, Target_index)
|
||||
|
||||
def ModifyFvExtData(TreeNode) -> None:
|
||||
FvExtData = b''
|
||||
if TreeNode.Data.Header.ExtHeaderOffset:
|
||||
FvExtHeader = struct2stream(TreeNode.Data.ExtHeader)
|
||||
FvExtData += FvExtHeader
|
||||
if TreeNode.Data.Header.ExtHeaderOffset and TreeNode.Data.ExtEntryExist:
|
||||
FvExtEntry = struct2stream(TreeNode.Data.ExtEntry)
|
||||
FvExtData += FvExtEntry
|
||||
if FvExtData:
|
||||
InfoNode = TreeNode.Child[0]
|
||||
InfoNode.Data.Data = FvExtData + InfoNode.Data.Data[TreeNode.Data.ExtHeader.ExtHeaderSize:]
|
||||
InfoNode.Data.ModCheckSum()
|
||||
|
||||
def ModifyFvSystemGuid(TargetFv) -> None:
|
||||
if struct2stream(TargetFv.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:
|
||||
TargetFv.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")
|
||||
TargetFv.Data.ModCheckSum()
|
||||
TargetFv.Data.Data = b''
|
||||
for item in TargetFv.Child:
|
||||
if item.type == FFS_FREE_SPACE:
|
||||
TargetFv.Data.Data += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
|
||||
class FvHandler:
|
||||
def __init__(self, NewFfs, TargetFfs) -> None:
|
||||
self.NewFfs = NewFfs
|
||||
self.TargetFfs = TargetFfs
|
||||
self.Status = False
|
||||
self.Remain_New_Free_Space = 0
|
||||
|
||||
## Use for Compress the Section Data
|
||||
def CompressData(self, TargetTree) -> None:
|
||||
TreePath = TargetTree.GetTreePath()
|
||||
pos = len(TreePath)
|
||||
self.Status = False
|
||||
while pos:
|
||||
if not self.Status:
|
||||
if TreePath[pos-1].type == SECTION_TREE and TreePath[pos-1].Data.Type == 0x02:
|
||||
self.CompressSectionData(TreePath[pos-1], None, TreePath[pos-1].Data.ExtHeader.SectionDefinitionGuid)
|
||||
else:
|
||||
if pos == len(TreePath):
|
||||
self.CompressSectionData(TreePath[pos-1], pos)
|
||||
else:
|
||||
self.CompressSectionData(TreePath[pos-1], None)
|
||||
pos -= 1
|
||||
|
||||
def CompressSectionData(self, TargetTree, pos: int, GuidTool=None) -> None:
|
||||
NewData = b''
|
||||
temp_save_child = TargetTree.Child
|
||||
if TargetTree.Data:
|
||||
# Update current node data as adding all the header and data of its child node.
|
||||
for item in temp_save_child:
|
||||
if item.type == SECTION_TREE and not item.Data.OriData and item.Data.ExtHeader:
|
||||
NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
|
||||
elif item.type == SECTION_TREE and item.Data.OriData and not item.Data.ExtHeader:
|
||||
NewData += struct2stream(item.Data.Header) + item.Data.OriData + item.Data.PadData
|
||||
elif item.type == SECTION_TREE and item.Data.OriData and item.Data.ExtHeader:
|
||||
NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
|
||||
elif item.type == FFS_FREE_SPACE:
|
||||
NewData += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
NewData += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData
|
||||
# If node is FFS_TREE, update Pad data and Header info.
|
||||
# Remain_New_Free_Space is used for move more free space into lst level Fv.
|
||||
if TargetTree.type == FFS_TREE:
|
||||
New_Pad_Size = GetPadSize(len(NewData), 8)
|
||||
Size_delta = len(NewData) - len(TargetTree.Data.Data)
|
||||
ChangeSize(TargetTree, -Size_delta)
|
||||
Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size
|
||||
self.Remain_New_Free_Space += Delta_Pad_Size
|
||||
TargetTree.Data.PadData = b'\xff' * New_Pad_Size
|
||||
TargetTree.Data.ModCheckSum()
|
||||
# If node is FV_TREE, update Pad data and Header info.
|
||||
# Consume Remain_New_Free_Space is used for move more free space into lst level Fv.
|
||||
elif TargetTree.type == FV_TREE or TargetTree.type == SEC_FV_TREE and not pos:
|
||||
if self.Remain_New_Free_Space:
|
||||
if TargetTree.Data.Free_Space:
|
||||
TargetTree.Data.Free_Space += self.Remain_New_Free_Space
|
||||
NewData += self.Remain_New_Free_Space * b'\xff'
|
||||
TargetTree.Child[-1].Data.Data += self.Remain_New_Free_Space * b'\xff'
|
||||
else:
|
||||
TargetTree.Data.Data += self.Remain_New_Free_Space * b'\xff'
|
||||
New_Free_Space = BIOSTREE('FREE_SPACE')
|
||||
New_Free_Space.type = FFS_FREE_SPACE
|
||||
New_Free_Space.Data = FreeSpaceNode(b'\xff' * self.Remain_New_Free_Space)
|
||||
TargetTree.insertChild(New_Free_Space)
|
||||
self.Remain_New_Free_Space = 0
|
||||
if TargetTree.type == SEC_FV_TREE:
|
||||
Size_delta = len(NewData) + self.Remain_New_Free_Space - len(TargetTree.Data.Data)
|
||||
TargetTree.Data.Header.FvLength += Size_delta
|
||||
TargetTree.Data.ModFvExt()
|
||||
TargetTree.Data.ModFvSize()
|
||||
TargetTree.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetTree)
|
||||
TargetTree.Data.ModCheckSum()
|
||||
# If node is SECTION_TREE and not guided section, update Pad data and Header info.
|
||||
# Remain_New_Free_Space is used for move more free space into lst level Fv.
|
||||
elif TargetTree.type == SECTION_TREE and TargetTree.Data.Type != 0x02:
|
||||
New_Pad_Size = GetPadSize(len(NewData), 4)
|
||||
Size_delta = len(NewData) - len(TargetTree.Data.Data)
|
||||
ChangeSize(TargetTree, -Size_delta)
|
||||
if TargetTree.NextRel:
|
||||
Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size
|
||||
self.Remain_New_Free_Space += Delta_Pad_Size
|
||||
TargetTree.Data.PadData = b'\x00' * New_Pad_Size
|
||||
TargetTree.Data.Data = NewData
|
||||
if GuidTool:
|
||||
guidtool = GUIDTools().__getitem__(struct2stream(GuidTool))
|
||||
if not guidtool.ifexist:
|
||||
logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, TargetTree.Parent.Data.Name))
|
||||
raise Exception("Process Failed: GuidTool not found!")
|
||||
CompressedData = guidtool.pack(TargetTree.Data.Data)
|
||||
if len(CompressedData) < len(TargetTree.Data.OriData):
|
||||
New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)
|
||||
Size_delta = len(CompressedData) - len(TargetTree.Data.OriData)
|
||||
ChangeSize(TargetTree, -Size_delta)
|
||||
if TargetTree.NextRel:
|
||||
TargetTree.Data.PadData = b'\x00' * New_Pad_Size
|
||||
self.Remain_New_Free_Space = len(TargetTree.Data.OriData) + len(TargetTree.Data.PadData) - len(CompressedData) - New_Pad_Size
|
||||
else:
|
||||
TargetTree.Data.PadData = b''
|
||||
self.Remain_New_Free_Space = len(TargetTree.Data.OriData) - len(CompressedData)
|
||||
TargetTree.Data.OriData = CompressedData
|
||||
elif len(CompressedData) == len(TargetTree.Data.OriData):
|
||||
TargetTree.Data.OriData = CompressedData
|
||||
elif len(CompressedData) > len(TargetTree.Data.OriData):
|
||||
New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)
|
||||
self.Remain_New_Free_Space = len(CompressedData) + New_Pad_Size - len(TargetTree.Data.OriData) - len(TargetTree.Data.PadData)
|
||||
self.ModifyTest(TargetTree, self.Remain_New_Free_Space)
|
||||
self.Status = True
|
||||
|
||||
def ModifyTest(self, ParTree, Needed_Space: int) -> None:
|
||||
# If have needed space, will find if there have free space in parent tree, meanwhile update the node data.
|
||||
if Needed_Space > 0:
|
||||
# If current node is a Fv node
|
||||
if ParTree.type == FV_TREE or ParTree.type == SEC_FV_TREE:
|
||||
ParTree.Data.Data = b''
|
||||
# First check if Fv free space is enough for needed space.
|
||||
# If so, use the current Fv free space;
|
||||
# Else, use all the Free space, and recalculate needed space, continue finding in its parent node.
|
||||
Needed_Space = Needed_Space - ParTree.Data.Free_Space
|
||||
if Needed_Space < 0:
|
||||
ParTree.Child[-1].Data.Data = b'\xff' * (-Needed_Space)
|
||||
ParTree.Data.Free_Space = (-Needed_Space)
|
||||
self.Status = True
|
||||
else:
|
||||
if ParTree.type == FV_TREE:
|
||||
self.Status = False
|
||||
else:
|
||||
BlockSize = ParTree.Data.Header.BlockMap[0].Length
|
||||
New_Add_Len = BlockSize - Needed_Space%BlockSize
|
||||
if New_Add_Len % BlockSize:
|
||||
ParTree.Child[-1].Data.Data = b'\xff' * New_Add_Len
|
||||
ParTree.Data.Free_Space = New_Add_Len
|
||||
Needed_Space += New_Add_Len
|
||||
else:
|
||||
ParTree.Child.remove(ParTree.Child[-1])
|
||||
ParTree.Data.Free_Space = 0
|
||||
ParTree.Data.Size += Needed_Space
|
||||
ParTree.Data.Header.Fvlength = ParTree.Data.Size
|
||||
ModifyFvSystemGuid(ParTree)
|
||||
for item in ParTree.Child:
|
||||
if item.type == FFS_FREE_SPACE:
|
||||
ParTree.Data.Data += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
ParTree.Data.ModFvExt()
|
||||
ParTree.Data.ModFvSize()
|
||||
ParTree.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(ParTree)
|
||||
ParTree.Data.ModCheckSum()
|
||||
# If current node is a Ffs node
|
||||
elif ParTree.type == FFS_TREE:
|
||||
ParTree.Data.Data = b''
|
||||
OriHeaderLen = ParTree.Data.HeaderLength
|
||||
# Update its data as adding all the header and data of its child node.
|
||||
for item in ParTree.Child:
|
||||
if item.Data.OriData:
|
||||
if item.Data.ExtHeader:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
|
||||
else:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.OriData + item.Data.PadData
|
||||
else:
|
||||
if item.Data.ExtHeader:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
ChangeSize(ParTree, -Needed_Space)
|
||||
ModifyFfsType(ParTree)
|
||||
# Recalculate pad data, update needed space with Delta_Pad_Size.
|
||||
Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
|
||||
New_Pad_Size = GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)
|
||||
Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
|
||||
Needed_Space += Delta_Pad_Size
|
||||
ParTree.Data.PadData = b'\xff' * GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)
|
||||
ParTree.Data.ModCheckSum()
|
||||
# If current node is a Section node
|
||||
elif ParTree.type == SECTION_TREE:
|
||||
OriData = ParTree.Data.Data
|
||||
OriHeaderLen = ParTree.Data.HeaderLength
|
||||
ParTree.Data.Data = b''
|
||||
# Update its data as adding all the header and data of its child node.
|
||||
for item in ParTree.Child:
|
||||
if item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type != 0x02:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
|
||||
elif item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type == 0x02:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
|
||||
else:
|
||||
ParTree.Data.Data += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData
|
||||
# If the current section is guided section
|
||||
if ParTree.Data.Type == 0x02:
|
||||
guidtool = GUIDTools().__getitem__(struct2stream(ParTree.Data.ExtHeader.SectionDefinitionGuid))
|
||||
if not guidtool.ifexist:
|
||||
logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, ParTree.Parent.Data.Name))
|
||||
raise Exception("Process Failed: GuidTool not found!")
|
||||
# Recompress current data, and recalculate the needed space
|
||||
CompressedData = guidtool.pack(ParTree.Data.Data)
|
||||
Needed_Space = len(CompressedData) - len(ParTree.Data.OriData)
|
||||
ParTree.Data.OriData = CompressedData
|
||||
New_Size = ParTree.Data.HeaderLength + len(CompressedData)
|
||||
ParTree.Data.Header.Size[0] = New_Size % (16**2)
|
||||
ParTree.Data.Header.Size[1] = New_Size % (16**4) //(16**2)
|
||||
ParTree.Data.Header.Size[2] = New_Size // (16**4)
|
||||
ParTree.Data.Size = ParTree.Data.Header.SECTION_SIZE
|
||||
ModifySectionType(ParTree)
|
||||
Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
|
||||
# Update needed space with Delta_Pad_Size
|
||||
if ParTree.NextRel:
|
||||
New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)
|
||||
Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
|
||||
ParTree.Data.PadData = b'\x00' * New_Pad_Size
|
||||
Needed_Space += Delta_Pad_Size
|
||||
else:
|
||||
ParTree.Data.PadData = b''
|
||||
if Needed_Space < 0:
|
||||
self.Remain_New_Free_Space = len(ParTree.Data.OriData) - len(CompressedData)
|
||||
# If current section is not guided section
|
||||
elif Needed_Space:
|
||||
ChangeSize(ParTree, -Needed_Space)
|
||||
ModifySectionType(ParTree)
|
||||
# Update needed space with Delta_Pad_Size
|
||||
Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
|
||||
New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)
|
||||
Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
|
||||
Needed_Space += Delta_Pad_Size
|
||||
ParTree.Data.PadData = b'\x00' * New_Pad_Size
|
||||
NewParTree = ParTree.Parent
|
||||
ROOT_TYPE = [ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE, ROOT_TREE]
|
||||
if NewParTree and NewParTree.type not in ROOT_TYPE:
|
||||
self.ModifyTest(NewParTree, Needed_Space)
|
||||
# If current node have enough space, will recompress all the related node data, return true.
|
||||
else:
|
||||
self.CompressData(ParTree)
|
||||
self.Status = True
|
||||
|
||||
def ReplaceFfs(self) -> bool:
|
||||
logger.debug('Start Replacing Process......')
|
||||
TargetFv = self.TargetFfs.Parent
|
||||
# If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.
|
||||
if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
|
||||
self.NewFfs.Data.Header.State = c_uint8(
|
||||
~self.NewFfs.Data.Header.State)
|
||||
# NewFfs parsing will not calculate the PadSize, thus recalculate.
|
||||
self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)
|
||||
if self.NewFfs.Data.Size >= self.TargetFfs.Data.Size:
|
||||
Needed_Space = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)
|
||||
# If TargetFv have enough free space, just move part of the free space to NewFfs.
|
||||
if TargetFv.Data.Free_Space >= Needed_Space:
|
||||
# Modify TargetFv Child info and BiosTree.
|
||||
TargetFv.Child[-1].Data.Data = b'\xff' * (TargetFv.Data.Free_Space - Needed_Space)
|
||||
TargetFv.Data.Free_Space -= Needed_Space
|
||||
Target_index = TargetFv.Child.index(self.TargetFfs)
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.insertChild(self.NewFfs, Target_index)
|
||||
# Modify TargetFv Header and ExtHeader info.
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModFvSize()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
# Recompress from the Fv node to update all the related node data.
|
||||
self.CompressData(TargetFv)
|
||||
# return the Status
|
||||
self.Status = True
|
||||
# If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
|
||||
else:
|
||||
if TargetFv.type == FV_TREE:
|
||||
self.Status = False
|
||||
else:
|
||||
# Recalculate TargetFv needed space to keep it match the BlockSize setting.
|
||||
Needed_Space -= TargetFv.Data.Free_Space
|
||||
BlockSize = TargetFv.Data.Header.BlockMap[0].Length
|
||||
New_Add_Len = BlockSize - Needed_Space%BlockSize
|
||||
Target_index = TargetFv.Child.index(self.TargetFfs)
|
||||
if New_Add_Len % BlockSize:
|
||||
TargetFv.Child[-1].Data.Data = b'\xff' * New_Add_Len
|
||||
TargetFv.Data.Free_Space = New_Add_Len
|
||||
Needed_Space += New_Add_Len
|
||||
TargetFv.insertChild(self.NewFfs, Target_index)
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
else:
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.Data.Free_Space = 0
|
||||
TargetFv.insertChild(self.NewFfs)
|
||||
# Encapsulate the Fv Data for update.
|
||||
TargetFv.Data.Data = b''
|
||||
for item in TargetFv.Child:
|
||||
if item.type == FFS_FREE_SPACE:
|
||||
TargetFv.Data.Data += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
TargetFv.Data.Size += Needed_Space
|
||||
# Modify TargetFv Data Header and ExtHeader info.
|
||||
TargetFv.Data.Header.FvLength = TargetFv.Data.Size
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModFvSize()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
# Start free space calculating and moving process.
|
||||
self.ModifyTest(TargetFv.Parent, Needed_Space)
|
||||
else:
|
||||
New_Free_Space = self.TargetFfs.Data.Size - self.NewFfs.Data.Size
|
||||
# If TargetFv already have free space, move the new free space into it.
|
||||
if TargetFv.Data.Free_Space:
|
||||
TargetFv.Child[-1].Data.Data += b'\xff' * New_Free_Space
|
||||
TargetFv.Data.Free_Space += New_Free_Space
|
||||
Target_index = TargetFv.Child.index(self.TargetFfs)
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.insertChild(self.NewFfs, Target_index)
|
||||
self.Status = True
|
||||
# If TargetFv do not have free space, create free space for Fv.
|
||||
else:
|
||||
New_Free_Space_Tree = BIOSTREE('FREE_SPACE')
|
||||
New_Free_Space_Tree.type = FFS_FREE_SPACE
|
||||
New_Free_Space_Tree.Data = FfsNode(b'\xff' * New_Free_Space)
|
||||
TargetFv.Data.Free_Space = New_Free_Space
|
||||
TargetFv.insertChild(New_Free_Space)
|
||||
Target_index = TargetFv.Child.index(self.TargetFfs)
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.insertChild(self.NewFfs, Target_index)
|
||||
self.Status = True
|
||||
# Modify TargetFv Header and ExtHeader info.
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModFvSize()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
# Recompress from the Fv node to update all the related node data.
|
||||
self.CompressData(TargetFv)
|
||||
logger.debug('Done!')
|
||||
return self.Status
|
||||
|
||||
def AddFfs(self) -> bool:
|
||||
logger.debug('Start Adding Process......')
|
||||
# NewFfs parsing will not calculate the PadSize, thus recalculate.
|
||||
self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)
|
||||
if self.TargetFfs.type == FFS_FREE_SPACE:
|
||||
TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)
|
||||
TargetFv = self.TargetFfs.Parent
|
||||
# If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.
|
||||
if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
|
||||
self.NewFfs.Data.Header.State = c_uint8(
|
||||
~self.NewFfs.Data.Header.State)
|
||||
# If TargetFv have enough free space, just move part of the free space to NewFfs, split free space to NewFfs and new free space.
|
||||
if TargetLen < 0:
|
||||
self.Status = True
|
||||
self.TargetFfs.Data.Data = b'\xff' * (-TargetLen)
|
||||
TargetFv.Data.Free_Space = (-TargetLen)
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
TargetFv.insertChild(self.NewFfs, -1)
|
||||
ModifyFfsType(self.NewFfs)
|
||||
# Recompress from the Fv node to update all the related node data.
|
||||
self.CompressData(TargetFv)
|
||||
elif TargetLen == 0:
|
||||
self.Status = True
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.insertChild(self.NewFfs)
|
||||
ModifyFfsType(self.NewFfs)
|
||||
# Recompress from the Fv node to update all the related node data.
|
||||
self.CompressData(TargetFv)
|
||||
# If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
|
||||
else:
|
||||
if TargetFv.type == FV_TREE:
|
||||
self.Status = False
|
||||
elif TargetFv.type == SEC_FV_TREE:
|
||||
# Recalculate TargetFv needed space to keep it match the BlockSize setting.
|
||||
BlockSize = TargetFv.Data.Header.BlockMap[0].Length
|
||||
New_Add_Len = BlockSize - TargetLen%BlockSize
|
||||
if New_Add_Len % BlockSize:
|
||||
self.TargetFfs.Data.Data = b'\xff' * New_Add_Len
|
||||
self.TargetFfs.Data.Size = New_Add_Len
|
||||
TargetLen += New_Add_Len
|
||||
TargetFv.insertChild(self.NewFfs, -1)
|
||||
TargetFv.Data.Free_Space = New_Add_Len
|
||||
else:
|
||||
TargetFv.Child.remove(self.TargetFfs)
|
||||
TargetFv.insertChild(self.NewFfs)
|
||||
TargetFv.Data.Free_Space = 0
|
||||
ModifyFfsType(self.NewFfs)
|
||||
ModifyFvSystemGuid(TargetFv)
|
||||
TargetFv.Data.Data = b''
|
||||
for item in TargetFv.Child:
|
||||
if item.type == FFS_FREE_SPACE:
|
||||
TargetFv.Data.Data += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
# Encapsulate the Fv Data for update.
|
||||
TargetFv.Data.Size += TargetLen
|
||||
TargetFv.Data.Header.FvLength = TargetFv.Data.Size
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModFvSize()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
# Start free space calculating and moving process.
|
||||
self.ModifyTest(TargetFv.Parent, TargetLen)
|
||||
else:
|
||||
# If TargetFv do not have free space, need directly move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
|
||||
TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData)
|
||||
TargetFv = self.TargetFfs.Parent
|
||||
if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
|
||||
self.NewFfs.Data.Header.State = c_uint8(
|
||||
~self.NewFfs.Data.Header.State)
|
||||
if TargetFv.type == FV_TREE:
|
||||
self.Status = False
|
||||
elif TargetFv.type == SEC_FV_TREE:
|
||||
BlockSize = TargetFv.Data.Header.BlockMap[0].Length
|
||||
New_Add_Len = BlockSize - TargetLen%BlockSize
|
||||
if New_Add_Len % BlockSize:
|
||||
New_Free_Space = BIOSTREE('FREE_SPACE')
|
||||
New_Free_Space.type = FFS_FREE_SPACE
|
||||
New_Free_Space.Data = FreeSpaceNode(b'\xff' * New_Add_Len)
|
||||
TargetLen += New_Add_Len
|
||||
TargetFv.Data.Free_Space = New_Add_Len
|
||||
TargetFv.insertChild(self.NewFfs)
|
||||
TargetFv.insertChild(New_Free_Space)
|
||||
else:
|
||||
TargetFv.insertChild(self.NewFfs)
|
||||
ModifyFfsType(self.NewFfs)
|
||||
ModifyFvSystemGuid(TargetFv)
|
||||
TargetFv.Data.Data = b''
|
||||
for item in TargetFv.Child:
|
||||
if item.type == FFS_FREE_SPACE:
|
||||
TargetFv.Data.Data += item.Data.Data + item.Data.PadData
|
||||
else:
|
||||
TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
|
||||
TargetFv.Data.Size += TargetLen
|
||||
TargetFv.Data.Header.FvLength = TargetFv.Data.Size
|
||||
TargetFv.Data.ModFvExt()
|
||||
TargetFv.Data.ModFvSize()
|
||||
TargetFv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(TargetFv)
|
||||
TargetFv.Data.ModCheckSum()
|
||||
self.ModifyTest(TargetFv.Parent, TargetLen)
|
||||
logger.debug('Done!')
|
||||
return self.Status
|
||||
|
||||
def DeleteFfs(self) -> bool:
|
||||
logger.debug('Start Deleting Process......')
|
||||
Delete_Ffs = self.TargetFfs
|
||||
Delete_Fv = Delete_Ffs.Parent
|
||||
# Calculate free space
|
||||
Add_Free_Space = Delete_Ffs.Data.Size + len(Delete_Ffs.Data.PadData)
|
||||
# If Ffs parent Fv have free space, follow the rules to merge the new free space.
|
||||
if Delete_Fv.Data.Free_Space:
|
||||
# If Fv is a Section fv, free space need to be recalculated to keep align with BlockSize.
|
||||
# Other free space saved in self.Remain_New_Free_Space, will be moved to the 1st level Fv.
|
||||
if Delete_Fv.type == SEC_FV_TREE:
|
||||
Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space
|
||||
BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length
|
||||
New_Free_Space = BlockSize - Used_Size % BlockSize
|
||||
self.Remain_New_Free_Space += Delete_Fv.Data.Free_Space + Add_Free_Space - New_Free_Space
|
||||
Delete_Fv.Child[-1].Data.Data = New_Free_Space * b'\xff'
|
||||
Delete_Fv.Data.Free_Space = New_Free_Space
|
||||
# If Fv is lst level Fv, new free space will be merged with origin free space.
|
||||
else:
|
||||
Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space
|
||||
Delete_Fv.Child[-1].Data.Data += Add_Free_Space * b'\xff'
|
||||
Delete_Fv.Data.Free_Space += Add_Free_Space
|
||||
New_Free_Space = Delete_Fv.Data.Free_Space
|
||||
# If Ffs parent Fv not have free space, will create new free space node to save the free space.
|
||||
else:
|
||||
# If Fv is a Section fv, new free space need to be recalculated to keep align with BlockSize.
|
||||
# Then create a Free spcae node to save the 0xff data, and insert into the Fv.
|
||||
# If have more space left, move to 1st level fv.
|
||||
if Delete_Fv.type == SEC_FV_TREE:
|
||||
Used_Size = Delete_Fv.Data.Size - Add_Free_Space
|
||||
BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length
|
||||
New_Free_Space = BlockSize - Used_Size % BlockSize
|
||||
self.Remain_New_Free_Space += Add_Free_Space - New_Free_Space
|
||||
Add_Free_Space = New_Free_Space
|
||||
# If Fv is lst level Fv, new free space node will be created to save the free space.
|
||||
else:
|
||||
Used_Size = Delete_Fv.Data.Size - Add_Free_Space
|
||||
New_Free_Space = Add_Free_Space
|
||||
New_Free_Space_Info = FfsNode(Add_Free_Space * b'\xff')
|
||||
New_Free_Space_Info.Data = Add_Free_Space * b'\xff'
|
||||
New_Ffs_Tree = BIOSTREE(New_Free_Space_Info.Name)
|
||||
New_Ffs_Tree.type = FFS_FREE_SPACE
|
||||
New_Ffs_Tree.Data = New_Free_Space_Info
|
||||
Delete_Fv.insertChild(New_Ffs_Tree)
|
||||
Delete_Fv.Data.Free_Space = Add_Free_Space
|
||||
Delete_Fv.Child.remove(Delete_Ffs)
|
||||
Delete_Fv.Data.Header.FvLength = Used_Size + New_Free_Space
|
||||
Delete_Fv.Data.ModFvExt()
|
||||
Delete_Fv.Data.ModFvSize()
|
||||
Delete_Fv.Data.ModExtHeaderData()
|
||||
ModifyFvExtData(Delete_Fv)
|
||||
Delete_Fv.Data.ModCheckSum()
|
||||
# Recompress from the Fv node to update all the related node data.
|
||||
self.CompressData(Delete_Fv)
|
||||
self.Status = True
|
||||
logger.debug('Done!')
|
||||
return self.Status
|
179
BaseTools/Source/Python/FMMT/core/GuidTools.py
Normal file
179
BaseTools/Source/Python/FMMT/core/GuidTools.py
Normal file
@ -0,0 +1,179 @@
|
||||
## @file
|
||||
# This file is used to define the FMMT dependent external tool management class.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
from FirmwareStorageFormat.Common import *
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
import subprocess
|
||||
|
||||
def ExecuteCommand(cmd: list) -> None:
|
||||
subprocess.run(cmd,stdout=subprocess.DEVNULL)
|
||||
|
||||
class GUIDTool:
|
||||
def __init__(self, guid: str, short_name: str, command: str) -> None:
|
||||
self.guid: str = guid
|
||||
self.short_name: str = short_name
|
||||
self.command: str = command
|
||||
self.ifexist: bool = False
|
||||
|
||||
def pack(self, buffer: bytes) -> bytes:
|
||||
"""
|
||||
compress file.
|
||||
"""
|
||||
tool = self.command
|
||||
if tool:
|
||||
tmp = tempfile.mkdtemp(dir=os.environ.get('tmp'))
|
||||
ToolInputFile = os.path.join(tmp, "pack_uncompress_sec_file")
|
||||
ToolOuputFile = os.path.join(tmp, "pack_sec_file")
|
||||
try:
|
||||
file = open(ToolInputFile, "wb")
|
||||
file.write(buffer)
|
||||
file.close()
|
||||
command = [tool, '-e', '-o', ToolOuputFile,
|
||||
ToolInputFile]
|
||||
ExecuteCommand(command)
|
||||
buf = open(ToolOuputFile, "rb")
|
||||
res_buffer = buf.read()
|
||||
except Exception as msg:
|
||||
logger.error(msg)
|
||||
return ""
|
||||
else:
|
||||
buf.close()
|
||||
if os.path.exists(tmp):
|
||||
shutil.rmtree(tmp)
|
||||
return res_buffer
|
||||
else:
|
||||
logger.error(
|
||||
"Error parsing section: EFI_SECTION_GUID_DEFINED cannot be parsed at this time.")
|
||||
logger.info("Its GUID is: %s" % self.guid)
|
||||
return ""
|
||||
|
||||
|
||||
def unpack(self, buffer: bytes) -> bytes:
|
||||
"""
|
||||
buffer: remove common header
|
||||
uncompress file
|
||||
"""
|
||||
tool = self.command
|
||||
if tool:
|
||||
tmp = tempfile.mkdtemp(dir=os.environ.get('tmp'))
|
||||
ToolInputFile = os.path.join(tmp, "unpack_sec_file")
|
||||
ToolOuputFile = os.path.join(tmp, "unpack_uncompress_sec_file")
|
||||
try:
|
||||
file = open(ToolInputFile, "wb")
|
||||
file.write(buffer)
|
||||
file.close()
|
||||
command = [tool, '-d', '-o', ToolOuputFile, ToolInputFile]
|
||||
ExecuteCommand(command)
|
||||
buf = open(ToolOuputFile, "rb")
|
||||
res_buffer = buf.read()
|
||||
except Exception as msg:
|
||||
logger.error(msg)
|
||||
return ""
|
||||
else:
|
||||
buf.close()
|
||||
if os.path.exists(tmp):
|
||||
shutil.rmtree(tmp)
|
||||
return res_buffer
|
||||
else:
|
||||
logger.error("Error parsing section: EFI_SECTION_GUID_DEFINED cannot be parsed at this time.")
|
||||
logger.info("Its GUID is: %s" % self.guid)
|
||||
return ""
|
||||
|
||||
class GUIDTools:
|
||||
'''
|
||||
GUIDTools is responsible for reading FMMTConfig.ini, verify the tools and provide interfaces to access those tools.
|
||||
'''
|
||||
default_tools = {
|
||||
struct2stream(ModifyGuidFormat("a31280ad-481e-41b6-95e8-127f4c984779")): GUIDTool("a31280ad-481e-41b6-95e8-127f4c984779", "TIANO", "TianoCompress"),
|
||||
struct2stream(ModifyGuidFormat("ee4e5898-3914-4259-9d6e-dc7bd79403cf")): GUIDTool("ee4e5898-3914-4259-9d6e-dc7bd79403cf", "LZMA", "LzmaCompress"),
|
||||
struct2stream(ModifyGuidFormat("fc1bcdb0-7d31-49aa-936a-a4600d9dd083")): GUIDTool("fc1bcdb0-7d31-49aa-936a-a4600d9dd083", "CRC32", "GenCrc32"),
|
||||
struct2stream(ModifyGuidFormat("d42ae6bd-1352-4bfb-909a-ca72a6eae889")): GUIDTool("d42ae6bd-1352-4bfb-909a-ca72a6eae889", "LZMAF86", "LzmaF86Compress"),
|
||||
struct2stream(ModifyGuidFormat("3d532050-5cda-4fd0-879e-0f7f630d5afb")): GUIDTool("3d532050-5cda-4fd0-879e-0f7f630d5afb", "BROTLI", "BrotliCompress"),
|
||||
}
|
||||
|
||||
def __init__(self, tooldef_file: str=None) -> None:
|
||||
self.dir = os.path.join(os.path.dirname(__file__), "..")
|
||||
self.tooldef_file = tooldef_file if tooldef_file else os.path.join(self.dir, "FmmtConf.ini")
|
||||
self.tooldef = dict()
|
||||
|
||||
def SetConfigFile(self) -> None:
|
||||
if os.environ['FmmtConfPath']:
|
||||
self.tooldef_file = os.path.join(os.environ['FmmtConfPath'], 'FmmtConf.ini')
|
||||
else:
|
||||
PathList = os.environ['PATH']
|
||||
for CurrentPath in PathList:
|
||||
if os.path.exists(os.path.join(CurrentPath, 'FmmtConf.ini')):
|
||||
self.tooldef_file = os.path.join(CurrentPath, 'FmmtConf.ini')
|
||||
break
|
||||
|
||||
def VerifyTools(self, guidtool) -> None:
|
||||
"""
|
||||
Verify Tools and Update Tools path.
|
||||
"""
|
||||
path_env = os.environ.get("PATH")
|
||||
path_env_list = path_env.split(os.pathsep)
|
||||
path_env_list.append(os.path.dirname(__file__))
|
||||
path_env_list = list(set(path_env_list))
|
||||
cmd = guidtool.command
|
||||
if os.path.isabs(cmd):
|
||||
if not os.path.exists(cmd):
|
||||
if guidtool not in self.default_tools:
|
||||
logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)
|
||||
logger.error("Please goto edk2 repo in current console, run 'edksetup.bat rebuild' command, and try again.\n")
|
||||
else:
|
||||
logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)
|
||||
else:
|
||||
guidtool.ifexist = True
|
||||
else:
|
||||
for syspath in path_env_list:
|
||||
if glob.glob(os.path.join(syspath, cmd+"*")):
|
||||
guidtool.ifexist = True
|
||||
break
|
||||
else:
|
||||
if guidtool not in self.default_tools:
|
||||
logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)
|
||||
logger.error("Please goto edk2 repo in current console, run 'edksetup.bat rebuild' command, and try again.\n")
|
||||
else:
|
||||
logger.error("Tool Not found %s, which causes compress/uncompress process error." % cmd)
|
||||
|
||||
def LoadingTools(self) -> None:
|
||||
self.SetConfigFile()
|
||||
if os.path.exists(self.tooldef_file):
|
||||
with open(self.tooldef_file, "r") as fd:
|
||||
config_data = fd.readlines()
|
||||
for line in config_data:
|
||||
try:
|
||||
if not line.startswith("#"):
|
||||
guid, short_name, command = line.split()
|
||||
new_format_guid = struct2stream(ModifyGuidFormat(guid.strip()))
|
||||
self.tooldef[new_format_guid] = GUIDTool(
|
||||
guid.strip(), short_name.strip(), command.strip())
|
||||
except:
|
||||
logger.error("GuidTool load error!")
|
||||
continue
|
||||
else:
|
||||
self.tooldef.update(self.default_tools)
|
||||
|
||||
def __getitem__(self, guid):
|
||||
if not self.tooldef:
|
||||
self.LoadingTools()
|
||||
guid_tool = self.tooldef.get(guid)
|
||||
if guid_tool:
|
||||
self.VerifyTools(guid_tool)
|
||||
return guid_tool
|
||||
else:
|
||||
logger.error("{} GuidTool is not defined!".format(guid))
|
||||
raise Exception("Process Failed: is not defined!")
|
||||
|
||||
guidtools = GUIDTools()
|
||||
|
31
BaseTools/Source/Python/FMMT/utils/FmmtLogger.py
Normal file
31
BaseTools/Source/Python/FMMT/utils/FmmtLogger.py
Normal file
@ -0,0 +1,31 @@
|
||||
## @file
|
||||
# This file is used to define the Fmmt Logger.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
##
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
logfile = 'FMMT_Build.log'
|
||||
if os.path.exists(logfile):
|
||||
os.remove(logfile)
|
||||
|
||||
FmmtLogger = logging.getLogger('FMMT')
|
||||
FmmtLogger.setLevel(logging.DEBUG)
|
||||
|
||||
log_stream_handler=logging.StreamHandler(sys.stdout)
|
||||
log_file_handler=logging.FileHandler(logfile)
|
||||
log_stream_handler.setLevel(logging.INFO)
|
||||
|
||||
stream_format=logging.Formatter("%(levelname)-8s: %(message)s")
|
||||
file_format=logging.Formatter("%(levelname)-8s: %(message)s")
|
||||
|
||||
log_stream_handler.setFormatter(stream_format)
|
||||
log_file_handler.setFormatter(file_format)
|
||||
|
||||
FmmtLogger.addHandler(log_stream_handler)
|
||||
FmmtLogger.addHandler(log_file_handler)
|
55
BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py
Normal file
55
BaseTools/Source/Python/FMMT/utils/FvLayoutPrint.py
Normal file
@ -0,0 +1,55 @@
|
||||
## @file
|
||||
# This file is used to define the printer for Bios layout.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from utils.FmmtLogger import FmmtLogger as logger
|
||||
|
||||
def GetFormatter(layout_format: str):
|
||||
if layout_format == 'json':
|
||||
return JsonFormatter()
|
||||
elif layout_format == 'yaml':
|
||||
return YamlFormatter()
|
||||
elif layout_format == 'html':
|
||||
return HtmlFormatter()
|
||||
else:
|
||||
return TxtFormatter()
|
||||
|
||||
class Formatter(object):
|
||||
def dump(self, layoutdict, layoutlist, outputfile: str=None) -> None:
|
||||
raise NotImplemented
|
||||
|
||||
class JsonFormatter(Formatter):
|
||||
def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=None) -> None:
|
||||
try:
|
||||
import json
|
||||
except:
|
||||
TxtFormatter().dump(layoutdict, layoutlist, outputfile)
|
||||
return
|
||||
print(outputfile)
|
||||
if outputfile:
|
||||
with open(outputfile,"w") as fw:
|
||||
json.dump(layoutdict, fw, indent=2)
|
||||
else:
|
||||
print(json.dumps(layoutdict,indent=2))
|
||||
|
||||
class TxtFormatter(Formatter):
|
||||
def LogPrint(self,layoutlist: list) -> None:
|
||||
for item in layoutlist:
|
||||
print(item)
|
||||
print('\n')
|
||||
|
||||
def dump(self,layoutdict: dict, layoutlist: list, outputfile: str=None) -> None:
|
||||
logger.info('Binary Layout Info is saved in {} file.'.format(outputfile))
|
||||
with open(outputfile, "w") as f:
|
||||
for item in layoutlist:
|
||||
f.writelines(item + '\n')
|
||||
|
||||
class YamlFormatter(Formatter):
|
||||
def dump(self,layoutdict, layoutlist, outputfile = None):
|
||||
TxtFormatter().dump(layoutdict, layoutlist, outputfile)
|
||||
|
||||
class HtmlFormatter(Formatter):
|
||||
def dump(self,layoutdict, layoutlist, outputfile = None):
|
||||
TxtFormatter().dump(layoutdict, layoutlist, outputfile)
|
85
BaseTools/Source/Python/FirmwareStorageFormat/Common.py
Normal file
85
BaseTools/Source/Python/FirmwareStorageFormat/Common.py
Normal file
@ -0,0 +1,85 @@
|
||||
## @file
|
||||
# This file is used to define the common C struct and functions.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from ctypes import *
|
||||
import uuid
|
||||
|
||||
# ZeroGuid = uuid.UUID('{00000000-0000-0000-0000-000000000000}')
|
||||
# EFI_FIRMWARE_FILE_SYSTEM2_GUID = uuid.UUID('{8C8CE578-8A3D-4f1c-9935-896185C32DD3}')
|
||||
# EFI_FIRMWARE_FILE_SYSTEM3_GUID = uuid.UUID('{5473C07A-3DCB-4dca-BD6F-1E9689E7349A}')
|
||||
# EFI_FFS_VOLUME_TOP_FILE_GUID = uuid.UUID('{1BA0062E-C779-4582-8566-336AE8F78F09}')
|
||||
|
||||
EFI_FIRMWARE_FILE_SYSTEM2_GUID = uuid.UUID("8c8ce578-8a3d-4f1c-9935-896185c32dd3")
|
||||
EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE = b'x\xe5\x8c\x8c=\x8a\x1cO\x995\x89a\x85\xc3-\xd3'
|
||||
# EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE = EFI_FIRMWARE_FILE_SYSTEM2_GUID.bytes
|
||||
EFI_FIRMWARE_FILE_SYSTEM3_GUID = uuid.UUID("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")
|
||||
# EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE = b'x\xe5\x8c\x8c=\x8a\x1cO\x995\x89a\x85\xc3-\xd3'
|
||||
EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE = b'z\xc0sT\xcb=\xcaM\xbdo\x1e\x96\x89\xe74\x9a'
|
||||
EFI_SYSTEM_NVDATA_FV_GUID = uuid.UUID("fff12b8d-7696-4c8b-a985-2747075b4f50")
|
||||
EFI_SYSTEM_NVDATA_FV_GUID_BYTE = b"\x8d+\xf1\xff\x96v\x8bL\xa9\x85'G\x07[OP"
|
||||
EFI_FFS_VOLUME_TOP_FILE_GUID = uuid.UUID("1ba0062e-c779-4582-8566-336ae8f78f09")
|
||||
EFI_FFS_VOLUME_TOP_FILE_GUID_BYTE = b'.\x06\xa0\x1by\xc7\x82E\x85f3j\xe8\xf7\x8f\t'
|
||||
ZEROVECTOR_BYTE = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
PADVECTOR = uuid.UUID("ffffffff-ffff-ffff-ffff-ffffffffffff")
|
||||
FVH_SIGNATURE = b'_FVH'
|
||||
|
||||
#Alignment
|
||||
SECTION_COMMON_ALIGNMENT = 4
|
||||
FFS_COMMON_ALIGNMENT = 8
|
||||
|
||||
class GUID(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Guid1', c_uint32),
|
||||
('Guid2', c_uint16),
|
||||
('Guid3', c_uint16),
|
||||
('Guid4', ARRAY(c_uint8, 8)),
|
||||
]
|
||||
|
||||
def from_list(self, listformat: list) -> None:
|
||||
self.Guid1 = listformat[0]
|
||||
self.Guid2 = listformat[1]
|
||||
self.Guid3 = listformat[2]
|
||||
for i in range(8):
|
||||
self.Guid4[i] = listformat[i+3]
|
||||
|
||||
def __cmp__(self, otherguid) -> bool:
|
||||
if not isinstance(otherguid, GUID):
|
||||
return 'Input is not the GUID instance!'
|
||||
rt = False
|
||||
if self.Guid1 == otherguid.Guid1 and self.Guid2 == otherguid.Guid2 and self.Guid3 == otherguid.Guid3:
|
||||
rt = True
|
||||
for i in range(8):
|
||||
rt = rt & (self.Guid4[i] == otherguid.Guid4[i])
|
||||
return rt
|
||||
|
||||
def ModifyGuidFormat(target_guid: str) -> GUID:
|
||||
target_guid = target_guid.replace('-', '')
|
||||
target_list = []
|
||||
start = [0,8,12,16,18,20,22,24,26,28,30]
|
||||
end = [8,12,16,18,20,22,24,26,28,30,32]
|
||||
num = len(start)
|
||||
for pos in range(num):
|
||||
new_value = int(target_guid[start[pos]:end[pos]], 16)
|
||||
target_list.append(new_value)
|
||||
new_format = GUID()
|
||||
new_format.from_list(target_list)
|
||||
return new_format
|
||||
|
||||
|
||||
# Get data from ctypes to bytes.
|
||||
def struct2stream(s) -> bytes:
|
||||
length = sizeof(s)
|
||||
p = cast(pointer(s), POINTER(c_char * length))
|
||||
return p.contents.raw
|
||||
|
||||
|
||||
|
||||
def GetPadSize(Size: int, alignment: int) -> int:
|
||||
if Size % alignment == 0:
|
||||
return 0
|
||||
Pad_Size = alignment - Size % alignment
|
||||
return Pad_Size
|
@ -0,0 +1,66 @@
|
||||
## @file
|
||||
# This file is used to define the Ffs Header C Struct.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from struct import *
|
||||
from ctypes import *
|
||||
from FirmwareStorageFormat.Common import *
|
||||
|
||||
EFI_FFS_FILE_HEADER_LEN = 24
|
||||
EFI_FFS_FILE_HEADER2_LEN = 32
|
||||
|
||||
class CHECK_SUM(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Header', c_uint8),
|
||||
('File', c_uint8),
|
||||
]
|
||||
|
||||
class EFI_FFS_INTEGRITY_CHECK(Union):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Checksum', CHECK_SUM),
|
||||
('Checksum16', c_uint16),
|
||||
]
|
||||
|
||||
|
||||
class EFI_FFS_FILE_HEADER(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Name', GUID),
|
||||
('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),
|
||||
('Type', c_uint8),
|
||||
('Attributes', c_uint8),
|
||||
('Size', ARRAY(c_uint8, 3)),
|
||||
('State', c_uint8),
|
||||
]
|
||||
|
||||
@property
|
||||
def FFS_FILE_SIZE(self) -> int:
|
||||
return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16
|
||||
|
||||
@property
|
||||
def HeaderLength(self) -> int:
|
||||
return 24
|
||||
|
||||
class EFI_FFS_FILE_HEADER2(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Name', GUID),
|
||||
('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),
|
||||
('Type', c_uint8),
|
||||
('Attributes', c_uint8),
|
||||
('Size', ARRAY(c_uint8, 3)),
|
||||
('State', c_uint8),
|
||||
('ExtendedSize', c_uint64),
|
||||
]
|
||||
|
||||
@property
|
||||
def FFS_FILE_SIZE(self) -> int:
|
||||
return self.ExtendedSize
|
||||
|
||||
@property
|
||||
def HeaderLength(self) -> int:
|
||||
return 32
|
112
BaseTools/Source/Python/FirmwareStorageFormat/FvHeader.py
Normal file
112
BaseTools/Source/Python/FirmwareStorageFormat/FvHeader.py
Normal file
@ -0,0 +1,112 @@
|
||||
## @file
|
||||
# This file is used to define the FV Header C Struct.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from ast import Str
|
||||
from struct import *
|
||||
from ctypes import *
|
||||
from FirmwareStorageFormat.Common import *
|
||||
|
||||
class EFI_FV_BLOCK_MAP_ENTRY(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('NumBlocks', c_uint32),
|
||||
('Length', c_uint32),
|
||||
]
|
||||
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_HEADER(Structure):
|
||||
_fields_ = [
|
||||
('ZeroVector', ARRAY(c_uint8, 16)),
|
||||
('FileSystemGuid', GUID),
|
||||
('FvLength', c_uint64),
|
||||
('Signature', c_uint32),
|
||||
('Attributes', c_uint32),
|
||||
('HeaderLength', c_uint16),
|
||||
('Checksum', c_uint16),
|
||||
('ExtHeaderOffset', c_uint16),
|
||||
('Reserved', c_uint8),
|
||||
('Revision', c_uint8),
|
||||
('BlockMap', ARRAY(EFI_FV_BLOCK_MAP_ENTRY, 1)),
|
||||
]
|
||||
|
||||
def Refine_FV_Header(nums):
|
||||
class EFI_FIRMWARE_VOLUME_HEADER(Structure):
|
||||
_fields_ = [
|
||||
('ZeroVector', ARRAY(c_uint8, 16)),
|
||||
('FileSystemGuid', GUID),
|
||||
('FvLength', c_uint64),
|
||||
('Signature', c_uint32),
|
||||
('Attributes', c_uint32),
|
||||
('HeaderLength', c_uint16),
|
||||
('Checksum', c_uint16),
|
||||
('ExtHeaderOffset', c_uint16),
|
||||
('Reserved', c_uint8),
|
||||
('Revision', c_uint8),
|
||||
('BlockMap', ARRAY(EFI_FV_BLOCK_MAP_ENTRY, nums)),
|
||||
]
|
||||
return EFI_FIRMWARE_VOLUME_HEADER
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):
|
||||
_fields_ = [
|
||||
('FvName', GUID),
|
||||
('ExtHeaderSize', c_uint32)
|
||||
]
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY(Structure):
|
||||
_fields_ = [
|
||||
('ExtEntrySize', c_uint16),
|
||||
('ExtEntryType', c_uint16)
|
||||
]
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE_0(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('TypeMask', c_uint32)
|
||||
]
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('TypeMask', c_uint32),
|
||||
('Types', ARRAY(GUID, 1))
|
||||
]
|
||||
|
||||
def Refine_FV_EXT_ENTRY_OEM_TYPE_Header(nums: int) -> EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE:
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('TypeMask', c_uint32),
|
||||
('Types', ARRAY(GUID, nums))
|
||||
]
|
||||
return EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE(Structure)
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE_0(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('FormatType', GUID)
|
||||
]
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('FormatType', GUID),
|
||||
('Data', ARRAY(c_uint8, 1))
|
||||
]
|
||||
|
||||
def Refine_FV_EXT_ENTRY_GUID_TYPE_Header(nums: int) -> EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE:
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('FormatType', GUID),
|
||||
('Data', ARRAY(c_uint8, nums))
|
||||
]
|
||||
return EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE(Structure)
|
||||
|
||||
class EFI_FIRMWARE_VOLUME_EXT_ENTRY_USED_SIZE_TYPE(Structure):
|
||||
_fields_ = [
|
||||
('Hdr', EFI_FIRMWARE_VOLUME_EXT_ENTRY),
|
||||
('UsedSize', c_uint32)
|
||||
]
|
110
BaseTools/Source/Python/FirmwareStorageFormat/SectionHeader.py
Normal file
110
BaseTools/Source/Python/FirmwareStorageFormat/SectionHeader.py
Normal file
@ -0,0 +1,110 @@
|
||||
## @file
|
||||
# This file is used to define the Section Header C Struct.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
||||
from struct import *
|
||||
from ctypes import *
|
||||
from FirmwareStorageFormat.Common import *
|
||||
|
||||
EFI_COMMON_SECTION_HEADER_LEN = 4
|
||||
EFI_COMMON_SECTION_HEADER2_LEN = 8
|
||||
|
||||
class EFI_COMMON_SECTION_HEADER(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Size', ARRAY(c_uint8, 3)),
|
||||
('Type', c_uint8),
|
||||
]
|
||||
|
||||
@property
|
||||
def SECTION_SIZE(self) -> int:
|
||||
return self.Size[0] | self.Size[1] << 8 | self.Size[2] << 16
|
||||
|
||||
def Common_Header_Size(self) -> int:
|
||||
return 4
|
||||
|
||||
class EFI_COMMON_SECTION_HEADER2(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('Size', ARRAY(c_uint8, 3)),
|
||||
('Type', c_uint8),
|
||||
('ExtendedSize', c_uint32),
|
||||
]
|
||||
|
||||
@property
|
||||
def SECTION_SIZE(self) -> int:
|
||||
return self.ExtendedSize
|
||||
|
||||
def Common_Header_Size(self) -> int:
|
||||
return 8
|
||||
|
||||
class EFI_COMPRESSION_SECTION(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('UncompressedLength', c_uint32),
|
||||
('CompressionType', c_uint8),
|
||||
]
|
||||
|
||||
def ExtHeaderSize(self) -> int:
|
||||
return 5
|
||||
|
||||
class EFI_FREEFORM_SUBTYPE_GUID_SECTION(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('SubTypeGuid', GUID),
|
||||
]
|
||||
|
||||
def ExtHeaderSize(self) -> int:
|
||||
return 16
|
||||
|
||||
class EFI_GUID_DEFINED_SECTION(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('SectionDefinitionGuid', GUID),
|
||||
('DataOffset', c_uint16),
|
||||
('Attributes', c_uint16),
|
||||
]
|
||||
|
||||
def ExtHeaderSize(self) -> int:
|
||||
return 20
|
||||
|
||||
def Get_USER_INTERFACE_Header(nums: int):
|
||||
class EFI_SECTION_USER_INTERFACE(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('FileNameString', ARRAY(c_uint16, nums)),
|
||||
]
|
||||
|
||||
def ExtHeaderSize(self) -> int:
|
||||
return 2 * nums
|
||||
|
||||
def GetUiString(self) -> str:
|
||||
UiString = ''
|
||||
for i in range(nums):
|
||||
if self.FileNameString[i]:
|
||||
UiString += chr(self.FileNameString[i])
|
||||
return UiString
|
||||
|
||||
return EFI_SECTION_USER_INTERFACE
|
||||
|
||||
def Get_VERSION_Header(nums: int):
|
||||
class EFI_SECTION_VERSION(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
('BuildNumber', c_uint16),
|
||||
('VersionString', ARRAY(c_uint16, nums)),
|
||||
]
|
||||
|
||||
def ExtHeaderSize(self) -> int:
|
||||
return 2 * (nums+1)
|
||||
|
||||
def GetVersionString(self) -> str:
|
||||
VersionString = ''
|
||||
for i in range(nums):
|
||||
if self.VersionString[i]:
|
||||
VersionString += chr(self.VersionString[i])
|
||||
return VersionString
|
||||
|
||||
return EFI_SECTION_VERSION
|
@ -0,0 +1,6 @@
|
||||
## @file
|
||||
# This file is used to define the Firmware Storage Format.
|
||||
#
|
||||
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
##
|
Loading…
x
Reference in New Issue
Block a user