--- /dev/null
+## @file\r
+#\r
+# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available\r
+# under the terms and conditions of the BSD License which accompanies this\r
+# distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+\r
+import array\r
+import uuid\r
+import re\r
+import os\r
+import logging\r
+import core.pe as pe\r
+\r
+def GetLogger():\r
+ return logging.getLogger('EFI Binary File')\r
+\r
+class EFIBinaryError(Exception):\r
+ def __init__(self, message):\r
+ Exception.__init__(self)\r
+ self._message = message\r
+\r
+ def GetMessage(self):\r
+ return self._message\r
+\r
+class EfiFd(object):\r
+ EFI_FV_HEADER_SIZE = 0x48\r
+\r
+ def __init__(self):\r
+ self._fvs = []\r
+\r
+ def Load(self, fd, size):\r
+ index = fd.tell()\r
+ while (index + self.EFI_FV_HEADER_SIZE < size):\r
+ fv = EfiFv(self)\r
+ fv.Load(fd)\r
+ self._fvs.append(fv)\r
+ index += fv.GetHeader().GetFvLength()\r
+ index = align(index, 8)\r
+ fd.seek(index)\r
+\r
+ def GetFvs(self):\r
+ return self._fvs\r
+\r
+class EfiFv(object):\r
+ FILE_SYSTEM_GUID = uuid.UUID('{8c8ce578-8a3d-4f1c-9935-896185c32dd3}')\r
+\r
+ def __init__(self, parent=None):\r
+ self._size = 0\r
+ self._filename = None\r
+ self._fvheader = None\r
+ self._blockentries = []\r
+ self._ffs = []\r
+\r
+ # following field is for FV in FD\r
+ self._parent = parent\r
+ self._offset = 0\r
+ self._raw = array.array('B')\r
+\r
+ def Load(self, fd):\r
+ self._offset = fd.tell()\r
+ self._filename = fd.name\r
+\r
+ # get file header\r
+ self._fvheader = EfiFirmwareVolumeHeader.Read(fd)\r
+ #self._fvheader.Dump()\r
+\r
+ self._size = self._fvheader.GetFvLength()\r
+\r
+ if self._fvheader.GetFileSystemGuid() != self.FILE_SYSTEM_GUID:\r
+ fd.seek(self._offset)\r
+ self._raw.fromfile(fd, self.GetHeader().GetFvLength())\r
+ return\r
+\r
+ # read block map\r
+ blockentry = BlockMapEntry.Read(fd)\r
+ self._blockentries.append(blockentry)\r
+ while (blockentry.GetNumberBlocks() != 0 and blockentry.GetLength() != 0):\r
+ self._blockentries.append(blockentry)\r
+ blockentry = BlockMapEntry.Read(fd)\r
+\r
+\r
+ if self._fvheader.GetSize() + (len(self._blockentries)) * 8 != \\r
+ self._fvheader.GetHeaderLength():\r
+ raise EFIBinaryError("Volume Header length not consistent with block map!")\r
+\r
+ index = align(fd.tell(), 8)\r
+ count = 0\r
+ while ((index + EfiFfs.FFS_HEADER_SIZE) < self._size):\r
+ ffs = EfiFfs.Read(fd, self)\r
+ if not isValidGuid(ffs.GetNameGuid()):\r
+ break\r
+ self._ffs.append(ffs)\r
+ count += 1\r
+ index = align(fd.tell(), 8)\r
+\r
+ fd.seek(self._offset)\r
+ self._raw.fromfile(fd, self.GetHeader().GetFvLength())\r
+\r
+ def GetFfs(self):\r
+ return self._ffs\r
+\r
+ def GetHeader(self):\r
+ return self._fvheader\r
+\r
+ def GetBlockEntries(self):\r
+ return self._blockentries\r
+\r
+ def GetHeaderRawData(self):\r
+ ret = []\r
+ ret += self._fvheader.GetRawData()\r
+ for block in self._blockentries:\r
+ ret += block.GetRawData()\r
+ return ret\r
+\r
+ def GetOffset(self):\r
+ return 0\r
+\r
+ def GetRawData(self):\r
+ return self._raw.tolist()\r
+\r
+class BinaryItem(object):\r
+ def __init__(self, parent=None):\r
+ self._size = 0\r
+ self._arr = array.array('B')\r
+ self._parent = parent\r
+\r
+ @classmethod\r
+ def Read(cls, fd, parent=None):\r
+ item = cls(parent)\r
+ item.fromfile(fd)\r
+ return item\r
+\r
+ def Load(self, fd):\r
+ self.fromfile(fd)\r
+\r
+ def GetSize(self):\r
+ """should be implemented by inherited class"""\r
+\r
+ def fromfile(self, fd):\r
+ self._arr.fromfile(fd, self.GetSize())\r
+\r
+ def GetParent(self):\r
+ return self._parent\r
+\r
+class EfiFirmwareVolumeHeader(BinaryItem):\r
+ def GetSize(self):\r
+ return 56\r
+\r
+ def GetSigunature(self):\r
+ list = self._arr.tolist()\r
+ sig = ''\r
+ for x in list[40:44]:\r
+ sig += chr(x)\r
+ return sig\r
+\r
+ def GetAttribute(self):\r
+ return list2int(self._arr.tolist()[44:48])\r
+\r
+ def GetErasePolarity(self):\r
+ list = self.GetAttrStrings()\r
+ if 'EFI_FVB2_ERASE_POLARITY' in list:\r
+ return True\r
+ return False\r
+\r
+ def GetAttrStrings(self):\r
+ list = []\r
+ value = self.GetAttribute()\r
+ if (value & 0x01) != 0:\r
+ list.append('EFI_FVB2_READ_DISABLED_CAP')\r
+ if (value & 0x02) != 0:\r
+ list.append('EFI_FVB2_READ_ENABLED_CAP')\r
+ if (value & 0x04) != 0:\r
+ list.append('EFI_FVB2_READ_STATUS')\r
+ if (value & 0x08) != 0:\r
+ list.append('EFI_FVB2_WRITE_DISABLED_CAP')\r
+ if (value & 0x10) != 0:\r
+ list.append('EFI_FVB2_WRITE_ENABLED_CAP')\r
+ if (value & 0x20) != 0:\r
+ list.append('EFI_FVB2_WRITE_STATUS')\r
+ if (value & 0x40) != 0:\r
+ list.append('EFI_FVB2_LOCK_CAP')\r
+ if (value & 0x80) != 0:\r
+ list.append('EFI_FVB2_LOCK_STATUS')\r
+ if (value & 0x200) != 0:\r
+ list.append('EFI_FVB2_STICKY_WRITE')\r
+ if (value & 0x400) != 0:\r
+ list.append('EFI_FVB2_MEMORY_MAPPED')\r
+ if (value & 0x800) != 0:\r
+ list.append('EFI_FVB2_ERASE_POLARITY')\r
+ if (value & 0x1000) != 0:\r
+ list.append('EFI_FVB2_READ_LOCK_CAP')\r
+ if (value & 0x00002000) != 0:\r
+ list.append('EFI_FVB2_READ_LOCK_STATUS')\r
+ if (value & 0x00004000) != 0:\r
+ list.append('EFI_FVB2_WRITE_LOCK_CAP')\r
+ if (value & 0x00008000) != 0:\r
+ list.append('EFI_FVB2_WRITE_LOCK_STATUS')\r
+\r
+ if (value == 0):\r
+ list.append('EFI_FVB2_ALIGNMENT_1')\r
+ if (value & 0x001F0000) == 0x00010000:\r
+ list.append('EFI_FVB2_ALIGNMENT_2')\r
+ if (value & 0x001F0000) == 0x00020000:\r
+ list.append('EFI_FVB2_ALIGNMENT_4')\r
+ if (value & 0x001F0000) == 0x00030000:\r
+ list.append('EFI_FVB2_ALIGNMENT_8')\r
+ if (value & 0x001F0000) == 0x00040000:\r
+ list.append('EFI_FVB2_ALIGNMENT_16')\r
+ if (value & 0x001F0000) == 0x00050000:\r
+ list.append('EFI_FVB2_ALIGNMENT_32')\r
+ if (value & 0x001F0000) == 0x00060000:\r
+ list.append('EFI_FVB2_ALIGNMENT_64')\r
+ if (value & 0x001F0000) == 0x00070000:\r
+ list.append('EFI_FVB2_ALIGNMENT_128')\r
+ if (value & 0x001F0000) == 0x00080000:\r
+ list.append('EFI_FVB2_ALIGNMENT_256')\r
+ if (value & 0x001F0000) == 0x00090000:\r
+ list.append('EFI_FVB2_ALIGNMENT_512')\r
+ if (value & 0x001F0000) == 0x000A0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_1K')\r
+ if (value & 0x001F0000) == 0x000B0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_2K')\r
+ if (value & 0x001F0000) == 0x000C0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_4K')\r
+ if (value & 0x001F0000) == 0x000D0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_8K')\r
+ if (value & 0x001F0000) == 0x000E0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_16K')\r
+ if (value & 0x001F0000) == 0x000F0000:\r
+ list.append('EFI_FVB2_ALIGNMENT_32K')\r
+ if (value & 0x001F0000) == 0x00100000:\r
+ list.append('EFI_FVB2_ALIGNMENT_64K')\r
+ if (value & 0x001F0000) == 0x00110000:\r
+ list.append('EFI_FVB2_ALIGNMENT_128K')\r
+ if (value & 0x001F0000) == 0x00120000:\r
+ list.append('EFI_FVB2_ALIGNMENT_256K')\r
+ if (value & 0x001F0000) == 0x00130000:\r
+ list.append('EFI_FVB2_ALIGNMENT_512K')\r
+\r
+ return list\r
+\r
+ def GetHeaderLength(self):\r
+ return list2int(self._arr.tolist()[48:50])\r
+\r
+ def Dump(self):\r
+ print 'Signature: %s' % self.GetSigunature()\r
+ print 'Attribute: 0x%X' % self.GetAttribute()\r
+ print 'Header Length: 0x%X' % self.GetHeaderLength()\r
+ print 'File system Guid: ', self.GetFileSystemGuid()\r
+ print 'Revision: 0x%X' % self.GetRevision()\r
+ print 'FvLength: 0x%X' % self.GetFvLength()\r
+\r
+ def GetFileSystemGuid(self):\r
+ list = self._arr.tolist()\r
+ return list2guid(list[16:32])\r
+\r
+ def GetRevision(self):\r
+ list = self._arr.tolist()\r
+ return int(list[55])\r
+\r
+ def GetFvLength(self):\r
+ list = self._arr.tolist()\r
+ return list2int(list[32:40])\r
+\r
+ def GetRawData(self):\r
+ return self._arr.tolist()\r
+\r
+class BlockMapEntry(BinaryItem):\r
+ def GetSize(self):\r
+ return 8\r
+\r
+ def GetNumberBlocks(self):\r
+ list = self._arr.tolist()\r
+ return list2int(list[0:4])\r
+\r
+ def GetLength(self):\r
+ list = self._arr.tolist()\r
+ return list2int(list[4:8])\r
+\r
+ def GetRawData(self):\r
+ return self._arr.tolist()\r
+\r
+ def __str__(self):\r
+ return '[BlockEntry] Number = 0x%X, length=0x%X' % (self.GetNumberBlocks(), self.GetLength())\r
+\r
+class EfiFfs(object):\r
+ FFS_HEADER_SIZE = 24\r
+\r
+ def __init__(self, parent=None):\r
+ self._header = None\r
+\r
+ # following field is for FFS in FV file.\r
+ self._parent = parent\r
+ self._offset = 0\r
+ self._sections = []\r
+\r
+ def Load(self, fd):\r
+ self._offset = align(fd.tell(), 8)\r
+\r
+ self._header = EfiFfsHeader.Read(fd, self)\r
+\r
+ if not isValidGuid(self.GetNameGuid()):\r
+ return\r
+\r
+ index = self._offset\r
+ fileend = self._offset + self.GetSize()\r
+ while (index + EfiSection.EFI_SECTION_HEADER_SIZE < fileend):\r
+ section = EfiSection(self)\r
+ section.Load(fd)\r
+ if section.GetSize() == 0 and section.GetHeader().GetType() == 0:\r
+ break\r
+ self._sections.append(section)\r
+ index = fd.tell()\r
+\r
+ # rebase file pointer to next ffs file\r
+ index = self._offset + self._header.GetFfsSize()\r
+ index = align(index, 8)\r
+ fd.seek(index)\r
+\r
+ def GetOffset(self):\r
+ return self._offset\r
+\r
+ def GetSize(self):\r
+ return self._header.GetFfsSize()\r
+\r
+ @classmethod\r
+ def Read(cls, fd, parent=None):\r
+ item = cls(parent)\r
+ item.Load(fd)\r
+ return item\r
+\r
+ def GetNameGuid(self):\r
+ return self._header.GetNameGuid()\r
+\r
+ def DumpContent(self):\r
+ list = self._content.tolist()\r
+ line = []\r
+ count = 0\r
+ for item in list:\r
+ if count < 32:\r
+ line.append('0x%X' % int(item))\r
+ count += 1\r
+ else:\r
+ print ' '.join(line)\r
+ count = 0\r
+ line = []\r
+ line.append('0x%X' % int(item))\r
+ count += 1\r
+\r
+ def GetHeader(self):\r
+ return self._header\r
+\r
+ def GetParent(self):\r
+ return self._parent\r
+\r
+ def GetSections(self):\r
+ return self._sections\r
+\r
+class EfiFfsHeader(BinaryItem):\r
+ ffs_state_map = {0x01:'EFI_FILE_HEADER_CONSTRUCTION',\r
+ 0x02:'EFI_FILE_HEADER_VALID',\r
+ 0x04:'EFI_FILE_DATA_VALID',\r
+ 0x08:'EFI_FILE_MARKED_FOR_UPDATE',\r
+ 0x10:'EFI_FILE_DELETED',\r
+ 0x20:'EFI_FILE_HEADER_INVALID'}\r
+\r
+ def GetSize(self):\r
+ return 24\r
+\r
+ def GetNameGuid(self):\r
+ list = self._arr.tolist()\r
+ return list2guid(list[0:16])\r
+\r
+ def GetType(self):\r
+ list = self._arr.tolist()\r
+ return int(list[18])\r
+\r
+\r
+ def GetTypeString(self):\r
+ value = self.GetType()\r
+ if value == 0x01:\r
+ return 'EFI_FV_FILETYPE_RAW'\r
+ if value == 0x02:\r
+ return 'EFI_FV_FILETYPE_FREEFORM'\r
+ if value == 0x03:\r
+ return 'EFI_FV_FILETYPE_SECURITY_CORE'\r
+ if value == 0x04:\r
+ return 'EFI_FV_FILETYPE_PEI_CORE'\r
+ if value == 0x05:\r
+ return 'EFI_FV_FILETYPE_DXE_CORE'\r
+ if value == 0x06:\r
+ return 'EFI_FV_FILETYPE_PEIM'\r
+ if value == 0x07:\r
+ return 'EFI_FV_FILETYPE_DRIVER'\r
+ if value == 0x08:\r
+ return 'EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER'\r
+ if value == 0x09:\r
+ return 'EFI_FV_FILETYPE_APPLICATION'\r
+ if value == 0x0B:\r
+ return 'EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE'\r
+ if value == 0xc0:\r
+ return 'EFI_FV_FILETYPE_OEM_MIN'\r
+ if value == 0xdf:\r
+ return 'EFI_FV_FILETYPE_OEM_MAX'\r
+ if value == 0xe0:\r
+ return 'EFI_FV_FILETYPE_DEBUG_MIN'\r
+ if value == 0xef:\r
+ return 'EFI_FV_FILETYPE_DEBUG_MAX'\r
+ if value == 0xf0:\r
+ return 'EFI_FV_FILETYPE_FFS_PAD'\r
+ if value == 0xff:\r
+ return 'EFI_FV_FILETYPE_FFS_MAX'\r
+ return 'Unknown FFS Type'\r
+\r
+ def GetAttributes(self):\r
+ list = self._arr.tolist()\r
+ return int(list[19])\r
+\r
+ def GetFfsSize(self):\r
+ list = self._arr.tolist()\r
+ return list2int(list[20:23])\r
+\r
+ def GetState(self):\r
+ list = self._arr.tolist()\r
+ state = int(list[23])\r
+ polarity = self.GetParent().GetParent().GetHeader().GetErasePolarity()\r
+ if polarity:\r
+ state = (~state) & 0xFF\r
+ HighestBit = 0x80\r
+ while (HighestBit != 0) and (HighestBit & state) == 0:\r
+ HighestBit = HighestBit >> 1\r
+ return HighestBit\r
+\r
+ def GetStateString(self):\r
+ state = self.GetState()\r
+ if state in self.ffs_state_map.keys():\r
+ return self.ffs_state_map[state]\r
+ return 'Unknown Ffs State'\r
+\r
+ def Dump(self):\r
+ print "FFS name: ", self.GetNameGuid()\r
+ print "FFS type: ", self.GetType()\r
+ print "FFS attr: 0x%X" % self.GetAttributes()\r
+ print "FFS size: 0x%X" % self.GetFfsSize()\r
+ print "FFS state: 0x%X" % self.GetState()\r
+\r
+ def GetRawData(self):\r
+ return self._arr.tolist()\r
+\r
+\r
+class EfiSection(object):\r
+ EFI_SECTION_HEADER_SIZE = 4\r
+\r
+ def __init__(self, parent=None):\r
+ self._size = 0\r
+ self._parent = parent\r
+ self._offset = 0\r
+ self._contents = array.array('B')\r
+\r
+ def Load(self, fd):\r
+ self._offset = align(fd.tell(), 4)\r
+\r
+ self._header = EfiSectionHeader.Read(fd, self)\r
+\r
+ if self._header.GetTypeString() == "EFI_SECTION_PE32":\r
+ pefile = pe.PEFile(self)\r
+ pefile.Load(fd, self.GetContentSize())\r
+\r
+ fd.seek(self._offset)\r
+ self._contents.fromfile(fd, self.GetContentSize())\r
+\r
+ # rebase file pointer to next section\r
+ index = self._offset + self.GetSize()\r
+ index = align(index, 4)\r
+ fd.seek(index)\r
+\r
+ def GetContentSize(self):\r
+ return self.GetSize() - self.EFI_SECTION_HEADER_SIZE\r
+\r
+ def GetContent(self):\r
+ return self._contents.tolist()\r
+\r
+ def GetSize(self):\r
+ return self._header.GetSectionSize()\r
+\r
+ def GetHeader(self):\r
+ return self._header\r
+\r
+ def GetSectionOffset(self):\r
+ return self._offset + self.EFI_SECTION_HEADER_SIZE\r
+\r
+class EfiSectionHeader(BinaryItem):\r
+ section_type_map = {0x01: 'EFI_SECTION_COMPRESSION',\r
+ 0x02: 'EFI_SECTION_GUID_DEFINED',\r
+ 0x10: 'EFI_SECTION_PE32',\r
+ 0x11: 'EFI_SECTION_PIC',\r
+ 0x12: 'EFI_SECTION_TE',\r
+ 0x13: 'EFI_SECTION_DXE_DEPEX',\r
+ 0x14: 'EFI_SECTION_VERSION',\r
+ 0x15: 'EFI_SECTION_USER_INTERFACE',\r
+ 0x16: 'EFI_SECTION_COMPATIBILITY16',\r
+ 0x17: 'EFI_SECTION_FIRMWARE_VOLUME_IMAGE',\r
+ 0x18: 'EFI_SECTION_FREEFORM_SUBTYPE_GUID',\r
+ 0x19: 'EFI_SECTION_RAW',\r
+ 0x1B: 'EFI_SECTION_PEI_DEPEX'}\r
+ def GetSize(self):\r
+ return 4\r
+\r
+ def GetSectionSize(self):\r
+ list = self._arr.tolist()\r
+ return list2int(list[0:3])\r
+\r
+ def GetType(self):\r
+ list = self._arr.tolist()\r
+ return int(list[3])\r
+\r
+ def GetTypeString(self):\r
+ type = self.GetType()\r
+ if type not in self.section_type_map.keys():\r
+ return 'Unknown Section Type'\r
+ return self.section_type_map[type]\r
+\r
+ def Dump(self):\r
+ print 'size = 0x%X' % self.GetSectionSize()\r
+ print 'type = 0x%X' % self.GetType()\r
+\r
+\r
+\r
+rMapEntry = re.compile('^(\w+)[ \(\w\)]* \(BaseAddress=([0-9a-fA-F]+), EntryPoint=([0-9a-fA-F]+), GUID=([0-9a-fA-F\-]+)')\r
+class EfiFvMapFile(object):\r
+ def __init__(self):\r
+ self._mapentries = {}\r
+\r
+ def Load(self, path):\r
+ if not os.path.exists(path):\r
+ return False\r
+\r
+ try:\r
+ file = open(path, 'r')\r
+ lines = file.readlines()\r
+ file.close()\r
+ except:\r
+ return False\r
+\r
+ for line in lines:\r
+ if line[0] != ' ':\r
+ # new entry\r
+ ret = rMapEntry.match(line)\r
+ if ret != None:\r
+ name = ret.groups()[0]\r
+ baseaddr = int(ret.groups()[1], 16)\r
+ entry = int(ret.groups()[2], 16)\r
+ guidstr = '{' + ret.groups()[3] + '}'\r
+ guid = uuid.UUID(guidstr)\r
+ self._mapentries[guid] = EfiFvMapFileEntry(name, baseaddr, entry, guid)\r
+ return True\r
+\r
+ def GetEntry(self, guid):\r
+ if guid in self._mapentries.keys():\r
+ return self._mapentries[guid]\r
+ return None\r
+\r
+class EfiFvMapFileEntry(object):\r
+ def __init__(self, name, baseaddr, entry, guid):\r
+ self._name = name\r
+ self._baseaddr = baseaddr\r
+ self._entry = entry\r
+ self._guid = guid\r
+\r
+ def GetName(self):\r
+ return self._name\r
+\r
+ def GetBaseAddress(self):\r
+ return self._baseaddr\r
+\r
+ def GetEntryPoint(self):\r
+ return self._entry\r
+\r
+def list2guid(list):\r
+ val1 = list2int(list[0:4])\r
+ val2 = list2int(list[4:6])\r
+ val3 = list2int(list[6:8])\r
+ val4 = 0\r
+ for item in list[8:16]:\r
+ val4 = (val4 << 8) | int(item)\r
+\r
+ val = val1 << 12 * 8 | val2 << 10 * 8 | val3 << 8 * 8 | val4\r
+ guid = uuid.UUID(int=val)\r
+ return guid\r
+\r
+def list2int(list):\r
+ val = 0\r
+ for index in range(len(list) - 1, -1, -1):\r
+ val = (val << 8) | int(list[index])\r
+ return val\r
+\r
+def align(value, alignment):\r
+ return (value + ((alignment - value) & (alignment - 1)))\r
+\r
+gInvalidGuid = uuid.UUID(int=0xffffffffffffffffffffffffffffffff)\r
+def isValidGuid(guid):\r
+ if guid == gInvalidGuid:\r
+ return False\r
+ return True\r