audk/IntelFsp2Pkg/Tools/SplitFspBin.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

947 lines
39 KiB
Python
Raw Normal View History

## @ SplitFspBin.py
#
# Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
import os
import sys
import uuid
import copy
import struct
import argparse
from ctypes import *
from functools import reduce
"""
This utility supports some operations for Intel FSP 1.x/2.x image.
It supports:
- Display FSP 1.x/2.x information header
- Split FSP 2.x image into individual FSP-T/M/S/O component
- Rebase FSP 1.x/2.x components to a different base address
- Generate FSP 1.x/2.x 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),
('FspMultiPhaseSiInitEntryOffset', c_uint32),
('ExtendedImageRevision', c_uint16),
('Reserved4', c_uint16),
('FspMultiPhaseMemInitEntryOffset', c_uint32),
('FspSmmInitEntryOffset', 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 IsIntegerType (val):
if sys.version_info[0] < 3:
if type(val) in (int, long):
return True
else:
if type(val) is int:
return True
return False
def IsStrType (val):
if sys.version_info[0] < 3:
if type(val) is str:
return True
else:
if type(val) is bytes:
return True
return False
def HandleNameStr (val):
if sys.version_info[0] < 3:
rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), val)
else:
rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), str (val, 'utf-8'))
return rep
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 IsStrType (val):
rep = HandleNameStr (val)
elif IsIntegerType (val):
if (key == 'ImageRevision'):
FspImageRevisionMajor = ((val >> 24) & 0xFF)
FspImageRevisionMinor = ((val >> 16) & 0xFF)
FspImageRevisionRevision = ((val >> 8) & 0xFF)
FspImageRevisionBuildNumber = (val & 0xFF)
rep = '0x%08X' % val
elif (key == 'ExtendedImageRevision'):
FspImageRevisionRevision |= (val & 0xFF00)
FspImageRevisionBuildNumber |= ((val << 8) & 0xFF00)
rep = "0x%04X ('%02X.%02X.%04X.%04X')" % (val, FspImageRevisionMajor, FspImageRevisionMinor, FspImageRevisionRevision, FspImageRevisionBuildNumber)
elif field[1] == c_uint64:
rep = '0x%016X' % val
elif field[1] == c_uint32:
rep = '0x%08X' % val
elif field[1] == c_uint16:
rep = '0x%04X' % val
elif field[1] == c_uint8:
rep = '0x%02X' % val
else:
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:
if sys.version_info[0] < 3:
rep = str(bytearray(val))
else:
rep = bytes(val)
rep = str(uuid.UUID(bytes_le = rep)).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 - sizeof (EFI_COMMON_SECTION_HEADER)):
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 = []
self.ChildFvList = []
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 - sizeof (EFI_FFS_FILE_HEADER)):
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)])
# check if there is child fv
childfvfound = 0
if (ffs.FfsHdr.Type == EFI_FV_FILETYPE.FIRMWARE_VOLUME_IMAGE):
csoffset = offset + sizeof (EFI_FFS_FILE_HEADER)
csoffset = AlignPtr(csoffset, 4)
# find fv section
while csoffset < (offset + int(ffs.FfsHdr.Size)):
cshdr = EFI_COMMON_SECTION_HEADER.from_buffer (self.FvData, csoffset)
if (cshdr.Type == EFI_SECTION_TYPE.FIRMWARE_VOLUME_IMAGE):
childfvfound = 1
break
else:
# check next section
csoffset += int(cshdr.Size)
csoffset = AlignPtr(csoffset, 4)
if (childfvfound):
childfvoffset = csoffset + sizeof (EFI_COMMON_SECTION_HEADER)
childfvhdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FvData, childfvoffset)
childfv = FirmwareVolume (childfvoffset, self.FvData[childfvoffset:childfvoffset + int(childfvhdr.FvLength)])
childfv.ParseFv ()
self.ChildFvList.append(childfv)
else:
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 = "XTMSIXXXOXXXXXXX"[(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 - sizeof (EFI_FIRMWARE_VOLUME_HEADER)):
fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FdData, offset)
if b'_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 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 b'FSPH' != fih.Signature:
continue
offset += fih.HeaderLength
offset = AlignPtr(offset, 4)
plist = []
while True:
fch = FSP_COMMON_HEADER.from_buffer (self.FdData, offset)
if b'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 == b'VZ': # TE image
self.TeHdr = tehdr
elif tehdr.Signature == b'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:
# Assuming PE32 image type (self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b)
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.PePlusOptHdr.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 = int (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
if self.PeHdr.OptionalHeader.PePlusOptHdr.Magic == 0x20b: # PE32+ image
offset += EFI_IMAGE_OPTIONAL_HEADER32_PLUS.ImageBase.offset
size = EFI_IMAGE_OPTIONAL_HEADER32_PLUS.ImageBase.size
else:
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:
if sys.version_info[0] < 3:
name = str(bytearray(name))
else:
name = bytes(name)
guid = uuid.UUID(bytes_le = 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:
if sys.version_info[0] < 3:
hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), fih.ImageId))
else:
hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), str (fih.ImageId, 'utf-8')))
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:
if fsp.Fih.HeaderRevision < 3:
raise Exception("ERROR: FSP 1.x is not supported by the split command !")
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 GetImageFromFv (fd, parentfvoffset, fv, imglist):
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 + parentfvoffset + fv.Offset + ffs.Offset + sec.Offset + sizeof(sec.SecHdr)
imglist.append ((offset, len(sec.SecData) - sizeof(sec.SecHdr)))
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:
# Is this FSP 1.x single binary?
if fsp.Fih.HeaderRevision < 3:
found = True
ftype = 'X'
break
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]
GetImageFromFv (fd, 0, fv, imglist)
# get image from child fv
for childfv in fv.ChildFvList:
print ("Get image from child fv of fv%d, parent fv offset: 0x%x" % (fvidx, fv.Offset))
GetImageFromFv (fd, fv.Offset, childfv, imglist)
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', dest="which")
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','i'], 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:
parser.print_help()
return 0
if __name__ == '__main__':
sys.exit(main())