## @file # Parse FV image # # Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.
# This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license.php # # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. # ## Import Modules # import os import re import sys import uuid import struct import codecs import copy from UserDict import IterableUserDict from cStringIO import StringIO from array import array from CommonDataClass import * from Common.Misc import sdict, GuidStructureStringToGuidString import Common.EdkLogger as EdkLogger import EotGlobalData # Global definiton gFfsPrintTitle = "%-36s %-21s %8s %8s %8s %-4s %-36s" % ("GUID", "TYPE", "OFFSET", "SIZE", "FREE", "ALIGN", "NAME") gFfsPrintFormat = "%36s %-21s %8X %8X %8X %4s %-36s" gGuidStringFormat = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" gPeiAprioriFileNameGuid = '1b45cc0a-156a-428a-af62-49864da0e6e6' gAprioriGuid = 'fc510ee7-ffdc-11d4-bd41-0080c73c8881' gIndention = -4 ## Image() class # # A class for Image # class Image(array): _HEADER_ = struct.Struct("") _HEADER_SIZE_ = _HEADER_.size def __new__(cls, *args, **kwargs): return array.__new__(cls, 'B') def __init__(m, ID=None): if ID == None: m._ID_ = str(uuid.uuid1()).upper() else: m._ID_ = ID m._BUF_ = None m._LEN_ = None m._OFF_ = None m._SubImages = sdict() # {offset: Image()} array.__init__(m, 'B') def __repr__(m): return m._ID_ def __len__(m): Len = array.__len__(m) for Offset in m._SubImages: Len += len(m._SubImages[Offset]) return Len def _Unpack(m): m.extend(m._BUF_[m._OFF_ : m._OFF_ + m._LEN_]) return len(m) def _Pack(m, PadByte=0xFF): raise NotImplementedError def frombuffer(m, Buffer, Offset=0, Size=None): m._BUF_ = Buffer m._OFF_ = Offset # we may need the Size information in advance if it's given m._LEN_ = Size m._LEN_ = m._Unpack() def empty(m): del m[0:] def GetField(m, FieldStruct, Offset=0): return FieldStruct.unpack_from(m, Offset) def SetField(m, FieldStruct, Offset, *args): # check if there's enough space Size = FieldStruct.size if Size > len(m): m.extend([0] * (Size - len(m))) FieldStruct.pack_into(m, Offset, *args) def _SetData(m, Data): if len(m) < m._HEADER_SIZE_: m.extend([0] * (m._HEADER_SIZE_ - len(m))) else: del m[m._HEADER_SIZE_:] m.extend(Data) def _GetData(m): if len(m) > m._HEADER_SIZE_: return m[m._HEADER_SIZE_:] return None Data = property(_GetData, _SetData) ## FirmwareVolume() class # # A class for Firmware Volume # class FirmwareVolume(Image): # Read FvLength, Attributes, HeaderLength, Checksum _HEADER_ = struct.Struct("16x 1I2H8B 1Q 4x 1I 1H 1H") _HEADER_SIZE_ = _HEADER_.size _FfsGuid = "8C8CE578-8A3D-4F1C-9935-896185C32DD3" _GUID_ = struct.Struct("16x 1I2H8B") _LENGTH_ = struct.Struct("16x 16x 1Q") _SIG_ = struct.Struct("16x 16x 8x 1I") _ATTR_ = struct.Struct("16x 16x 8x 4x 1I") _HLEN_ = struct.Struct("16x 16x 8x 4x 4x 1H") _CHECKSUM_ = struct.Struct("16x 16x 8x 4x 4x 2x 1H") def __init__(self, Name=''): Image.__init__(self) self.Name = Name self.FfsDict = sdict() self.OrderedFfsDict = sdict() self.UnDispatchedFfsDict = sdict() self.NoDepexFfsDict = sdict() self.ProtocolList = sdict() def CheckArchProtocol(self): for Item in EotGlobalData.gArchProtocolGuids: if Item.lower() not in EotGlobalData.gProtocolList: return False return True def ParseDepex(self, Depex, Type): List = None if Type == 'Ppi': List = EotGlobalData.gPpiList if Type == 'Protocol': List = EotGlobalData.gProtocolList DepexStack = [] DepexList = [] DepexString = '' FileDepex = None CouldBeLoaded = True for Index in range(0, len(Depex.Expression)): Item = Depex.Expression[Index] if Item == 0x00: Index = Index + 1 Guid = gGuidStringFormat % Depex.Expression[Index] if Guid in self.OrderedFfsDict and Depex.Expression[Index + 1] == 0x08: return (True, 'BEFORE %s' % Guid, [Guid, 'BEFORE']) elif Item == 0x01: Index = Index + 1 Guid = gGuidStringFormat % Depex.Expression[Index] if Guid in self.OrderedFfsDict and Depex.Expression[Index + 1] == 0x08: return (True, 'AFTER %s' % Guid, [Guid, 'AFTER']) elif Item == 0x02: Index = Index + 1 Guid = gGuidStringFormat % Depex.Expression[Index] if Guid.lower() in List: DepexStack.append(True) DepexList.append(Guid) else: DepexStack.append(False) DepexList.append(Guid) continue elif Item == 0x03 or Item == 0x04: DepexStack.append(eval(str(DepexStack.pop()) + ' ' + Depex._OPCODE_STRING_[Item].lower() + ' ' + str(DepexStack.pop()))) DepexList.append(str(DepexList.pop()) + ' ' + Depex._OPCODE_STRING_[Item].upper() + ' ' + str(DepexList.pop())) elif Item == 0x05: DepexStack.append(eval(Depex._OPCODE_STRING_[Item].lower() + ' ' + str(DepexStack.pop()))) DepexList.append(Depex._OPCODE_STRING_[Item].lower() + ' ' + str(DepexList.pop())) elif Item == 0x06: DepexStack.append(True) DepexList.append('TRUE') DepexString = DepexString + 'TRUE' + ' ' elif Item == 0x07: DepexStack.append(False) DepexList.append('False') DepexString = DepexString + 'FALSE' + ' ' elif Item == 0x08: if Index != len(Depex.Expression) - 1: CouldBeLoaded = False else: CouldBeLoaded = DepexStack.pop() else: CouldBeLoaded = False if DepexList != []: DepexString = DepexList[0].strip() return (CouldBeLoaded, DepexString, FileDepex) def Dispatch(self, Db = None): if Db == None: return False self.UnDispatchedFfsDict = copy.copy(self.FfsDict) # Find PeiCore, DexCore, PeiPriori, DxePriori first FfsSecCoreGuid = None FfsPeiCoreGuid = None FfsDxeCoreGuid = None FfsPeiPrioriGuid = None FfsDxePrioriGuid = None for FfsID in self.UnDispatchedFfsDict: Ffs = self.UnDispatchedFfsDict[FfsID] if Ffs.Type == 0x03: FfsSecCoreGuid = FfsID continue if Ffs.Type == 0x04: FfsPeiCoreGuid = FfsID continue if Ffs.Type == 0x05: FfsDxeCoreGuid = FfsID continue if Ffs.Guid.lower() == gPeiAprioriFileNameGuid: FfsPeiPrioriGuid = FfsID continue if Ffs.Guid.lower() == gAprioriGuid: FfsDxePrioriGuid = FfsID continue # Parse SEC_CORE first if FfsSecCoreGuid != None: self.OrderedFfsDict[FfsSecCoreGuid] = self.UnDispatchedFfsDict.pop(FfsSecCoreGuid) self.LoadPpi(Db, FfsSecCoreGuid) # Parse PEI first if FfsPeiCoreGuid != None: self.OrderedFfsDict[FfsPeiCoreGuid] = self.UnDispatchedFfsDict.pop(FfsPeiCoreGuid) self.LoadPpi(Db, FfsPeiCoreGuid) if FfsPeiPrioriGuid != None: # Load PEIM described in priori file FfsPeiPriori = self.UnDispatchedFfsDict.pop(FfsPeiPrioriGuid) if len(FfsPeiPriori.Sections) == 1: Section = FfsPeiPriori.Sections.popitem()[1] if Section.Type == 0x19: GuidStruct = struct.Struct('1I2H8B') Start = 4 while len(Section) > Start: Guid = GuidStruct.unpack_from(Section[Start : Start + 16]) GuidString = gGuidStringFormat % Guid Start = Start + 16 if GuidString in self.UnDispatchedFfsDict: self.OrderedFfsDict[GuidString] = self.UnDispatchedFfsDict.pop(GuidString) self.LoadPpi(Db, GuidString) self.DisPatchPei(Db) # Parse DXE then if FfsDxeCoreGuid != None: self.OrderedFfsDict[FfsDxeCoreGuid] = self.UnDispatchedFfsDict.pop(FfsDxeCoreGuid) self.LoadProtocol(Db, FfsDxeCoreGuid) if FfsDxePrioriGuid != None: # Load PEIM described in priori file FfsDxePriori = self.UnDispatchedFfsDict.pop(FfsDxePrioriGuid) if len(FfsDxePriori.Sections) == 1: Section = FfsDxePriori.Sections.popitem()[1] if Section.Type == 0x19: GuidStruct = struct.Struct('1I2H8B') Start = 4 while len(Section) > Start: Guid = GuidStruct.unpack_from(Section[Start : Start + 16]) GuidString = gGuidStringFormat % Guid Start = Start + 16 if GuidString in self.UnDispatchedFfsDict: self.OrderedFfsDict[GuidString] = self.UnDispatchedFfsDict.pop(GuidString) self.LoadProtocol(Db, GuidString) self.DisPatchDxe(Db) def DisPatchNoDepexFfs(self, Db): # Last Load Drivers without Depex for FfsID in self.NoDepexFfsDict: NewFfs = self.NoDepexFfsDict.pop(FfsID) self.OrderedFfsDict[FfsID] = NewFfs self.LoadProtocol(Db, FfsID) return True def LoadCallbackProtocol(self): IsLoad = True for Protocol in self.ProtocolList: for Callback in self.ProtocolList[Protocol][1]: if Callback[0] not in self.OrderedFfsDict.keys(): IsLoad = False continue if IsLoad: EotGlobalData.gProtocolList[Protocol.lower()] = self.ProtocolList[Protocol][0] self.ProtocolList.pop(Protocol) def LoadProtocol(self, Db, ModuleGuid): SqlCommand = """select GuidValue from Report where SourceFileFullPath in (select Value1 from Inf where BelongsToFile = (select BelongsToFile from Inf where Value1 = 'FILE_GUID' and Value2 like '%s' and Model = %s) and Model = %s) and ItemType = 'Protocol' and ItemMode = 'Produced'""" \ % (ModuleGuid, 5001, 3007) RecordSet = Db.TblReport.Exec(SqlCommand) for Record in RecordSet: SqlCommand = """select Value2 from Inf where BelongsToFile = (select DISTINCT BelongsToFile from Inf where Value1 = (select SourceFileFullPath from Report where GuidValue like '%s' and ItemMode = 'Callback')) and Value1 = 'FILE_GUID'""" % Record[0] CallBackSet = Db.TblReport.Exec(SqlCommand) if CallBackSet != []: EotGlobalData.gProtocolList[Record[0].lower()] = ModuleGuid else: EotGlobalData.gProtocolList[Record[0].lower()] = ModuleGuid def LoadPpi(self, Db, ModuleGuid): SqlCommand = """select GuidValue from Report where SourceFileFullPath in (select Value1 from Inf where BelongsToFile = (select BelongsToFile from Inf where Value1 = 'FILE_GUID' and Value2 like '%s' and Model = %s) and Model = %s) and ItemType = 'Ppi' and ItemMode = 'Produced'""" \ % (ModuleGuid, 5001, 3007) RecordSet = Db.TblReport.Exec(SqlCommand) for Record in RecordSet: EotGlobalData.gPpiList[Record[0].lower()] = ModuleGuid def DisPatchDxe(self, Db): IsInstalled = False ScheduleList = sdict() for FfsID in self.UnDispatchedFfsDict: CouldBeLoaded = False DepexString = '' FileDepex = None Ffs = self.UnDispatchedFfsDict[FfsID] if Ffs.Type == 0x07: # Get Depex IsFoundDepex = False for Section in Ffs.Sections.values(): # Find Depex if Section.Type == 0x13: IsFoundDepex = True CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(Section._SubImages[4], 'Protocol') break if Section.Type == 0x01: CompressSections = Section._SubImages[4] for CompressSection in CompressSections.Sections: if CompressSection.Type == 0x13: IsFoundDepex = True CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(CompressSection._SubImages[4], 'Protocol') break if CompressSection.Type == 0x02: NewSections = CompressSection._SubImages[4] for NewSection in NewSections.Sections: if NewSection.Type == 0x13: IsFoundDepex = True CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(NewSection._SubImages[4], 'Protocol') break # Not find Depex if not IsFoundDepex: CouldBeLoaded = self.CheckArchProtocol() DepexString = '' FileDepex = None # Append New Ffs if CouldBeLoaded: IsInstalled = True NewFfs = self.UnDispatchedFfsDict.pop(FfsID) NewFfs.Depex = DepexString if FileDepex != None: ScheduleList.insert.insert(FileDepex[1], FfsID, NewFfs, FileDepex[0]) else: ScheduleList[FfsID] = NewFfs else: self.UnDispatchedFfsDict[FfsID].Depex = DepexString for FfsID in ScheduleList: NewFfs = ScheduleList.pop(FfsID) FfsName = 'UnKnown' self.OrderedFfsDict[FfsID] = NewFfs self.LoadProtocol(Db, FfsID) SqlCommand = """select Value2 from Inf where BelongsToFile = (select BelongsToFile from Inf where Value1 = 'FILE_GUID' and lower(Value2) = lower('%s') and Model = %s) and Model = %s and Value1='BASE_NAME'""" % (FfsID, 5001, 5001) RecordSet = Db.TblReport.Exec(SqlCommand) if RecordSet != []: FfsName = RecordSet[0][0] if IsInstalled: self.DisPatchDxe(Db) def DisPatchPei(self, Db): IsInstalled = False for FfsID in self.UnDispatchedFfsDict: CouldBeLoaded = True DepexString = '' FileDepex = None Ffs = self.UnDispatchedFfsDict[FfsID] if Ffs.Type == 0x06 or Ffs.Type == 0x08: # Get Depex for Section in Ffs.Sections.values(): if Section.Type == 0x1B: CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(Section._SubImages[4], 'Ppi') break if Section.Type == 0x01: CompressSections = Section._SubImages[4] for CompressSection in CompressSections.Sections: if CompressSection.Type == 0x1B: CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(CompressSection._SubImages[4], 'Ppi') break if CompressSection.Type == 0x02: NewSections = CompressSection._SubImages[4] for NewSection in NewSections.Sections: if NewSection.Type == 0x1B: CouldBeLoaded, DepexString, FileDepex = self.ParseDepex(NewSection._SubImages[4], 'Ppi') break # Append New Ffs if CouldBeLoaded: IsInstalled = True NewFfs = self.UnDispatchedFfsDict.pop(FfsID) NewFfs.Depex = DepexString self.OrderedFfsDict[FfsID] = NewFfs self.LoadPpi(Db, FfsID) else: self.UnDispatchedFfsDict[FfsID].Depex = DepexString if IsInstalled: self.DisPatchPei(Db) def __str__(self): global gIndention gIndention += 4 FvInfo = '\n' + ' ' * gIndention FvInfo += "[FV:%s] file_system=%s size=%x checksum=%s\n" % (self.Name, self.FileSystemGuid, self.Size, self.Checksum) FfsInfo = "\n".join([str(self.FfsDict[FfsId]) for FfsId in self.FfsDict]) gIndention -= 4 return FvInfo + FfsInfo def _Unpack(self): Size = self._LENGTH_.unpack_from(self._BUF_, self._OFF_)[0] self.empty() self.extend(self._BUF_[self._OFF_:self._OFF_+Size]) # traverse the FFS EndOfFv = Size FfsStartAddress = self.HeaderSize LastFfsObj = None while FfsStartAddress < EndOfFv: FfsObj = Ffs() FfsObj.frombuffer(self, FfsStartAddress) FfsId = repr(FfsObj) if ((self.Attributes & 0x00000800) != 0 and len(FfsObj) == 0xFFFFFF) \ or ((self.Attributes & 0x00000800) == 0 and len(FfsObj) == 0): if LastFfsObj != None: LastFfsObj.FreeSpace = EndOfFv - LastFfsObj._OFF_ - len(LastFfsObj) else: if FfsId in self.FfsDict: EdkLogger.error("FV", 0, "Duplicate GUID in FFS", ExtraData="\t%s @ %s\n\t%s @ %s" \ % (FfsObj.Guid, FfsObj.Offset, self.FfsDict[FfsId].Guid, self.FfsDict[FfsId].Offset)) self.FfsDict[FfsId] = FfsObj if LastFfsObj != None: LastFfsObj.FreeSpace = FfsStartAddress - LastFfsObj._OFF_ - len(LastFfsObj) FfsStartAddress += len(FfsObj) # # align to next 8-byte aligned address: A = (A + 8 - 1) & (~(8 - 1)) # The next FFS must be at the latest next 8-byte aligned address # FfsStartAddress = (FfsStartAddress + 7) & (~7) LastFfsObj = FfsObj def _GetAttributes(self): return self.GetField(self._ATTR_, 0)[0] def _GetSize(self): return self.GetField(self._LENGTH_, 0)[0] def _GetChecksum(self): return self.GetField(self._CHECKSUM_, 0)[0] def _GetHeaderLength(self): return self.GetField(self._HLEN_, 0)[0] def _GetFileSystemGuid(self): return gGuidStringFormat % self.GetField(self._GUID_, 0) Attributes = property(_GetAttributes) Size = property(_GetSize) Checksum = property(_GetChecksum) HeaderSize = property(_GetHeaderLength) FileSystemGuid = property(_GetFileSystemGuid) ## CompressedImage() class # # A class for Compressed Image # class CompressedImage(Image): # UncompressedLength = 4-byte # CompressionType = 1-byte _HEADER_ = struct.Struct("1I 1B") _HEADER_SIZE_ = _HEADER_.size _ORIG_SIZE_ = struct.Struct("1I") _CMPRS_TYPE_ = struct.Struct("4x 1B") def __init__(m, CompressedData=None, CompressionType=None, UncompressedLength=None): Image.__init__(m) if UncompressedLength != None: m.UncompressedLength = UncompressedLength if CompressionType != None: m.CompressionType = CompressionType if CompressedData != None: m.Data = CompressedData def __str__(m): global gIndention S = "algorithm=%s uncompressed=%x" % (m.CompressionType, m.UncompressedLength) for Sec in m.Sections: S += '\n' + str(Sec) return S def _SetOriginalSize(m, Size): m.SetField(m._ORIG_SIZE_, 0, Size) def _GetOriginalSize(m): return m.GetField(m._ORIG_SIZE_)[0] def _SetCompressionType(m, Type): m.SetField(m._CMPRS_TYPE_, 0, Type) def _GetCompressionType(m): return m.GetField(m._CMPRS_TYPE_)[0] def _GetSections(m): try: import EfiCompressor TmpData = EfiCompressor.FrameworkDecompress( m[m._HEADER_SIZE_:], len(m) - m._HEADER_SIZE_ ) DecData = array('B') DecData.fromstring(TmpData) except: import EfiCompressor TmpData = EfiCompressor.UefiDecompress( m[m._HEADER_SIZE_:], len(m) - m._HEADER_SIZE_ ) DecData = array('B') DecData.fromstring(TmpData) SectionList = [] Offset = 0 while Offset < len(DecData): Sec = Section() try: Sec.frombuffer(DecData, Offset) Offset += Sec.Size # the section is aligned to 4-byte boundary except: break SectionList.append(Sec) return SectionList UncompressedLength = property(_GetOriginalSize, _SetOriginalSize) CompressionType = property(_GetCompressionType, _SetCompressionType) Sections = property(_GetSections) ## GuidDefinedImage() class # # A class for GUID Defined Image # class GuidDefinedImage(Image): _HEADER_ = struct.Struct("1I2H8B 1H 1H") _HEADER_SIZE_ = _HEADER_.size _GUID_ = struct.Struct("1I2H8B") _DATA_OFFSET_ = struct.Struct("16x 1H") _ATTR_ = struct.Struct("18x 1H") CRC32_GUID = "FC1BCDB0-7D31-49AA-936A-A4600D9DD083" TIANO_COMPRESS_GUID = 'A31280AD-481E-41B6-95E8-127F4C984779' LZMA_COMPRESS_GUID = 'EE4E5898-3914-4259-9D6E-DC7BD79403CF' def __init__(m, SectionDefinitionGuid=None, DataOffset=None, Attributes=None, Data=None): Image.__init__(m) if SectionDefinitionGuid != None: m.SectionDefinitionGuid = SectionDefinitionGuid if DataOffset != None: m.DataOffset = DataOffset if Attributes != None: m.Attributes = Attributes if Data != None: m.Data = Data def __str__(m): S = "guid=%s" % (gGuidStringFormat % m.SectionDefinitionGuid) for Sec in m.Sections: S += "\n" + str(Sec) return S def _Unpack(m): # keep header in this Image object m.empty() m.extend(m._BUF_[m._OFF_ : m._OFF_ + m._LEN_]) return len(m) def _SetAttribute(m, Attribute): m.SetField(m._ATTR_, 0, Attribute) def _GetAttribute(m): return m.GetField(m._ATTR_)[0] def _SetGuid(m, Guid): m.SetField(m._GUID_, 0, Guid) def _GetGuid(m): return m.GetField(m._GUID_) def _SetDataOffset(m, Offset): m.SetField(m._DATA_OFFSET_, 0, Offset) def _GetDataOffset(m): return m.GetField(m._DATA_OFFSET_)[0] def _GetSections(m): SectionList = [] Guid = gGuidStringFormat % m.SectionDefinitionGuid if Guid == m.CRC32_GUID: # skip the CRC32 value, we don't do CRC32 verification here Offset = m.DataOffset - 4 while Offset < len(m): Sec = Section() try: Sec.frombuffer(m, Offset) Offset += Sec.Size # the section is aligned to 4-byte boundary Offset = (Offset + 3) & (~3) except: break SectionList.append(Sec) elif Guid == m.TIANO_COMPRESS_GUID: try: import EfiCompressor # skip the header Offset = m.DataOffset - 4 TmpData = EfiCompressor.FrameworkDecompress(m[Offset:], len(m)-Offset) DecData = array('B') DecData.fromstring(TmpData) Offset = 0 while Offset < len(DecData): Sec = Section() try: Sec.frombuffer(DecData, Offset) Offset += Sec.Size # the section is aligned to 4-byte boundary Offset = (Offset + 3) & (~3) except: break SectionList.append(Sec) except: pass elif Guid == m.LZMA_COMPRESS_GUID: try: import LzmaCompressor # skip the header Offset = m.DataOffset - 4 TmpData = LzmaCompressor.LzmaDecompress(m[Offset:], len(m)-Offset) DecData = array('B') DecData.fromstring(TmpData) Offset = 0 while Offset < len(DecData): Sec = Section() try: Sec.frombuffer(DecData, Offset) Offset += Sec.Size # the section is aligned to 4-byte boundary Offset = (Offset + 3) & (~3) except: break SectionList.append(Sec) except: pass return SectionList Attributes = property(_GetAttribute, _SetAttribute) SectionDefinitionGuid = property(_GetGuid, _SetGuid) DataOffset = property(_GetDataOffset, _SetDataOffset) Sections = property(_GetSections) ## Depex() class # # A class for Depex # class Depex(Image): _HEADER_ = struct.Struct("") _HEADER_SIZE_ = 0 _GUID_ = struct.Struct("1I2H8B") _OPCODE_ = struct.Struct("1B") _OPCODE_STRING_ = { 0x00 : "BEFORE", 0x01 : "AFTER", 0x02 : "PUSH", 0x03 : "AND", 0x04 : "OR", 0x05 : "NOT", 0x06 : "TRUE", 0x07 : "FALSE", 0x08 : "END", 0x09 : "SOR" } _NEXT_ = { -1 : _OPCODE_, # first one in depex must be an opcdoe 0x00 : _GUID_, #"BEFORE", 0x01 : _GUID_, #"AFTER", 0x02 : _GUID_, #"PUSH", 0x03 : _OPCODE_, #"AND", 0x04 : _OPCODE_, #"OR", 0x05 : _OPCODE_, #"NOT", 0x06 : _OPCODE_, #"TRUE", 0x07 : _OPCODE_, #"FALSE", 0x08 : None, #"END", 0x09 : _OPCODE_, #"SOR" } def __init__(m): Image.__init__(m) m._ExprList = [] def __str__(m): global gIndention gIndention += 4 Indention = ' ' * gIndention S = '\n' for T in m.Expression: if T in m._OPCODE_STRING_: S += Indention + m._OPCODE_STRING_[T] if T not in [0x00, 0x01, 0x02]: S += '\n' else: S += ' ' + gGuidStringFormat % T + '\n' gIndention -= 4 return S def _Unpack(m): # keep header in this Image object m.empty() m.extend(m._BUF_[m._OFF_ : m._OFF_ + m._LEN_]) return len(m) def _GetExpression(m): if m._ExprList == []: Offset = 0 CurrentData = m._OPCODE_ while Offset < len(m): Token = CurrentData.unpack_from(m, Offset) Offset += CurrentData.size if len(Token) == 1: Token = Token[0] if Token in m._NEXT_: CurrentData = m._NEXT_[Token] else: CurrentData = m._GUID_ else: CurrentData = m._OPCODE_ m._ExprList.append(Token) if CurrentData == None: break return m._ExprList Expression = property(_GetExpression) ## Ui() class # # A class for Ui # class Ui(Image): _HEADER_ = struct.Struct("") _HEADER_SIZE_ = 0 def __init__(m): Image.__init__(m) def __str__(m): return m.String def _Unpack(m): # keep header in this Image object m.empty() m.extend(m._BUF_[m._OFF_ : m._OFF_ + m._LEN_]) return len(m) def _GetUiString(m): return codecs.utf_16_decode(m[0:-2].tostring())[0] String = property(_GetUiString) ## Section() class # # A class for Section # class Section(Image): _TypeName = { 0x00 : "", 0x01 : "COMPRESSION", 0x02 : "GUID_DEFINED", 0x10 : "PE32", 0x11 : "PIC", 0x12 : "TE", 0x13 : "DXE_DEPEX", 0x14 : "VERSION", 0x15 : "USER_INTERFACE", 0x16 : "COMPATIBILITY16", 0x17 : "FIRMWARE_VOLUME_IMAGE", 0x18 : "FREEFORM_SUBTYPE_GUID", 0x19 : "RAW", 0x1B : "PEI_DEPEX" } _SectionSubImages = { 0x01 : CompressedImage, 0x02 : GuidDefinedImage, 0x17 : FirmwareVolume, 0x13 : Depex, 0x1B : Depex, 0x15 : Ui } # Size = 3-byte # Type = 1-byte _HEADER_ = struct.Struct("3B 1B") _HEADER_SIZE_ = _HEADER_.size # SubTypeGuid # _FREE_FORM_SUBTYPE_GUID_HEADER_ = struct.Struct("1I2H8B") _SIZE_ = struct.Struct("3B") _TYPE_ = struct.Struct("3x 1B") def __init__(m, Type=None, Size=None): Image.__init__(m) m._Alignment = 1 if Type != None: m.Type = Type if Size != None: m.Size = Size def __str__(m): global gIndention gIndention += 4 SectionInfo = ' ' * gIndention if m.Type in m._TypeName: SectionInfo += "[SECTION:%s] offset=%x size=%x" % (m._TypeName[m.Type], m._OFF_, m.Size) else: SectionInfo += "[SECTION:%x] offset=%x size=%x " % (m.Type, m._OFF_, m.Size) for Offset in m._SubImages: SectionInfo += ", " + str(m._SubImages[Offset]) gIndention -= 4 return SectionInfo def _Unpack(m): m.empty() Type, = m._TYPE_.unpack_from(m._BUF_, m._OFF_) Size1, Size2, Size3 = m._SIZE_.unpack_from(m._BUF_, m._OFF_) Size = Size1 + (Size2 << 8) + (Size3 << 16) if Type not in m._SectionSubImages: # no need to extract sub-image, keep all in this Image object m.extend(m._BUF_[m._OFF_ : m._OFF_ + Size]) else: # keep header in this Image object m.extend(m._BUF_[m._OFF_ : m._OFF_ + m._HEADER_SIZE_]) # # use new Image object to represent payload, which may be another kind # of image such as PE32 # PayloadOffset = m._HEADER_SIZE_ PayloadLen = m.Size - m._HEADER_SIZE_ Payload = m._SectionSubImages[m.Type]() Payload.frombuffer(m._BUF_, m._OFF_ + m._HEADER_SIZE_, PayloadLen) m._SubImages[PayloadOffset] = Payload return Size def _SetSize(m, Size): Size1 = Size & 0xFF Size2 = (Size & 0xFF00) >> 8 Size3 = (Size & 0xFF0000) >> 16 m.SetField(m._SIZE_, 0, Size1, Size2, Size3) def _GetSize(m): Size1, Size2, Size3 = m.GetField(m._SIZE_) return Size1 + (Size2 << 8) + (Size3 << 16) def _SetType(m, Type): m.SetField(m._TYPE_, 0, Type) def _GetType(m): return m.GetField(m._TYPE_)[0] def _GetAlignment(m): return m._Alignment def _SetAlignment(m, Alignment): m._Alignment = Alignment AlignmentMask = Alignment - 1 # section alignment is actually for payload, so we need to add header size PayloadOffset = m._OFF_ + m._HEADER_SIZE_ if (PayloadOffset & (~AlignmentMask)) == 0: return NewOffset = (PayloadOffset + AlignmentMask) & (~AlignmentMask) while (NewOffset - PayloadOffset) < m._HEADER_SIZE_: NewOffset += m._Alignment def tofile(m, f): m.Size = len(m) Image.tofile(m, f) for Offset in m._SubImages: m._SubImages[Offset].tofile(f) Type = property(_GetType, _SetType) Size = property(_GetSize, _SetSize) Alignment = property(_GetAlignment, _SetAlignment) # SubTypeGuid = property(_GetGuid, _SetGuid) ## PadSection() class # # A class for Pad Section # class PadSection(Section): def __init__(m, Size): Section.__init__(m) m.Type = 0x19 m.Size = Size m.Data = [0] * (Size - m._HEADER_SIZE_) ## Ffs() class # # A class for Ffs Section # class Ffs(Image): _FfsFormat = "24B%(payload_size)sB" # skip IntegrityCheck _HEADER_ = struct.Struct("1I2H8B 2x 1B 1B 3B 1B") _HEADER_SIZE_ = _HEADER_.size _NAME_ = struct.Struct("1I2H8B") _INT_CHECK_ = struct.Struct("16x 1H") _TYPE_ = struct.Struct("18x 1B") _ATTR_ = struct.Struct("19x 1B") _SIZE_ = struct.Struct("20x 3B") _STATE_ = struct.Struct("23x 1B") VTF_GUID = "1BA0062E-C779-4582-8566-336AE8F78F09" FFS_ATTRIB_FIXED = 0x04 FFS_ATTRIB_DATA_ALIGNMENT = 0x38 FFS_ATTRIB_CHECKSUM = 0x40 _TypeName = { 0x00 : "", 0x01 : "RAW", 0x02 : "FREEFORM", 0x03 : "SECURITY_CORE", 0x04 : "PEI_CORE", 0x05 : "DXE_CORE", 0x06 : "PEIM", 0x07 : "DRIVER", 0x08 : "COMBINED_PEIM_DRIVER", 0x09 : "APPLICATION", 0x0A : "SMM", 0x0B : "FIRMWARE_VOLUME_IMAGE", 0x0C : "COMBINED_SMM_DXE", 0x0D : "SMM_CORE", 0xc0 : "OEM_MIN", 0xdf : "OEM_MAX", 0xe0 : "DEBUG_MIN", 0xef : "DEBUG_MAX", 0xf0 : "FFS_MIN", 0xff : "FFS_MAX", 0xf0 : "FFS_PAD", } def __init__(self): Image.__init__(self) self.FreeSpace = 0 self.Sections = sdict() self.Depex = '' self.__ID__ = None def __str__(self): global gIndention gIndention += 4 Indention = ' ' * gIndention FfsInfo = Indention FfsInfo += "[FFS:%s] offset=%x size=%x guid=%s free_space=%x alignment=%s\n" % \ (Ffs._TypeName[self.Type], self._OFF_, self.Size, self.Guid, self.FreeSpace, self.Alignment) SectionInfo = '\n'.join([str(self.Sections[Offset]) for Offset in self.Sections]) gIndention -= 4 return FfsInfo + SectionInfo + "\n" def __len__(self): return self.Size def __repr__(self): return self.__ID__ def _Unpack(self): Size1, Size2, Size3 = self._SIZE_.unpack_from(self._BUF_, self._OFF_) Size = Size1 + (Size2 << 8) + (Size3 << 16) self.empty() self.extend(self._BUF_[self._OFF_ : self._OFF_ + Size]) # Pad FFS may use the same GUID. We need to avoid it. if self.Type == 0xf0: self.__ID__ = str(uuid.uuid1()).upper() else: self.__ID__ = self.Guid # Traverse the SECTION. RAW and PAD do not have sections if self.Type not in [0xf0, 0x01] and Size > 0 and Size < 0xFFFFFF: EndOfFfs = Size SectionStartAddress = self._HEADER_SIZE_ while SectionStartAddress < EndOfFfs: SectionObj = Section() SectionObj.frombuffer(self, SectionStartAddress) #f = open(repr(SectionObj), 'wb') #SectionObj.Size = 0 #SectionObj.tofile(f) #f.close() self.Sections[SectionStartAddress] = SectionObj SectionStartAddress += len(SectionObj) SectionStartAddress = (SectionStartAddress + 3) & (~3) def Pack(self): pass def SetFreeSpace(self, Size): self.FreeSpace = Size def _GetGuid(self): return gGuidStringFormat % self.Name def _SetName(self, Value): # Guid1, Guid2, Guid3, Guid4, Guid5, Guid6, Guid7, Guid8, Guid9, Guid10, Guid11 self.SetField(self._NAME_, 0, Value) def _GetName(self): # Guid1, Guid2, Guid3, Guid4, Guid5, Guid6, Guid7, Guid8, Guid9, Guid10, Guid11 return self.GetField(self._NAME_) def _SetSize(m, Size): Size1 = Size & 0xFF Size2 = (Size & 0xFF00) >> 8 Size3 = (Size & 0xFF0000) >> 16 m.SetField(m._SIZE_, 0, Size1, Size2, Size3) def _GetSize(m): Size1, Size2, Size3 = m.GetField(m._SIZE_) return Size1 + (Size2 << 8) + (Size3 << 16) def _SetType(m, Type): m.SetField(m._TYPE_, 0, Type) def _GetType(m): return m.GetField(m._TYPE_)[0] def _SetAttributes(self, Value): self.SetField(m._ATTR_, 0, Value) def _GetAttributes(self): return self.GetField(self._ATTR_)[0] def _GetFixed(self): if (self.Attributes & self.FFS_ATTRIB_FIXED) != 0: return True return False def _GetCheckSum(self): if (self.Attributes & self.FFS_ATTRIB_CHECKSUM) != 0: return True return False def _GetAlignment(self): return (self.Attributes & self.FFS_ATTRIB_DATA_ALIGNMENT) >> 3 def _SetState(self, Value): self.SetField(m._STATE_, 0, Value) def _GetState(self): return self.GetField(m._STATE_)[0] Name = property(_GetName, _SetName) Guid = property(_GetGuid) Type = property(_GetType, _SetType) Size = property(_GetSize, _SetSize) Attributes = property(_GetAttributes, _SetAttributes) Fixed = property(_GetFixed) Checksum = property(_GetCheckSum) Alignment = property(_GetAlignment) State = property(_GetState, _SetState) ## PeImage() class # # A class for PE Image # class PeImage: # # just extract e_lfanew # _DosHeaderFormat = "60x 1I" # # Machine # NumberOfSections # SizeOfOptionalHeader # _FileHeaderFormat = "4x 1H 1H 4x 4x 4x 1H 2x" # # Magic # SizeOfImage # SizeOfHeaders # CheckSum # NumberOfRvaAndSizes # _OptionalHeader32Format = "1H 54x 1I 1I 1I 24x 1I" _OptionalHeader64Format = "" def __init__(self, Buf, Offset, Size): self.Offset = Offset self.Size = Size self.Machine = 0x014c # IA32 self.NumberOfSections = 0 self.SizeOfImage = 0 self.SizeOfOptionalHeader = 0 self.Checksum = 0 self._PeImageBuf = Buf self._SectionList = [] self._DosHeader = struct.Struct(PeImage._DosHeaderFormat) self._FileHeader = struct.Struct(PeImage._FileHeaderFormat) self._OptionalHeader32 = struct.Struct(PeImage._OptionalHeader32Format) self.Buffer = None self._Unpack() def __str__(self): pass def __len__(self): return self.Size def _Unpack(self): # from DOS header, get the offset of PE header FileHeaderOffset, = self._DosHeader.unpack_from(self._PeImageBuf, self.Offset) if FileHeaderOffset < struct.calcsize(self._DosHeaderFormat): EdkLogger.error("PE+", 0, "Invalid offset of IMAGE_FILE_HEADER: %s" % FileHeaderOffset) # from FILE header, get the optional header size self.Machine, self.NumberOfSections, self.SizeOfOptionalHeader = \ self._FileHeader.unpack_from(self._PeImageBuf, self.Offset + FileHeaderOffset) print "Machine=%x NumberOfSections=%x SizeOfOptionalHeader=%x" % (self.Machine, self.NumberOfSections, self.SizeOfOptionalHeader) # optional header follows the FILE header OptionalHeaderOffset = FileHeaderOffset + struct.calcsize(self._FileHeaderFormat) Magic, self.SizeOfImage, SizeOfHeaders, self.Checksum, NumberOfRvaAndSizes = \ self._OptionalHeader32.unpack_from(self._PeImageBuf, self.Offset + OptionalHeaderOffset) print "Magic=%x SizeOfImage=%x SizeOfHeaders=%x, Checksum=%x, NumberOfRvaAndSizes=%x" % (Magic, self.SizeOfImage, SizeOfHeaders, self.Checksum, NumberOfRvaAndSizes) PeImageSectionTableOffset = OptionalHeaderOffset + self.SizeOfOptionalHeader PeSections = PeSectionTable(self._PeImageBuf, self.Offset + PeImageSectionTableOffset, self.NumberOfSections) print "%x" % PeSections.GetFileAddress(0x3920) ## PeSectionTable() class # # A class for PE Section Table # class PeSectionTable: def __init__(self, Buf, Offset, NumberOfSections): self._SectionList = [] SectionHeaderOffset = Offset for TableIndex in range(0, NumberOfSections): SectionHeader = PeSectionHeader(Buf, SectionHeaderOffset) self._SectionList.append(SectionHeader) SectionHeaderOffset += len(SectionHeader) print SectionHeader def GetFileAddress(self, Rva): for PeSection in self._SectionList: if Rva in PeSection: return PeSection[Rva] ## PeSectionHeader() class # # A class for PE Section Header # class PeSectionHeader: # # VirtualAddress # SizeOfRawData # PointerToRawData # _HeaderFormat = "12x 1I 1I 1I 16x" _HeaderLength = struct.calcsize(_HeaderFormat) def __init__(self, Buf, Offset): self.VirtualAddressStart, self.SizeOfRawData, self.PointerToRawData = \ struct.unpack_from(self._HeaderFormat, Buf, Offset) self.VirtualAddressEnd = self.VirtualAddressStart + self.SizeOfRawData - 1 def __str__(self): return "VirtualAddress=%x, SizeOfRawData=%x, PointerToRawData=%x" % (self.VirtualAddressStart, self.SizeOfRawData, self.PointerToRawData) def __len__(self): return self._HeaderLength def __contains__(self, Rva): return Rva >= self.VirtualAddressStart and Rva <= self.VirtualAddressEnd def __getitem__(self, Rva): return Rva - self.VirtualAddressStart + self.PointerToRawData ## LinkMap() class # # A class for Link Map # class LinkMap: _StartFlag = { "MSFT" : re.compile("Address +Publics by Value +Rva\+Base +Lib:Object"), "GCC" : re.compile("^\.(text|bss|data|edata)"), } _MappingFormat = { "MSFT" : re.compile("([0-9a-f]+):([0-9a-f]+)\s+_+([0-9A-Za-z]+)\s+([0-9a-f]+)\s+"), "GCC" : re.compile("^(\.\w)?\s+(0x[0-9a-f]+)\s+_+([0-9A-Za-z]+)"), } def __init__(self, MapFile, MapType="MSFT"): self.File = MapFile self.MapType = MapType self._Globals = {} # global:RVA self._Parse() def _Parse(self): MapFile = open(self.File, 'r') MappingTitle = self._StartFlag[self.MapType] MappingFormat = self._MappingFormat[self.MapType] MappingStart = False try: for Line in MapFile: Line = Line.strip() if not MappingStart: if MappingTitle.match(Line) != None: MappingStart = True continue ResultList = MappingFormat.findall(Line) if len(ResultList) == 0 or len(ResultList[0]) != 4: continue self._Globals[ResultList[2]] = int(ResultList[3], 16) EdkLogger.verbose(ResultList[0]) finally: MapFile.close() def __contains__(self, Var): return Var in self._Globals def __getitem__(self, Var): if Var not in self._Globals: return None return self._Globals[Var] ## MultipleFv() class # # A class for Multiple FV # class MultipleFv(FirmwareVolume): def __init__(self, FvList): FirmwareVolume.__init__(self) self.BasicInfo = [] for FvPath in FvList: FvName = os.path.splitext(os.path.split(FvPath)[1])[0] Fd = open(FvPath, 'rb') Buf = array('B') try: Buf.fromfile(Fd, os.path.getsize(FvPath)) except EOFError: pass Fv = FirmwareVolume(FvName) Fv.frombuffer(Buf, 0, len(Buf)) self.BasicInfo.append([Fv.Name, Fv.FileSystemGuid, Fv.Size]) self.FfsDict.append(Fv.FfsDict) # Version and Copyright __version_number__ = "0.01" __version__ = "%prog Version " + __version_number__ __copyright__ = "Copyright (c) 2008, Intel Corporation. All rights reserved." ## Parse command line options # # Using standard Python module optparse to parse command line option of this tool. # # @retval Options A optparse.Values object containing the parsed options # @retval InputFile Path of file to be trimmed # def GetOptions(): OptionList = [ make_option("-a", "--arch", dest="Arch", help="The input file is preprocessed source code, including C or assembly code"), make_option("-p", "--platform", dest="ActivePlatform", help="The input file is preprocessed VFR file"), make_option("-m", "--module", dest="ActiveModule", help="Convert standard hex format (0xabcd) to MASM format (abcdh)"), make_option("-f", "--FDF-file", dest="FdfFile", help="Convert standard hex format (0xabcd) to MASM format (abcdh)"), make_option("-o", "--output", dest="OutputDirectory", help="File to store the trimmed content"), make_option("-t", "--toolchain-tag", dest="ToolChain", help=""), make_option("-k", "--msft", dest="MakefileType", action="store_const", const="nmake", help=""), make_option("-g", "--gcc", dest="MakefileType", action="store_const", const="gmake", help=""), make_option("-v", "--verbose", dest="LogLevel", action="store_const", const=EdkLogger.VERBOSE, help="Run verbosely"), make_option("-d", "--debug", dest="LogLevel", type="int", help="Run with debug information"), make_option("-q", "--quiet", dest="LogLevel", action="store_const", const=EdkLogger.QUIET, help="Run quietly"), make_option("-?", action="help", help="show this help message and exit"), ] # use clearer usage to override default usage message UsageString = "%prog [-a ARCH] [-p PLATFORM] [-m MODULE] [-t TOOLCHAIN_TAG] [-k] [-g] [-v|-d |-q] [-o ] [GenC|GenMake]" Parser = OptionParser(description=__copyright__, version=__version__, option_list=OptionList, usage=UsageString) Parser.set_defaults(Arch=[]) Parser.set_defaults(ActivePlatform=None) Parser.set_defaults(ActiveModule=None) Parser.set_defaults(OutputDirectory="build") Parser.set_defaults(FdfFile=None) Parser.set_defaults(ToolChain="MYTOOLS") if sys.platform == "win32": Parser.set_defaults(MakefileType="nmake") else: Parser.set_defaults(MakefileType="gmake") Parser.set_defaults(LogLevel=EdkLogger.INFO) Options, Args = Parser.parse_args() # error check if len(Args) == 0: Options.Target = "genmake" sys.argv.append("genmake") elif len(Args) == 1: Options.Target = Args[0].lower() if Options.Target not in ["genc", "genmake"]: EdkLogger.error("AutoGen", OPTION_NOT_SUPPORTED, "Not supported target", ExtraData="%s\n\n%s" % (Options.Target, Parser.get_usage())) else: EdkLogger.error("AutoGen", OPTION_NOT_SUPPORTED, "Too many targets", ExtraData=Parser.get_usage()) return Options ## Entrance method # # This method mainly dispatch specific methods per the command line options. # If no error found, return zero value so the caller of this tool can know # if it's executed successfully or not. # # @retval 0 Tool was successful # @retval 1 Tool failed # def Main(): from build import build try: Option = GetOptions() build.main() except Exception, e: print e return 1 return 0 # This acts like the main() function for the script, unless it is 'import'ed into another script. if __name__ == '__main__': EdkLogger.Initialize() # sys.exit(Main()) if len(sys.argv) > 1: FilePath = sys.argv[1] if FilePath.lower().endswith(".fv"): fd = open(FilePath, 'rb') buf = array('B') try: buf.fromfile(fd, os.path.getsize(FilePath)) except EOFError: pass fv = FirmwareVolume("FVRECOVERY") fv.frombuffer(buf, 0, len(buf)) #fv.Dispatch(None) print fv elif FilePath.endswith(".efi"): fd = open(FilePath, 'rb') buf = array('B') Size = os.path.getsize(FilePath) try: buf.fromfile(fd, Size) except EOFError: pass PeSection = Section(Type=0x10) PeSection.Data = buf sf, ext = os.path.splitext(os.path.basename(FilePath)) sf += ".sec" PeSection.tofile(open(sf, 'wb')) elif FilePath.endswith(".map"): mf = LinkMap(FilePath)