Reviewed-by: Liming Gao <liming....@intel.com> -----Original Message----- From: Gao, Liming [mailto:liming....@intel.com] Sent: Tuesday, June 16, 2015 6:09 PM To: edk2-devel@lists.sourceforge.net Subject: Re: [edk2] [Patch] BaseTools: Supported FMP capsule image.
Hi, all This BaseTool feature is to help generate UEFI FMP format capsule image. It reuses Capsule section and bases on specific capsule GUID to know this is FMP capsule image. The following is sample in FDF file for FMP capsule. It will be documented in FDF spec. If you have any comments for it, please let me know. Platform.fdf file: [FmpPayload.Payload1] IMAGE_HEADER_INIT_VERSION = 0x02 # FMP payload header IMAGE_TYPE_ID = 938A6F2E-9711-49CE-90D5-7ED68AC96501 # FMP payload header IMAGE_INDEX = 0x1 # FMP payload header HARDWARE_INSTANCE = 0x0 # FMP payload header FILE DATA = UpdateImage.bin FILE DATA = VendorCodeByes.bin # optional [Capsule.FmpCapsuleImage] CAPSULE_GUID = 6dcbd5ed-e82d-4c44-bda1-7194199ad92a # normal header FMP special Guid defined in UEFI spec CAPSULE_FLAGS = PersistAcrossReset,InitiateReset # normal header CAPSULE_HEADER_SIZE = 0x20 # normal header CAPSULE_HEADER_INIT_VERSION = 0x1 # FMP header FILE DATA = Driver1.efi FILE DATA = Driver2.efi # zero or more FMP_PAYLOAD = Payload1 # zero or more Thanks Liming -----Original Message----- From: Yingke Liu [mailto:yingke.d....@intel.com] Sent: Monday, June 15, 2015 3:39 PM To: edk2-devel@lists.sourceforge.net Subject: [edk2] [Patch] BaseTools: Supported FMP capsule image. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Yingke Liu <yingke.d....@intel.com> --- BaseTools/Source/Python/Common/Misc.py | 21 ++++ .../Source/Python/CommonDataClass/FdfClass.py | 1 + BaseTools/Source/Python/GenFds/Capsule.py | 89 +++++++++++++++ BaseTools/Source/Python/GenFds/CapsuleData.py | 72 +++++++++++- BaseTools/Source/Python/GenFds/FdfParser.py | 122 ++++++++++++++++++--- 5 files changed, 288 insertions(+), 17 deletions(-) diff --git a/BaseTools/Source/Python/Common/Misc.py b/BaseTools/Source/Python/Common/Misc.py index fc1680b..8ba5819 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 ce3df12..f758d35 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 85f95a6..1683433 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 2d532be..efc2812 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 8091a51..ffc54ab 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 -- 1.9.5.msysgit.0 ------------------------------------------------------------------------------ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel ------------------------------------------------------------------------------ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel ------------------------------------------------------------------------------ Monitor 25 network devices or servers for free with OpManager! OpManager is web-based network management software that monitors network devices and physical & virtual servers, alerts via email & sms for fault. Monitor 25 devices for free with no restriction. Download now http://ad.doubleclick.net/ddm/clk/292181274;119417398;o _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel