Skip to content

Commit

Permalink
Fmmt enhancement (#95)
Browse files Browse the repository at this point in the history
* BaseTools: fixing FMMT ShrinkFv issue

1. FvLength not change issue;
2. FileSystemGuid align with File Size;

Cc: Rebecca Cran <[email protected]>
Cc: Liming Gao <[email protected]>
Cc: Bob Feng <[email protected]>
Signed-off-by: Yuwei Chen <[email protected]>

* BaseTools: FMMT replace output file is not generated successfully

For replace function, when target Ffs and new ffs are with
same size, the output file can not be generated successfully.
This patch fixes this issue.

Cc: Rebecca Cran <[email protected]>
Cc: Bob Feng <[email protected]>
Cc: Liming Gao <[email protected]>
Signed-off-by: Yuwei Chen <[email protected]>

* BaseTools: FMMT support ELF UPLD parser

FMMT add new function to support the .elf file parsing.
Using '-v' option, the UPLD info will be printed out.

'''
- UNIVERSAL_PAYLOAD_INFO
  - 4 bytes align (BOOLEAN)
    - Identifier
    - SpecRevision
    - Attribute
    - Revision
    - Capability
    - ProducerId
    - ImageId
UPLD Buffer
'''

Cc: Rebecca Cran <[email protected]>
Cc: Bob Feng <[email protected]>
Cc: Liming Gao <[email protected]>
Signed-off-by: Yuwei Chen <[email protected]>

---------

Signed-off-by: Yuwei Chen <[email protected]>
  • Loading branch information
YuweiChen1110 authored Jun 5, 2023
1 parent 483e2c5 commit 8d9b898
Show file tree
Hide file tree
Showing 8 changed files with 408 additions and 14 deletions.
2 changes: 2 additions & 0 deletions edk2basetools/FMMT/FMMT.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ def View(self, inputfile: str, layoutfilename: str=None, outputfile: str=None) -
ROOT_TYPE = ROOT_FFS_TREE
elif filetype == '.sec':
ROOT_TYPE = ROOT_SECTION_TREE
elif filetype == '.elf':
ROOT_TYPE = ROOT_ELF_TREE
else:
ROOT_TYPE = ROOT_TREE
ViewFile(inputfile, ROOT_TYPE, layoutfilename, outputfile)
Expand Down
36 changes: 35 additions & 1 deletion edk2basetools/FMMT/core/BinaryFactoryProduct.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
from utils.FmmtLogger import FmmtLogger as logger

ROOT_TREE = 'ROOT'
ROOT_ELF_TREE = 'ROOT_ELF_TREE'
ROOT_FV_TREE = 'ROOT_FV_TREE'
ROOT_FFS_TREE = 'ROOT_FFS_TREE'
ROOT_SECTION_TREE = 'ROOT_SECTION_TREE'

ELF_TREE = 'ELF'
ELF_SECTION_TREE = 'ELF_SECTION_TREE'
FV_TREE = 'FV'
DATA_FV_TREE = 'DATA_FV'
FFS_TREE = 'FFS'
Expand Down Expand Up @@ -49,6 +52,12 @@ def DeCompressData(self, GuidTool, Section_Data: bytes, FileName) -> bytes:
def ParserData():
pass

class ElfFactory(BinaryFactory):
type = [ROOT_ELF_TREE, ELF_TREE]

def Create_Product():
return ElfProduct()

class SectionFactory(BinaryFactory):
type = [SECTION_TREE]

Expand Down Expand Up @@ -354,6 +363,30 @@ def GetFvFromFd(self, whole_data: bytes=b'') -> list:
tmp_index += 1
return Fd_Struct

class ElfSectionProduct(BinaryProduct):
## Decompress the compressed section.
def ParserData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
pass
def ParserSectionData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
pass
def ParserProgramData(self, Section_Tree, whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
pass

class ElfProduct(BinaryProduct):

def ParserData(self, ParTree, Whole_Data: bytes, Rel_Whole_Offset: int=0) -> None:
Elf_Info = ElfNode(Whole_Data)
if Elf_Info.Header.ELF_PHOff != 0:
Elf_Info.GetProgramList(Whole_Data[Elf_Info.Header.ELF_PHOff:])
if Elf_Info.Header.ELF_SHOff != 0:
Elf_Info.GetSectionList(Whole_Data[Elf_Info.Header.ELF_SHOff:])
Elf_Info.FindUPLDSection(Whole_Data)
Elf_Tree = BIOSTREE(Elf_Info.Name)
Elf_Tree.type = ELF_TREE
Elf_Info.Data = Whole_Data[Elf_Info.HeaderLength:]
Elf_Tree.Data = Elf_Info
ParTree.insertChild(Elf_Tree)

class ParserEntry():
FactoryTable:dict = {
SECTION_TREE: SectionFactory,
Expand All @@ -364,6 +397,7 @@ class ParserEntry():
SEC_FV_TREE: FvFactory,
ROOT_FV_TREE: FdFactory,
ROOT_TREE: FdFactory,
ROOT_ELF_TREE: ElfFactory,
}

def GetTargetFactory(self, Tree_type: str) -> BinaryFactory:
Expand All @@ -377,4 +411,4 @@ def Generate_Product(self, TargetFactory: BinaryFactory, Tree, Data: bytes, Offs
def DataParser(self, Tree, Data: bytes, Offset: int) -> None:
TargetFactory = self.GetTargetFactory(Tree.type)
if TargetFactory:
self.Generate_Product(TargetFactory, Tree, Data, Offset)
self.Generate_Product(TargetFactory, Tree, Data, Offset)
52 changes: 48 additions & 4 deletions edk2basetools/FMMT/core/BiosTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
ROOT_FV_TREE = 'ROOT_FV_TREE'
ROOT_FFS_TREE = 'ROOT_FFS_TREE'
ROOT_SECTION_TREE = 'ROOT_SECTION_TREE'
ROOT_ELF_TREE = 'ROOT_ELF_TREE'

FV_TREE = 'FV'
DATA_FV_TREE = 'DATA_FV'
Expand All @@ -21,11 +22,13 @@
SECTION_TREE = 'SECTION'
SEC_FV_TREE = 'SEC_FV_IMAGE'
BINARY_DATA = 'BINARY'
ELF_TREE = 'ELF'

RootType = [ROOT_TREE, ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE]
FvType = [FV_TREE, SEC_FV_TREE]
FfsType = FFS_TREE
SecType = SECTION_TREE
ElfType = [ROOT_ELF_TREE, ELF_TREE]

class BIOSTREE:
def __init__(self, NodeName: str) -> None:
Expand Down Expand Up @@ -56,7 +59,7 @@ def insertChild(self, newNode, pos: int=None) -> None:
if len(self.Child) == 0:
self.Child.append(newNode)
else:
if not pos:
if not pos or pos == len(self.Child):
LastTree = self.Child[-1]
self.Child.append(newNode)
LastTree.NextRel = newNode
Expand Down Expand Up @@ -118,6 +121,31 @@ def parserTree(self, TargetDict: dict=None, Info: list=None, space: int=0, ParFv
Info.append("Image File: {}".format(Key))
Info.append("FilesNum: {}".format(TargetDict.get(Key).get('FilesNum')))
Info.append("\n")
elif TargetDict[Key]["Type"] == ROOT_ELF_TREE:
Info.append("ELF File: {}\n".format(Key))
elif TargetDict[Key]["Type"] == ELF_TREE:
ProducerId = ""
ImageId = ""
if TargetDict.get(Key).get('IfExist'):
Identifier = TargetDict.get(Key).get('Identifier')
for item in TargetDict.get(Key).get('ProducerId'):
ProducerId += chr(item)
for item in TargetDict.get(Key).get('ImageId'):
ImageId += chr(item)
Info.append("- UNIVERSAL_PAYLOAD_INFO")
Info.append(" - 4 bytes align: {}".format(TargetDict.get(Key).get('Upld_Info_Align')))
Info.append(" - Identifier: {} # 0x48444c50--PLDH / 0x444c5055--UPLD".format(hex(Identifier)))
Info.append(" - SpecRevision: {}".format(hex(TargetDict.get(Key).get('SpecRevision'))))
Info.append(" - Attribute: {}".format(hex(TargetDict.get(Key).get('Attribute'))))
Info.append(" - Revision: {}".format(hex(TargetDict.get(Key).get('Revision'))))
Info.append(" - Capability: {}".format(hex(TargetDict.get(Key).get('Capability'))))
Info.append(" - ProducerId: {}".format(ProducerId))
Info.append(" - ImageId: {}".format(ImageId))
Info.append("\n")
Info.append("- UPLD buffer")
Info.append(" Buffer: {}".format(TargetDict.get(Key).get('Upld_Buffer')))
else:
print("Do not find the Upld Info section!!!\n")
elif TargetDict[Key]["Type"] in FvType:
space += 2
if TargetDict[Key]["Type"] == SEC_FV_TREE:
Expand Down Expand Up @@ -146,13 +174,29 @@ 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:
if self.type == ROOT_TREE or self.type == ROOT_FV_TREE or self.type == ROOT_FFS_TREE or self.type == ROOT_SECTION_TREE or self.type == ROOT_ELF_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:
elif self.type == ELF_TREE:
key = str(self.Data.Name)
TreeInfo[key] = collections.OrderedDict()
TreeInfo[key]["Name"] = key
TreeInfo[key]["Type"] = self.type
TreeInfo[key]["IfExist"] = self.Data.UpldInfo
if self.Data.UpldInfo:
TreeInfo[key]["Identifier"] = self.Data.UpldInfo.Identifier
TreeInfo[key]["SpecRevision"] = self.Data.UpldInfo.SpecRevision
TreeInfo[key]["Attribute"] = self.Data.UpldInfo.Attribute
TreeInfo[key]["Revision"] = self.Data.UpldInfo.Revision
TreeInfo[key]["Capability"] = self.Data.UpldInfo.Capability
TreeInfo[key]["ProducerId"] = self.Data.UpldInfo.ProducerId
TreeInfo[key]["ImageId"] = self.Data.UpldInfo.ImageId
TreeInfo[key]["Upld_Info_Align"] = self.Data.Upld_Info_Align
TreeInfo[key]["Upld_Buffer"] = self.Data.UpldBuffer
elif self.type == FV_TREE or self.type == SEC_FV_TREE:
key = str(self.Data.FvId)
TreeInfo[key] = collections.OrderedDict()
TreeInfo[key]["Name"] = key
Expand Down Expand Up @@ -195,4 +239,4 @@ def ExportTree(self,TreeInfo: dict=None) -> dict:
for item in self.Child:
TreeInfo[key].setdefault('Files',[]).append( item.ExportTree())

return TreeInfo
return TreeInfo
56 changes: 55 additions & 1 deletion edk2basetools/FMMT/core/BiosTreeNode.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
from FirmwareStorageFormat.UPLHeader import *
from FirmwareStorageFormat.FvHeader import *
from FirmwareStorageFormat.FfsFileHeader import *
from FirmwareStorageFormat.SectionHeader import *
Expand Down Expand Up @@ -37,6 +38,59 @@ def __init__(self, name: str) -> None:
self.HOffset = 0
self.Data = b''

class ElfNode:
def __init__(self, buffer: bytes) -> None:
self.Header = ELF_HEADER32.from_buffer_copy(buffer)
if self.Header.ELF_Identification[0:4] != b'\x7fELF':
logger.error('Invalid Elf Header! Elf Identification {} is not ".ELF".'.format(self.Header.ELF_Identification))
raise Exception("Process Failed: Invalid ELF Header Identification!")
self.Class = self.Header.ELF_Identification[4]
if self.Class == 0x02:
self.Header = ELF_HEADER64.from_buffer_copy(buffer)
elif self.Class != 0x01:
logger.error('Invalid Elf Class! Elf Class {} is not 0x01 or 0x02.'.format(self.Class))
raise Exception("Process Failed: Invalid ELF Class!")

self.ProList = []
self.SecList = []
self.UpldInfoSection = None
self.UpldInfo = None
self.UpldBuffer = b''
self.Name = "ELF"
self.HeaderLength = len(struct2stream(self.Header))
self.HOffset = 0
self.DOffset = 0
self.ROffset = 0
self.Data = b''
self.PadData = b''
self.Upld_Info_Align = False

def GetProgramList(self, buffer: bytes) -> None:
for i in range(self.Header.ELF_PHNum):
if self.Class == 0x01:
ElfProgramHeader = ELF_PROGRAM_HEADER32.from_buffer_copy(buffer[i*self.Header.ELF_PHEntSize:])
elif self.Class == 0x02:
ElfProgramHeader = ELF_PROGRAM_HEADER64.from_buffer_copy(buffer[i*self.Header.ELF_PHEntSize:])
self.ProList.append(ElfProgramHeader)

def GetSectionList(self, buffer: bytes) -> None:
for i in range(self.Header.ELF_SHNum):
if self.Class == 0x01:
ElfSectionHeader = ELF_SECTION_HEADER32.from_buffer_copy(buffer[i*self.Header.ELF_SHEntSize:])
elif self.Class == 0x02:
ElfSectionHeader = ELF_SECTION_HEADER64.from_buffer_copy(buffer[i*self.Header.ELF_SHEntSize:])
self.SecList.append(ElfSectionHeader)

def FindUPLDSection(self, buffer: bytes) -> None:
for item in self.SecList:
if buffer[item.SH_Offset:item.SH_Offset+4] == b'PLDH' or buffer[item.SH_Offset:item.SH_Offset+4] == b'UPLD':
self.UpldInfoSection = item
self.UpldInfo = UNIVERSAL_PAYLOAD_INFO.from_buffer_copy(buffer[item.SH_Offset:item.SH_Offset+item.SH_Size])
self.UpldBuffer = struct2stream(self.UpldInfo)
if (self.UpldInfoSection.SH_Offset) % 4 == 0:
# if (self.UpldInfoSection.SH_Offset - self.Header.ELF_Entry) % 4 == 0:
self.Upld_Info_Align = True

class FvNode:
def __init__(self, name, buffer: bytes) -> None:
self.Header = EFI_FIRMWARE_VOLUME_HEADER.from_buffer_copy(buffer)
Expand Down Expand Up @@ -191,4 +245,4 @@ def __init__(self, buffer: bytes) -> None:
self.HOffset = 0
self.DOffset = 0
self.ROffset = 0
self.PadData = b''
self.PadData = b''
2 changes: 1 addition & 1 deletion edk2basetools/FMMT/core/FMMTOperation.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def ExtractFfs(inputfile: str, Ffs_name: str, outputfile: str, Fv_name: str=None
FmmtParser.WholeFvTree.Findlist.remove(FmmtParser.WholeFvTree.Findlist[index])
if FmmtParser.WholeFvTree.Findlist != []:
TargetNode = FmmtParser.WholeFvTree.Findlist[0]
if TargetNode.type == FV_TREE or SEC_FV_TREE or DATA_FV_TREE:
if TargetNode.type == FV_TREE or TargetNode.type == SEC_FV_TREE or TargetNode.type == DATA_FV_TREE:
FinalData = struct2stream(TargetNode.Data.Header) + TargetNode.Data.Data
with open(outputfile, "wb") as f:
f.write(FinalData)
Expand Down
2 changes: 1 addition & 1 deletion edk2basetools/FMMT/core/FMMTParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self, name: str, TYPE: str) -> None:

## 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:
if WholeFvTree.type == ROOT_TREE or WholeFvTree.type == ROOT_FV_TREE or WholeFvTree.type == ROOT_ELF_TREE:
ParserEntry().DataParser(self.WholeFvTree, whole_data, Reloffset)
else:
ParserEntry().DataParser(WholeFvTree, whole_data, Reloffset)
Expand Down
28 changes: 22 additions & 6 deletions edk2basetools/FMMT/core/FvHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def ModifyTest(self, ParTree, Needed_Space: int) -> None:
ParTree.Child.remove(ParTree.Child[-1])
ParTree.Data.Free_Space = 0
ParTree.Data.Size += Needed_Space
ParTree.Data.Header.Fvlength = ParTree.Data.Size
ParTree.Data.Header.FvLength = ParTree.Data.Size
ModifyFvSystemGuid(ParTree)
for item in ParTree.Child:
if item.type == FFS_FREE_SPACE:
Expand Down Expand Up @@ -387,7 +387,21 @@ def ReplaceFfs(self) -> bool:
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:
if Needed_Space == 0:
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
elif 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
Expand Down Expand Up @@ -450,7 +464,6 @@ def ReplaceFfs(self) -> bool:
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')
Expand All @@ -461,7 +474,6 @@ def ReplaceFfs(self) -> bool:
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()
Expand All @@ -470,6 +482,7 @@ def ReplaceFfs(self) -> bool:
TargetFv.Data.ModCheckSum()
# Recompress from the Fv node to update all the related node data.
self.CompressData(TargetFv)
self.Status = True
logger.debug('Done!')
return self.Status

Expand Down Expand Up @@ -650,8 +663,11 @@ def ShrinkFv(self) -> bool:
Removed_Space = TargetFv.Data.Free_Space - New_Free_Space
TargetFv.Child[-1].Data.Data = b'\xff' * New_Free_Space
TargetFv.Data.Size -= Removed_Space
TargetFv.Data.Header.Fvlength = TargetFv.Data.Size
ModifyFvSystemGuid(TargetFv)
TargetFv.Data.Header.FvLength = TargetFv.Data.Size
if struct2stream(TargetFv.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE:
if TargetFv.Data.Size <= 0xFFFFFF:
TargetFv.Data.Header.FileSystemGuid = ModifyGuidFormat(
"8c8ce578-8a3d-4f1c-9935-896185c32dd3")
for item in TargetFv.Child:
if item.type == FFS_FREE_SPACE:
TargetFv.Data.Data += item.Data.Data + item.Data.PadData
Expand Down
Loading

0 comments on commit 8d9b898

Please sign in to comment.