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:
Chen, Christine 2022-04-28 20:49:37 +08:00 committed by mergify[bot]
parent 101f4c7892
commit a64b944942
22 changed files with 2713 additions and 0 deletions

View 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 "$@"

View 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% %*

View 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())

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View 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?.
![](Img/FirmwareVolumeFormat.png)
? 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)
![](Img/NodeTreeFormat.png)
? 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*** |

View 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
##

View 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)

View 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

View 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''

View 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!!!')

View 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)

View 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

View 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()

View 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)

View 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)

View 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

View File

@ -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

View 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)
]

View 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

View File

@ -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
##