Chen, Christine a64b944942 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>
2022-05-06 04:22:21 +00:00

154 lines
6.8 KiB
Python

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