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