Add SplitFspBin.py

Copied from edk2/IntelFsp2Pkg/Tools/SplitFspBin.py
This copy is intended to provide convinence.
Now one does not need to clone edk2 just for this file.
No delta between this copy and upstream is intended.
Please submit patches for this file to tianocore.

Signed-off-by: Nate DeSimone <nathaniel.l.desimone@intel.com>
This commit is contained in:
Nate DeSimone 2018-09-27 10:49:26 -07:00
parent 0c80d1d172
commit 162719b6cb
1 changed files with 853 additions and 0 deletions

853
Tools/SplitFspBin.py Normal file
View File

@ -0,0 +1,853 @@
## @ FspTool.py
#
# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials are licensed and made available under
# the terms and conditions of the BSD License that 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 os
import sys
import uuid
import copy
import struct
import argparse
from ctypes import *
"""
This utility supports some operations for Intel FSP 2.0 image.
It supports:
- Display FSP 2.0 information header
- Split FSP 2.0 image into individual FSP-T/M/S/O component
- Rebase FSP 2.0 components to a different base address
- Generate FSP mapping C header file
"""
CopyRightHeaderFile = """/*
*
* Automatically generated file; DO NOT EDIT.
* FSP mapping file
*
*/
"""
class c_uint24(Structure):
"""Little-Endian 24-bit Unsigned Integer"""
_pack_ = 1
_fields_ = [('Data', (c_uint8 * 3))]
def __init__(self, val=0):
self.set_value(val)
def __str__(self, indent=0):
return '0x%.6x' % self.value
def __int__(self):
return self.get_value()
def set_value(self, val):
self.Data[0:3] = Val2Bytes(val, 3)
def get_value(self):
return Bytes2Val(self.Data[0:3])
value = property(get_value, set_value)
class EFI_FIRMWARE_VOLUME_HEADER(Structure):
_fields_ = [
('ZeroVector', ARRAY(c_uint8, 16)),
('FileSystemGuid', ARRAY(c_uint8, 16)),
('FvLength', c_uint64),
('Signature', ARRAY(c_char, 4)),
('Attributes', c_uint32),
('HeaderLength', c_uint16),
('Checksum', c_uint16),
('ExtHeaderOffset', c_uint16),
('Reserved', c_uint8),
('Revision', c_uint8)
]
class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):
_fields_ = [
('FvName', ARRAY(c_uint8, 16)),
('ExtHeaderSize', c_uint32)
]
class EFI_FFS_INTEGRITY_CHECK(Structure):
_fields_ = [
('Header', c_uint8),
('File', c_uint8)
]
class EFI_FFS_FILE_HEADER(Structure):
_fields_ = [
('Name', ARRAY(c_uint8, 16)),
('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),
('Type', c_uint8),
('Attributes', c_uint8),
('Size', c_uint24),
('State', c_uint8)
]
class EFI_COMMON_SECTION_HEADER(Structure):
_fields_ = [
('Size', c_uint24),
('Type', c_uint8)
]
class FSP_COMMON_HEADER(Structure):
_fields_ = [
('Signature', ARRAY(c_char, 4)),
('HeaderLength', c_uint32)
]
class FSP_INFORMATION_HEADER(Structure):
_fields_ = [
('Signature', ARRAY(c_char, 4)),
('HeaderLength', c_uint32),
('Reserved1', c_uint16),
('SpecVersion', c_uint8),
('HeaderRevision', c_uint8),
('ImageRevision', c_uint32),
('ImageId', ARRAY(c_char, 8)),
('ImageSize', c_uint32),
('ImageBase', c_uint32),
('ImageAttribute', c_uint16),
('ComponentAttribute', c_uint16),
('CfgRegionOffset', c_uint32),
('CfgRegionSize', c_uint32),
('Reserved2', c_uint32),
('TempRamInitEntryOffset', c_uint32),
('Reserved3', c_uint32),
('NotifyPhaseEntryOffset', c_uint32),
('FspMemoryInitEntryOffset', c_uint32),
('TempRamExitEntryOffset', c_uint32),
('FspSiliconInitEntryOffset', c_uint32)
]
class FSP_PATCH_TABLE(Structure):
_fields_ = [
('Signature', ARRAY(c_char, 4)),
('HeaderLength', c_uint16),
('HeaderRevision', c_uint8),
('Reserved', c_uint8),
('PatchEntryNum', c_uint32)
]
class EFI_IMAGE_DATA_DIRECTORY(Structure):
_fields_ = [
('VirtualAddress', c_uint32),
('Size', c_uint32)
]
class EFI_TE_IMAGE_HEADER(Structure):
_fields_ = [
('Signature', ARRAY(c_char, 2)),
('Machine', c_uint16),
('NumberOfSections', c_uint8),
('Subsystem', c_uint8),
('StrippedSize', c_uint16),
('AddressOfEntryPoint', c_uint32),
('BaseOfCode', c_uint32),
('ImageBase', c_uint64),
('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY),
('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY)
]
class EFI_IMAGE_DOS_HEADER(Structure):
_fields_ = [
('e_magic', c_uint16),
('e_cblp', c_uint16),
('e_cp', c_uint16),
('e_crlc', c_uint16),
('e_cparhdr', c_uint16),
('e_minalloc', c_uint16),
('e_maxalloc', c_uint16),
('e_ss', c_uint16),
('e_sp', c_uint16),
('e_csum', c_uint16),
('e_ip', c_uint16),
('e_cs', c_uint16),
('e_lfarlc', c_uint16),
('e_ovno', c_uint16),
('e_res', ARRAY(c_uint16, 4)),
('e_oemid', c_uint16),
('e_oeminfo', c_uint16),
('e_res2', ARRAY(c_uint16, 10)),
('e_lfanew', c_uint16)
]
class EFI_IMAGE_FILE_HEADER(Structure):
_fields_ = [
('Machine', c_uint16),
('NumberOfSections', c_uint16),
('TimeDateStamp', c_uint32),
('PointerToSymbolTable', c_uint32),
('NumberOfSymbols', c_uint32),
('SizeOfOptionalHeader', c_uint16),
('Characteristics', c_uint16)
]
class PE_RELOC_BLOCK_HEADER(Structure):
_fields_ = [
('PageRVA', c_uint32),
('BlockSize', c_uint32)
]
class EFI_IMAGE_OPTIONAL_HEADER32(Structure):
_fields_ = [
('Magic', c_uint16),
('MajorLinkerVersion', c_uint8),
('MinorLinkerVersion', c_uint8),
('SizeOfCode', c_uint32),
('SizeOfInitializedData', c_uint32),
('SizeOfUninitializedData', c_uint32),
('AddressOfEntryPoint', c_uint32),
('BaseOfCode', c_uint32),
('BaseOfData', c_uint32),
('ImageBase', c_uint32),
('SectionAlignment', c_uint32),
('FileAlignment', c_uint32),
('MajorOperatingSystemVersion', c_uint16),
('MinorOperatingSystemVersion', c_uint16),
('MajorImageVersion', c_uint16),
('MinorImageVersion', c_uint16),
('MajorSubsystemVersion', c_uint16),
('MinorSubsystemVersion', c_uint16),
('Win32VersionValue', c_uint32),
('SizeOfImage', c_uint32),
('SizeOfHeaders', c_uint32),
('CheckSum' , c_uint32),
('Subsystem', c_uint16),
('DllCharacteristics', c_uint16),
('SizeOfStackReserve', c_uint32),
('SizeOfStackCommit' , c_uint32),
('SizeOfHeapReserve', c_uint32),
('SizeOfHeapCommit' , c_uint32),
('LoaderFlags' , c_uint32),
('NumberOfRvaAndSizes', c_uint32),
('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
]
class EFI_IMAGE_OPTIONAL_HEADER32_PLUS(Structure):
_fields_ = [
('Magic', c_uint16),
('MajorLinkerVersion', c_uint8),
('MinorLinkerVersion', c_uint8),
('SizeOfCode', c_uint32),
('SizeOfInitializedData', c_uint32),
('SizeOfUninitializedData', c_uint32),
('AddressOfEntryPoint', c_uint32),
('BaseOfCode', c_uint32),
('ImageBase', c_uint64),
('SectionAlignment', c_uint32),
('FileAlignment', c_uint32),
('MajorOperatingSystemVersion', c_uint16),
('MinorOperatingSystemVersion', c_uint16),
('MajorImageVersion', c_uint16),
('MinorImageVersion', c_uint16),
('MajorSubsystemVersion', c_uint16),
('MinorSubsystemVersion', c_uint16),
('Win32VersionValue', c_uint32),
('SizeOfImage', c_uint32),
('SizeOfHeaders', c_uint32),
('CheckSum' , c_uint32),
('Subsystem', c_uint16),
('DllCharacteristics', c_uint16),
('SizeOfStackReserve', c_uint64),
('SizeOfStackCommit' , c_uint64),
('SizeOfHeapReserve', c_uint64),
('SizeOfHeapCommit' , c_uint64),
('LoaderFlags' , c_uint32),
('NumberOfRvaAndSizes', c_uint32),
('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16))
]
class EFI_IMAGE_OPTIONAL_HEADER(Union):
_fields_ = [
('PeOptHdr', EFI_IMAGE_OPTIONAL_HEADER32),
('PePlusOptHdr', EFI_IMAGE_OPTIONAL_HEADER32_PLUS)
]
class EFI_IMAGE_NT_HEADERS32(Structure):
_fields_ = [
('Signature', c_uint32),
('FileHeader', EFI_IMAGE_FILE_HEADER),
('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER)
]
class EFI_IMAGE_DIRECTORY_ENTRY:
EXPORT = 0
IMPORT = 1
RESOURCE = 2
EXCEPTION = 3
SECURITY = 4
BASERELOC = 5
DEBUG = 6
COPYRIGHT = 7
GLOBALPTR = 8
TLS = 9
LOAD_CONFIG = 10
class EFI_FV_FILETYPE:
ALL = 0x00
RAW = 0x01
FREEFORM = 0x02
SECURITY_CORE = 0x03
PEI_CORE = 0x04
DXE_CORE = 0x05
PEIM = 0x06
DRIVER = 0x07
COMBINED_PEIM_DRIVER = 0x08
APPLICATION = 0x09
SMM = 0x0a
FIRMWARE_VOLUME_IMAGE = 0x0b
COMBINED_SMM_DXE = 0x0c
SMM_CORE = 0x0d
OEM_MIN = 0xc0
OEM_MAX = 0xdf
DEBUG_MIN = 0xe0
DEBUG_MAX = 0xef
FFS_MIN = 0xf0
FFS_MAX = 0xff
FFS_PAD = 0xf0
class EFI_SECTION_TYPE:
"""Enumeration of all valid firmware file section types."""
ALL = 0x00
COMPRESSION = 0x01
GUID_DEFINED = 0x02
DISPOSABLE = 0x03
PE32 = 0x10
PIC = 0x11
TE = 0x12
DXE_DEPEX = 0x13
VERSION = 0x14
USER_INTERFACE = 0x15
COMPATIBILITY16 = 0x16
FIRMWARE_VOLUME_IMAGE = 0x17
FREEFORM_SUBTYPE_GUID = 0x18
RAW = 0x19
PEI_DEPEX = 0x1b
SMM_DEPEX = 0x1c
def AlignPtr (offset, alignment = 8):
return (offset + alignment - 1) & ~(alignment - 1)
def Bytes2Val (bytes):
return reduce(lambda x,y: (x<<8)|y, bytes[::-1] )
def Val2Bytes (value, blen):
return [(value>>(i*8) & 0xff) for i in range(blen)]
def OutputStruct (obj, indent = 0, plen = 0):
if indent:
body = ''
else:
body = (' ' * indent + '<%s>:\n') % obj.__class__.__name__
if plen == 0:
plen = sizeof(obj)
max_key_len = 26
pstr = (' ' * (indent + 1) + '{0:<%d} = {1}\n') % max_key_len
for field in obj._fields_:
key = field[0]
val = getattr(obj, key)
rep = ''
if not isinstance(val, c_uint24) and isinstance(val, Structure):
body += pstr.format(key, val.__class__.__name__)
body += OutputStruct (val, indent + 1)
plen -= sizeof(val)
else:
if type(val) is str:
rep = "0x%X ('%s')" % (Bytes2Val(bytearray(val)), val)
elif type(val) in (int, long):
rep = '0x%X' % val
elif isinstance(val, c_uint24):
rep = '0x%X' % val.get_value()
elif 'c_ubyte_Array' in str(type(val)):
if sizeof(val) == 16:
rep = str(uuid.UUID(bytes = str(bytearray(val)))).upper()
else:
res = ['0x%02X'%i for i in bytearray(val)]
rep = '[%s]' % (','.join(res))
else:
rep = str(val)
plen -= sizeof(field[1])
body += pstr.format(key, rep)
if plen <= 0:
break
return body
class Section:
def __init__(self, offset, secdata):
self.SecHdr = EFI_COMMON_SECTION_HEADER.from_buffer (secdata, 0)
self.SecData = secdata[0:int(self.SecHdr.Size)]
self.Offset = offset
class FirmwareFile:
def __init__(self, offset, filedata):
self.FfsHdr = EFI_FFS_FILE_HEADER.from_buffer (filedata, 0)
self.FfsData = filedata[0:int(self.FfsHdr.Size)]
self.Offset = offset
self.SecList = []
def ParseFfs(self):
ffssize = len(self.FfsData)
offset = sizeof(self.FfsHdr)
if self.FfsHdr.Name != '\xff' * 16:
while offset < ffssize:
sechdr = EFI_COMMON_SECTION_HEADER.from_buffer (self.FfsData, offset)
sec = Section (offset, self.FfsData[offset:offset + int(sechdr.Size)])
self.SecList.append(sec)
offset += int(sechdr.Size)
offset = AlignPtr(offset, 4)
class FirmwareVolume:
def __init__(self, offset, fvdata):
self.FvHdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (fvdata, 0)
self.FvData = fvdata[0 : self.FvHdr.FvLength]
self.Offset = offset
if self.FvHdr.ExtHeaderOffset > 0:
self.FvExtHdr = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer (self.FvData, self.FvHdr.ExtHeaderOffset)
else:
self.FvExtHdr = None
self.FfsList = []
def ParseFv(self):
fvsize = len(self.FvData)
if self.FvExtHdr:
offset = self.FvHdr.ExtHeaderOffset + self.FvExtHdr.ExtHeaderSize
else:
offset = self.FvHdr.HeaderLength
offset = AlignPtr(offset)
while offset < fvsize:
ffshdr = EFI_FFS_FILE_HEADER.from_buffer (self.FvData, offset)
if (ffshdr.Name == '\xff' * 16) and (int(ffshdr.Size) == 0xFFFFFF):
offset = fvsize
else:
ffs = FirmwareFile (offset, self.FvData[offset:offset + int(ffshdr.Size)])
ffs.ParseFfs()
self.FfsList.append(ffs)
offset += int(ffshdr.Size)
offset = AlignPtr(offset)
class FspImage:
def __init__(self, offset, fih, fihoff, patch):
self.Fih = fih
self.FihOffset = fihoff
self.Offset = offset
self.FvIdxList = []
self.Type = "XTMSXXXXOXXXXXXX"[(fih.ComponentAttribute >> 12) & 0x0F]
self.PatchList = patch
self.PatchList.append(fihoff + 0x1C)
def AppendFv(self, FvIdx):
self.FvIdxList.append(FvIdx)
def Patch(self, delta, fdbin):
count = 0
applied = 0
for idx, patch in enumerate(self.PatchList):
ptype = (patch>>24) & 0x0F
if ptype not in [0x00, 0x0F]:
raise Exception('ERROR: Invalid patch type %d !' % ptype)
if patch & 0x80000000:
patch = self.Fih.ImageSize - (0x1000000 - (patch & 0xFFFFFF))
else:
patch = patch & 0xFFFFFF
if (patch < self.Fih.ImageSize) and (patch + sizeof(c_uint32) <= self.Fih.ImageSize):
offset = patch + self.Offset
value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)])
value += delta
fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32))
applied += 1
count += 1
# Don't count the FSP base address patch entry appended at the end
if count != 0:
count -= 1
applied -= 1
return (count, applied)
class FirmwareDevice:
def __init__(self, offset, fdfile):
self.FvList = []
self.FspList = []
self.FdFile = fdfile
self.Offset = 0
hfsp = open (self.FdFile, 'rb')
self.FdData = bytearray(hfsp.read())
hfsp.close()
def ParseFd(self):
offset = 0
fdsize = len(self.FdData)
self.FvList = []
while offset < fdsize:
fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FdData, offset)
if '_FVH' != fvh.Signature:
raise Exception("ERROR: Invalid FV header !")
fv = FirmwareVolume (offset, self.FdData[offset:offset + fvh.FvLength])
fv.ParseFv ()
self.FvList.append(fv)
offset += fv.FvHdr.FvLength
def CheckFsp (self):
if len(self.FspList) == 0:
return
fih = None
for fsp in self.FspList:
if fsp.Fih.HeaderRevision < 3:
raise Exception("ERROR: FSP 1.x is not supported by this tool !")
if not fih:
fih = fsp.Fih
else:
newfih = fsp.Fih
if (newfih.ImageId != fih.ImageId) or (newfih.ImageRevision != fih.ImageRevision):
raise Exception("ERROR: Inconsistent FSP ImageId or ImageRevision detected !")
def ParseFsp(self):
flen = 0
for idx, fv in enumerate(self.FvList):
# Check if this FV contains FSP header
if flen == 0:
if len(fv.FfsList) == 0:
continue
ffs = fv.FfsList[0]
if len(ffs.SecList) == 0:
continue
sec = ffs.SecList[0]
if sec.SecHdr.Type != EFI_SECTION_TYPE.RAW:
continue
fihoffset = ffs.Offset + sec.Offset + sizeof(sec.SecHdr)
fspoffset = fv.Offset
offset = fspoffset + fihoffset
fih = FSP_INFORMATION_HEADER.from_buffer (self.FdData, offset)
if 'FSPH' != fih.Signature:
continue
offset += fih.HeaderLength
offset = AlignPtr(offset, 4)
plist = []
while True:
fch = FSP_COMMON_HEADER.from_buffer (self.FdData, offset)
if 'FSPP' != fch.Signature:
offset += fch.HeaderLength
offset = AlignPtr(offset, 4)
else:
fspp = FSP_PATCH_TABLE.from_buffer (self.FdData, offset)
offset += sizeof(fspp)
pdata = (c_uint32 * fspp.PatchEntryNum).from_buffer(self.FdData, offset)
plist = list(pdata)
break
fsp = FspImage (fspoffset, fih, fihoffset, plist)
fsp.AppendFv (idx)
self.FspList.append(fsp)
flen = fsp.Fih.ImageSize - fv.FvHdr.FvLength
else:
fsp.AppendFv (idx)
flen -= fv.FvHdr.FvLength
if flen < 0:
raise Exception("ERROR: Incorrect FV size in image !")
self.CheckFsp ()
class PeTeImage:
def __init__(self, offset, data):
self.Offset = offset
tehdr = EFI_TE_IMAGE_HEADER.from_buffer (data, 0)
if tehdr.Signature == 'VZ': # TE image
self.TeHdr = tehdr
elif tehdr.Signature == 'MZ': # PE image
self.TeHdr = None
self.DosHdr = EFI_IMAGE_DOS_HEADER.from_buffer (data, 0)
self.PeHdr = EFI_IMAGE_NT_HEADERS32.from_buffer (data, self.DosHdr.e_lfanew)
if self.PeHdr.Signature != 0x4550:
raise Exception("ERROR: Invalid PE32 header !")
if self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b: # PE32 image
if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32.DataDirectory.offset:
raise Exception("ERROR: Unsupported PE32 image !")
if self.PeHdr.OptionalHeader.PeOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC:
raise Exception("ERROR: No relocation information available !")
elif self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x20b: # PE32+ image
if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32_PLUS.DataDirectory.offset:
raise Exception("ERROR: Unsupported PE32+ image !")
if self.PeHdr.OptionalHeader.PePlusOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC:
raise Exception("ERROR: No relocation information available !")
else:
raise Exception("ERROR: Invalid PE32 optional header !")
self.Offset = offset
self.Data = data
self.RelocList = []
def IsTeImage(self):
return self.TeHdr is not None
def ParseReloc(self):
if self.IsTeImage():
rsize = self.TeHdr.DataDirectoryBaseReloc.Size
roffset = sizeof(self.TeHdr) - self.TeHdr.StrippedSize + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress
else:
if self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b: # PE32 image
rsize = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size
roffset = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress
if self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x20b: # PE32+ image
rsize = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size
roffset = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress
alignment = 4
offset = roffset
while offset < roffset + rsize:
offset = AlignPtr(offset, 4)
blkhdr = PE_RELOC_BLOCK_HEADER.from_buffer(self.Data, offset)
offset += sizeof(blkhdr)
# Read relocation type,offset pairs
rlen = blkhdr.BlockSize - sizeof(PE_RELOC_BLOCK_HEADER)
rnum = rlen/sizeof(c_uint16)
rdata = (c_uint16 * rnum).from_buffer(self.Data, offset)
for each in rdata:
roff = each & 0xfff
rtype = each >> 12
if rtype == 0: # IMAGE_REL_BASED_ABSOLUTE:
continue
if ((rtype != 3) and (rtype != 10)): # IMAGE_REL_BASED_HIGHLOW and IMAGE_REL_BASED_DIR64
raise Exception("ERROR: Unsupported relocation type %d!" % rtype)
# Calculate the offset of the relocation
aoff = blkhdr.PageRVA + roff
if self.IsTeImage():
aoff += sizeof(self.TeHdr) - self.TeHdr.StrippedSize
self.RelocList.append((rtype, aoff))
offset += sizeof(rdata)
def Rebase(self, delta, fdbin):
count = 0
if delta == 0:
return count
for (rtype, roff) in self.RelocList:
if rtype == 3: # IMAGE_REL_BASED_HIGHLOW
offset = roff + self.Offset
value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)])
value += delta
fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32))
count += 1
elif rtype == 10: # IMAGE_REL_BASED_DIR64
offset = roff + self.Offset
value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint64)])
value += delta
fdbin[offset:offset+sizeof(c_uint64)] = Val2Bytes(value, sizeof(c_uint64))
count += 1
else:
raise Exception('ERROR: Unknown relocation type %d !' % rtype)
if self.IsTeImage():
offset = self.Offset + EFI_TE_IMAGE_HEADER.ImageBase.offset
size = EFI_TE_IMAGE_HEADER.ImageBase.size
else:
offset = self.Offset + self.DosHdr.e_lfanew
offset += EFI_IMAGE_NT_HEADERS32.OptionalHeader.offset
offset += EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.offset
size = EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.size
value = Bytes2Val(fdbin[offset:offset+size]) + delta
fdbin[offset:offset+size] = Val2Bytes(value, size)
return count
def ShowFspInfo (fspfile):
fd = FirmwareDevice(0, fspfile)
fd.ParseFd ()
fd.ParseFsp ()
print ("\nFound the following %d Firmware Volumes in FSP binary:" % (len(fd.FvList)))
for idx, fv in enumerate(fd.FvList):
name = fv.FvExtHdr.FvName
if not name:
name = '\xff' * 16
else:
name = str(bytearray(name))
guid = uuid.UUID(bytes = name)
print ("FV%d:" % idx)
print (" GUID : %s" % str(guid).upper())
print (" Offset : 0x%08X" % fv.Offset)
print (" Length : 0x%08X" % fv.FvHdr.FvLength)
print ("\n")
for fsp in fd.FspList:
fvlist = map(lambda x : 'FV%d' % x, fsp.FvIdxList)
print ("FSP_%s contains %s" % (fsp.Type, ','.join(fvlist)))
print ("%s" % (OutputStruct(fsp.Fih, 0, fsp.Fih.HeaderLength)))
def GenFspHdr (fspfile, outdir, hfile):
fd = FirmwareDevice(0, fspfile)
fd.ParseFd ()
fd.ParseFsp ()
if not hfile:
hfile = os.path.splitext(os.path.basename(fspfile))[0] + '.h'
fspname, ext = os.path.splitext(os.path.basename(hfile))
filename = os.path.join(outdir, fspname + ext)
hfsp = open(filename, 'w')
hfsp.write ('%s\n\n' % CopyRightHeaderFile)
firstfv = True
for fsp in fd.FspList:
fih = fsp.Fih
if firstfv:
hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), fih.ImageId))
hfsp.write("#define FSP_IMAGE_REV 0x%08X \n\n" % fih.ImageRevision)
firstfv = False
fv = fd.FvList[fsp.FvIdxList[0]]
hfsp.write ('#define FSP%s_BASE 0x%08X\n' % (fsp.Type, fih.ImageBase))
hfsp.write ('#define FSP%s_OFFSET 0x%08X\n' % (fsp.Type, fv.Offset))
hfsp.write ('#define FSP%s_LENGTH 0x%08X\n\n' % (fsp.Type, fih.ImageSize))
hfsp.close()
def SplitFspBin (fspfile, outdir, nametemplate):
fd = FirmwareDevice(0, fspfile)
fd.ParseFd ()
fd.ParseFsp ()
for fsp in fd.FspList:
ftype = fsp.Type
if not nametemplate:
nametemplate = fspfile
fspname, ext = os.path.splitext(os.path.basename(nametemplate))
filename = os.path.join(outdir, fspname + '_' + fsp.Type + ext)
hfsp = open(filename, 'wb')
print ("Create FSP component file '%s'" % filename)
for fvidx in fsp.FvIdxList:
fv = fd.FvList[fvidx]
hfsp.write(fv.FvData)
hfsp.close()
def RebaseFspBin (FspBinary, FspComponent, FspBase, OutputDir, OutputFile):
fd = FirmwareDevice(0, FspBinary)
fd.ParseFd ()
fd.ParseFsp ()
numcomp = len(FspComponent)
baselist = FspBase
if numcomp != len(baselist):
print "ERROR: Required number of base does not match number of FSP component !"
return
newfspbin = fd.FdData[:]
for idx, fspcomp in enumerate(FspComponent):
found = False
for fsp in fd.FspList:
ftype = fsp.Type.lower()
if ftype == fspcomp:
found = True
break
if not found:
print "ERROR: Could not find FSP_%c component to rebase !" % fspcomp.upper()
return
fspbase = baselist[idx]
if fspbase.startswith('0x'):
newbase = int(fspbase, 16)
else:
newbase = int(fspbase)
oldbase = fsp.Fih.ImageBase
delta = newbase - oldbase
print "Rebase FSP-%c from 0x%08X to 0x%08X:" % (ftype.upper(),oldbase,newbase)
imglist = []
for fvidx in fsp.FvIdxList:
fv = fd.FvList[fvidx]
for ffs in fv.FfsList:
for sec in ffs.SecList:
if sec.SecHdr.Type in [EFI_SECTION_TYPE.TE, EFI_SECTION_TYPE.PE32]: # TE or PE32
offset = fd.Offset + fv.Offset + ffs.Offset + sec.Offset + sizeof(sec.SecHdr)
imglist.append ((offset, len(sec.SecData) - sizeof(sec.SecHdr)))
fcount = 0
pcount = 0
for (offset, length) in imglist:
img = PeTeImage(offset, fd.FdData[offset:offset + length])
img.ParseReloc()
pcount += img.Rebase(delta, newfspbin)
fcount += 1
print " Patched %d entries in %d TE/PE32 images." % (pcount, fcount)
(count, applied) = fsp.Patch(delta, newfspbin)
print " Patched %d entries using FSP patch table." % applied
if count != applied:
print " %d invalid entries are ignored !" % (count - applied)
if OutputFile == '':
filename = os.path.basename(FspBinary)
base, ext = os.path.splitext(filename)
OutputFile = base + "_%08X" % newbase + ext
fspname, ext = os.path.splitext(os.path.basename(OutputFile))
filename = os.path.join(OutputDir, fspname + ext)
fd = open(filename, "wb")
fd.write(newfspbin)
fd.close()
def main ():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='commands')
parser_rebase = subparsers.add_parser('rebase', help='rebase a FSP into a new base address')
parser_rebase.set_defaults(which='rebase')
parser_rebase.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
parser_rebase.add_argument('-c', '--fspcomp', choices=['t','m','s','o'], nargs='+', dest='FspComponent', type=str, help='FSP component to rebase', default = "['t']", required = True)
parser_rebase.add_argument('-b', '--newbase', dest='FspBase', nargs='+', type=str, help='Rebased FSP binary file name', default = '', required = True)
parser_rebase.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.')
parser_rebase.add_argument('-n', '--outfile', dest='OutputFile', type=str, help='Rebased FSP binary file name', default = '')
parser_split = subparsers.add_parser('split', help='split a FSP into multiple components')
parser_split.set_defaults(which='split')
parser_split.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
parser_split.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.')
parser_split.add_argument('-n', '--nametpl', dest='NameTemplate', type=str, help='Output name template', default = '')
parser_genhdr = subparsers.add_parser('genhdr', help='generate a header file for FSP binary')
parser_genhdr.set_defaults(which='genhdr')
parser_genhdr.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
parser_genhdr.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.')
parser_genhdr.add_argument('-n', '--hfile', dest='HFileName', type=str, help='Output header file name', default = '')
parser_info = subparsers.add_parser('info', help='display FSP information')
parser_info.set_defaults(which='info')
parser_info.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)
args = parser.parse_args()
if args.which in ['rebase', 'split', 'genhdr', 'info']:
if not os.path.exists(args.FspBinary):
raise Exception ("ERROR: Could not locate FSP binary file '%s' !" % args.FspBinary)
if hasattr(args, 'OutputDir') and not os.path.exists(args.OutputDir):
raise Exception ("ERROR: Invalid output directory '%s' !" % args.OutputDir)
if args.which == 'rebase':
RebaseFspBin (args.FspBinary, args.FspComponent, args.FspBase, args.OutputDir, args.OutputFile)
elif args.which == 'split':
SplitFspBin (args.FspBinary, args.OutputDir, args.NameTemplate)
elif args.which == 'genhdr':
GenFspHdr (args.FspBinary, args.OutputDir, args.HFileName)
elif args.which == 'info':
ShowFspInfo (args.FspBinary)
else:
pass
return 0
if __name__ == '__main__':
sys.exit(main())