From: Yingke Liu Date: Tue, 23 Jun 2015 06:46:01 +0000 (+0000) Subject: BaseTools: Supported FMP capsule image. X-Git-Tag: edk2-stable201903~9593 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=a3251d844695f90711bfe094f12755fd01742369 BaseTools: Supported FMP capsule image. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Yingke Liu Reviewed-by: Liming Gao git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17678 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/BaseTools/Source/Python/Common/Misc.py b/BaseTools/Source/Python/Common/Misc.py index fc1680b5ed..8ba5819cc1 100644 --- a/BaseTools/Source/Python/Common/Misc.py +++ b/BaseTools/Source/Python/Common/Misc.py @@ -24,6 +24,7 @@ import re import cPickle import array import shutil +from struct import pack from UserDict import IterableUserDict from UserList import UserList @@ -2007,6 +2008,26 @@ class SkuClass(): AvailableSkuIdSet = property(__GetAvailableSkuIds) SkuUsageType = property(__SkuUsageType) AvailableSkuIdNumSet = property(__GetAvailableSkuIdNumber) + +# +# Pack a registry format GUID +# +def PackRegistryFormatGuid(Guid): + Guid = Guid.split('-') + return pack('=LHHBBBBBBBB', + int(Guid[0], 16), + int(Guid[1], 16), + int(Guid[2], 16), + int(Guid[3][-4:-2], 16), + int(Guid[3][-2:], 16), + int(Guid[4][-12:-10], 16), + int(Guid[4][-10:-8], 16), + int(Guid[4][-8:-6], 16), + int(Guid[4][-6:-4], 16), + int(Guid[4][-4:-2], 16), + int(Guid[4][-2:], 16) + ) + ## # # This acts like the main() function for the script, unless it is 'import'ed into another diff --git a/BaseTools/Source/Python/CommonDataClass/FdfClass.py b/BaseTools/Source/Python/CommonDataClass/FdfClass.py index ce3df12058..f758d35ba1 100644 --- a/BaseTools/Source/Python/CommonDataClass/FdfClass.py +++ b/BaseTools/Source/Python/CommonDataClass/FdfClass.py @@ -360,6 +360,7 @@ class CapsuleClassObject : # TokensDict[var] = value self.TokensDict = {} self.CapsuleDataList = [] + self.FmpPayloadList = [] ## VTF data in FDF # diff --git a/BaseTools/Source/Python/GenFds/Capsule.py b/BaseTools/Source/Python/GenFds/Capsule.py index 85f95a69c1..1683433e43 100644 --- a/BaseTools/Source/Python/GenFds/Capsule.py +++ b/BaseTools/Source/Python/GenFds/Capsule.py @@ -22,6 +22,9 @@ import subprocess import StringIO from Common.Misc import SaveFileOnChange from GenFds import GenFds +from Common.Misc import PackRegistryFormatGuid +import uuid +from struct import pack T_CHAR_LF = '\n' @@ -42,6 +45,88 @@ class Capsule (CapsuleClassObject) : self.BlockNum = None self.CapsuleName = None + ## Generate FMP capsule + # + # @retval string Generated Capsule file path + # + def GenFmpCapsule(self): + # + # Generate capsule header + # typedef struct { + # EFI_GUID CapsuleGuid; + # UINT32 HeaderSize; + # UINT32 Flags; + # UINT32 CapsuleImageSize; + # } EFI_CAPSULE_HEADER; + # + Header = StringIO.StringIO() + # + # Use FMP capsule GUID: 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A + # + Header.write(PackRegistryFormatGuid('6DCBD5ED-E82D-4C44-BDA1-7194199AD92A')) + HdrSize = 0 + if 'CAPSULE_HEADER_SIZE' in self.TokensDict: + Header.write(pack('=I', int(self.TokensDict['CAPSULE_HEADER_SIZE'], 16))) + HdrSize = int(self.TokensDict['CAPSULE_HEADER_SIZE'], 16) + else: + Header.write(pack('=I', 0x20)) + HdrSize = 0x20 + Flags = 0 + if 'CAPSULE_FLAGS' in self.TokensDict: + for flag in self.TokensDict['CAPSULE_FLAGS'].split(','): + flag = flag.strip() + if flag == 'PopulateSystemTable': + Flags |= 0x00010000 | 0x00020000 + elif flag == 'PersistAcrossReset': + Flags |= 0x00010000 + elif flag == 'InitiateReset': + Flags |= 0x00040000 + Header.write(pack('=I', Flags)) + # + # typedef struct { + # UINT32 Version; + # UINT16 EmbeddedDriverCount; + # UINT16 PayloadItemCount; + # // UINT64 ItemOffsetList[]; + # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER; + # + FwMgrHdr = StringIO.StringIO() + if 'CAPSULE_HEADER_INIT_VERSION' in self.TokensDict: + FwMgrHdr.write(pack('=I', int(self.TokensDict['CAPSULE_HEADER_INIT_VERSION'], 16))) + else: + FwMgrHdr.write(pack('=I', 0x00000001)) + FwMgrHdr.write(pack('=HH', len(self.CapsuleDataList), len(self.FmpPayloadList))) + FwMgrHdrSize = 4+2+2+8*(len(self.CapsuleDataList)+len(self.FmpPayloadList)) + + PreSize = FwMgrHdrSize + Content = StringIO.StringIO() + for driver in self.CapsuleDataList: + FileName = driver.GenCapsuleSubItem() + FwMgrHdr.write(pack('=Q', PreSize)) + PreSize += os.path.getsize(FileName) + File = open(FileName, 'rb') + Content.write(File.read()) + File.close() + for fmp in self.FmpPayloadList: + payload = fmp.GenCapsuleSubItem() + FwMgrHdr.write(pack('=Q', PreSize)) + PreSize += len(payload) + Content.write(payload) + BodySize = len(FwMgrHdr.getvalue()) + len(Content.getvalue()) + Header.write(pack('=I', HdrSize + BodySize)) + # + # The real capsule header structure is 28 bytes + # + Header.write('\x00'*(HdrSize-28)) + Header.write(FwMgrHdr.getvalue()) + Header.write(Content.getvalue()) + # + # Generate FMP capsule file + # + CapOutputFile = os.path.join(GenFdsGlobalVariable.FvDir, self.UiCapsuleName) + '.Cap' + SaveFileOnChange(CapOutputFile, Header.getvalue(), True) + return CapOutputFile + ## Generate capsule # # @param self The object pointer @@ -52,6 +137,10 @@ class Capsule (CapsuleClassObject) : return GenFds.ImageBinDict[self.UiCapsuleName.upper() + 'cap'] GenFdsGlobalVariable.InfLogger( "\nGenerate %s Capsule" %self.UiCapsuleName) + if ('CAPSULE_GUID' in self.TokensDict and + uuid.UUID(self.TokensDict['CAPSULE_GUID']) == uuid.UUID('6DCBD5ED-E82D-4C44-BDA1-7194199AD92A')): + return self.GenFmpCapsule() + CapInfFile = self.GenCapInf() CapInfFile.writelines("[files]" + T_CHAR_LF) CapFileList = [] diff --git a/BaseTools/Source/Python/GenFds/CapsuleData.py b/BaseTools/Source/Python/GenFds/CapsuleData.py index 2d532bea04..efc281222b 100644 --- a/BaseTools/Source/Python/GenFds/CapsuleData.py +++ b/BaseTools/Source/Python/GenFds/CapsuleData.py @@ -18,6 +18,9 @@ import Ffs from GenFdsGlobalVariable import GenFdsGlobalVariable import StringIO +from struct import pack +import os +from Common.Misc import SaveFileOnChange ## base class for capsule data # @@ -154,4 +157,71 @@ class CapsuleAfile (CapsuleData): # @retval string Generated file name # def GenCapsuleSubItem(self): - return self.FileName \ No newline at end of file + return self.FileName + +class CapsulePayload(CapsuleData): + '''Generate payload file, the header is defined below: + #pragma pack(1) + typedef struct { + UINT32 Version; + EFI_GUID UpdateImageTypeId; + UINT8 UpdateImageIndex; + UINT8 reserved_bytes[3]; + UINT32 UpdateImageSize; + UINT32 UpdateVendorCodeSize; + UINT64 UpdateHardwareInstance; //Introduced in v2 + } EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER; + ''' + def __init__(self): + self.UiName = None + self.Version = None + self.ImageTypeId = None + self.ImageIndex = None + self.HardwareInstance = None + self.ImageFile = None + self.VendorCodeFile = None + + def GenCapsuleSubItem(self): + if not self.Version: + self.Version = 0x00000002 + ImageFileSize = os.path.getsize(self.ImageFile) + VendorFileSize = 0 + if self.VendorCodeFile: + VendorFileSize = os.path.getsize(self.VendorCodeFile) + + # + # Fill structure + # + Guid = self.ImageTypeId.split('-') + Buffer = pack('=ILHHBBBBBBBBBBBBIIQ', + int(self.Version,16), + int(Guid[0], 16), + int(Guid[1], 16), + int(Guid[2], 16), + int(Guid[3][-4:-2], 16), + int(Guid[3][-2:], 16), + int(Guid[4][-12:-10], 16), + int(Guid[4][-10:-8], 16), + int(Guid[4][-8:-6], 16), + int(Guid[4][-6:-4], 16), + int(Guid[4][-4:-2], 16), + int(Guid[4][-2:], 16), + int(self.ImageIndex, 16), + 0, + 0, + 0, + ImageFileSize, + VendorFileSize, + int(self.HardwareInstance, 16) + ) + # + # Append file content to the structure + # + ImageFile = open(self.ImageFile, 'rb') + Buffer += ImageFile.read() + ImageFile.close() + if self.VendorCodeFile: + VendorFile = open(self.VendorCodeFile, 'rb') + Buffer += VendorFile.read() + VendorFile.close() + return Buffer diff --git a/BaseTools/Source/Python/GenFds/FdfParser.py b/BaseTools/Source/Python/GenFds/FdfParser.py index 8091a51f35..ffc54abc84 100644 --- a/BaseTools/Source/Python/GenFds/FdfParser.py +++ b/BaseTools/Source/Python/GenFds/FdfParser.py @@ -194,6 +194,7 @@ class FileProfile : self.VtfList = [] self.RuleDict = {} self.OptRomDict = {} + self.FmpPayloadDict = {} ## The syntax parser for FDF # @@ -1304,6 +1305,9 @@ class FdfParser: while self.__GetFv(): pass + while self.__GetFmp(): + pass + while self.__GetCapsule(): pass @@ -1387,7 +1391,7 @@ class FdfParser: S = self.__Token.upper() if S.startswith("[") and not S.startswith("[FD."): - if not S.startswith("[FV.") and not S.startswith("[CAPSULE.") \ + if not S.startswith("[FV.") and not S.startswith('[FMPPAYLOAD.') and not S.startswith("[CAPSULE.") \ and not S.startswith("[VTF.") and not S.startswith("[RULE.") and not S.startswith("[OPTIONROM."): raise Warning("Unknown section", self.FileName, self.CurrentLineNumber) self.__UndoToken() @@ -2024,7 +2028,7 @@ class FdfParser: S = self.__Token.upper() if S.startswith("[") and not S.startswith("[FV."): - if not S.startswith("[CAPSULE.") \ + if not S.startswith('[FMPPAYLOAD.') and not S.startswith("[CAPSULE.") \ and not S.startswith("[VTF.") and not S.startswith("[RULE.") and not S.startswith("[OPTIONROM."): raise Warning("Unknown section or section appear sequence error (The correct sequence should be [FD.], [FV.], [Capsule.], [VTF.], [Rule.], [OptionRom.])", self.FileName, self.CurrentLineNumber) self.__UndoToken() @@ -2996,6 +3000,67 @@ class FdfParser: else: return True + def __GetFmp(self): + if not self.__GetNextToken(): + return False + S = self.__Token.upper() + if not S.startswith("[FMPPAYLOAD."): + if not S.startswith("[CAPSULE.") and not S.startswith("[VTF.") and not S.startswith("[RULE.") and not S.startswith("[OPTIONROM."): + raise Warning("Unknown section or section appear sequence error (The correct sequence should be [FD.], [FV.], [FmpPayload.], [Capsule.], [VTF.], [Rule.], [OptionRom.])", self.FileName, self.CurrentLineNumber) + self.__UndoToken() + return False + + self.__UndoToken() + self.__SkipToToken("[FMPPAYLOAD.", True) + FmpUiName = self.__GetUiName().upper() + if FmpUiName in self.Profile.FmpPayloadDict: + raise Warning("Duplicated FMP UI name found: %s" % FmpUiName, self.FileName, self.CurrentLineNumber) + + FmpData = CapsuleData.CapsulePayload() + FmpData.UiName = FmpUiName + + if not self.__IsToken( "]"): + raise Warning("expected ']'", self.FileName, self.CurrentLineNumber) + + if not self.__GetNextToken(): + raise Warning("The FMP payload section is empty!", self.FileName, self.CurrentLineNumber) + FmpKeyList = ['IMAGE_HEADER_INIT_VERSION', 'IMAGE_TYPE_ID', 'IMAGE_INDEX', 'HARDWARE_INSTANCE'] + while self.__Token in FmpKeyList: + Name = self.__Token + FmpKeyList.remove(Name) + if not self.__IsToken("="): + raise Warning("expected '='", self.FileName, self.CurrentLineNumber) + if Name == 'IMAGE_TYPE_ID': + if not self.__GetNextGuid(): + raise Warning("expected GUID value for IMAGE_TYPE_ID", self.FileName, self.CurrentLineNumber) + FmpData.ImageTypeId = self.__Token + else: + if not self.__GetNextToken(): + raise Warning("expected value of %s" % Name, self.FileName, self.CurrentLineNumber) + Value = self.__Token + if Name == 'IMAGE_HEADER_INIT_VERSION': + FmpData.Version = Value + elif Name == 'IMAGE_INDEX': + FmpData.ImageIndex = Value + elif Name == 'HARDWARE_INSTANCE': + FmpData.HardwareInstance = Value + if not self.__GetNextToken(): + break + else: + self.__UndoToken() + + if FmpKeyList: + raise Warning("Missing keywords %s in FMP payload section" % ', '.join(FmpKeyList), self.FileName, self.CurrentLineNumber) + ImageFile = self.__ParseRawFileStatement() + if not ImageFile: + raise Warning("Missing image file in FMP payload section", self.FileName, self.CurrentLineNumber) + FmpData.ImageFile = ImageFile + VendorCodeFile = self.__ParseRawFileStatement() + if VendorCodeFile: + FmpData.VendorCodeFile = VendorCodeFile + self.Profile.FmpPayloadDict[FmpUiName] = FmpData + return True + ## __GetCapsule() method # # Get capsule section contents and store its data into capsule list of self.Profile @@ -3070,7 +3135,7 @@ class FdfParser: def __GetCapsuleTokens(self, Obj): if not self.__GetNextToken(): return False - while self.__Token in ("CAPSULE_GUID", "CAPSULE_HEADER_SIZE", "CAPSULE_FLAGS", "OEM_CAPSULE_FLAGS"): + while self.__Token in ("CAPSULE_GUID", "CAPSULE_HEADER_SIZE", "CAPSULE_FLAGS", "OEM_CAPSULE_FLAGS", "CAPSULE_HEADER_INIT_VERSION"): Name = self.__Token.strip() if not self.__IsToken("="): raise Warning("expected '='", self.FileName, self.CurrentLineNumber) @@ -3121,7 +3186,8 @@ class FdfParser: IsFd = self.__GetFdStatement(Obj) IsAnyFile = self.__GetAnyFileStatement(Obj) IsAfile = self.__GetAfileStatement(Obj) - if not (IsInf or IsFile or IsFv or IsFd or IsAnyFile or IsAfile): + IsFmp = self.__GetFmpStatement(Obj) + if not (IsInf or IsFile or IsFv or IsFd or IsAnyFile or IsAfile or IsFmp): break ## __GetFvStatement() method @@ -3180,23 +3246,32 @@ class FdfParser: CapsuleObj.CapsuleDataList.append(CapsuleFd) return True - ## __GetAnyFileStatement() method - # - # Get AnyFile for capsule - # - # @param self The object pointer - # @param CapsuleObj for whom AnyFile is got - # @retval True Successfully find a Anyfile statement - # @retval False Not able to find a AnyFile statement - # - def __GetAnyFileStatement(self, CapsuleObj): + def __GetFmpStatement(self, CapsuleObj): + if not self.__IsKeyword("FMP"): + return False - if not self.__IsKeyword("FILE"): + if not self.__IsKeyword("PAYLOAD"): + self.__UndoToken() return False + if not self.__IsToken("="): + raise Warning("expected '='", self.FileName, self.CurrentLineNumber) + + if not self.__GetNextToken(): + raise Warning("expected payload name after FMP PAYLOAD =", self.FileName, self.CurrentLineNumber) + Payload = self.__Token.upper() + if Payload not in self.Profile.FmpPayloadDict: + raise Warning("This FMP Payload does not exist: %s" % self.__Token, self.FileName, self.CurrentLineNumber) + CapsuleObj.FmpPayloadList.append(self.Profile.FmpPayloadDict[Payload]) + return True + + def __ParseRawFileStatement(self): + if not self.__IsKeyword("FILE"): + return None + if not self.__IsKeyword("DATA"): self.__UndoToken() - return False + return None if not self.__IsToken("="): raise Warning("expected '='", self.FileName, self.CurrentLineNumber) @@ -3208,6 +3283,21 @@ class FdfParser: AnyFileName = GenFdsGlobalVariable.ReplaceWorkspaceMacro(AnyFileName) if not os.path.exists(AnyFileName): raise Warning("File %s not exists"%AnyFileName, self.FileName, self.CurrentLineNumber) + return AnyFileName + + ## __GetAnyFileStatement() method + # + # Get AnyFile for capsule + # + # @param self The object pointer + # @param CapsuleObj for whom AnyFile is got + # @retval True Successfully find a Anyfile statement + # @retval False Not able to find a AnyFile statement + # + def __GetAnyFileStatement(self, CapsuleObj): + AnyFileName = self.__ParseRawFileStatement() + if not AnyFileName: + return False CapsuleAnyFile = CapsuleData.CapsuleAnyFile() CapsuleAnyFile.FileName = AnyFileName