mirror of https://github.com/acidanthera/audk.git
364 lines
14 KiB
Python
364 lines
14 KiB
Python
## @ FspTool.py
|
|
#
|
|
# Copyright (c) 2015 - 2016, 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 image.
|
|
It supports:
|
|
- Split a FSP 2.0 compatibale image into individual FSP-T/M/S/C
|
|
and generate the mapping header 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 get_value(self):
|
|
return (
|
|
(self.Data[0] ) +
|
|
(self.Data[1] << 8) +
|
|
(self.Data[2] << 16)
|
|
)
|
|
|
|
def set_value(self, val):
|
|
self.Data[0] = (val ) & 0xff
|
|
self.Data[1] = (val >> 8) & 0xff
|
|
self.Data[2] = (val >> 16) & 0xff
|
|
|
|
value = property(get_value, set_value)
|
|
|
|
class EFI_FIRMWARE_VOLUME_HEADER(Structure):
|
|
_fields_ = [
|
|
('ZeroVector', ARRAY(c_uint8, 16)),
|
|
('FileSystemGuid', ARRAY(c_char, 16)),
|
|
('FvLength', c_uint64),
|
|
('Signature', c_uint32),
|
|
('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_char, 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_char, 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_INFORMATION_HEADER(Structure):
|
|
_fields_ = [
|
|
('Signature', c_uint32),
|
|
('HeaderLength', c_uint32),
|
|
('Reserved1', ARRAY(c_uint8, 3)),
|
|
('HeaderRevision', c_uint8),
|
|
('ImageRevision', c_uint32),
|
|
('ImageId', c_uint64),
|
|
('ImageSize', c_uint32),
|
|
('ImageBase', c_uint32),
|
|
('ImageAttribute', c_uint32),
|
|
('CfgRegionOffset', c_uint32),
|
|
('CfgRegionSize', c_uint32),
|
|
('ApiEntryNum', c_uint32),
|
|
('NemInitEntry', c_uint32),
|
|
('FspInitEntry', c_uint32),
|
|
('NotifyPhaseEntry', c_uint32),
|
|
('FspMemoryInitEntry', c_uint32),
|
|
('TempRamExitEntry', c_uint32),
|
|
('FspSiliconInitEntry', c_uint32)
|
|
]
|
|
|
|
class FspFv:
|
|
HeaderFile = """/*
|
|
*
|
|
* Automatically generated file; DO NOT EDIT.
|
|
* FSP mapping file
|
|
*
|
|
*/
|
|
"""
|
|
FspNameDict = {
|
|
"0" : "-C.Fv",
|
|
"1" : "-T.Fv",
|
|
"2" : "-M.Fv",
|
|
"3" : "-S.Fv",
|
|
}
|
|
|
|
def __init__(self, FvBin):
|
|
self.FspFv = {}
|
|
self.FvList = []
|
|
self.FspBin = FvBin
|
|
hfsp = open (self.FspBin, 'r+b')
|
|
self.FspDat = bytearray(hfsp.read())
|
|
hfsp.close()
|
|
|
|
def OutputStruct (self, obj, indent = 0):
|
|
max_key_len = 20
|
|
pstr = (' ' * indent + '{0:<%d} = {1}\n') % max_key_len
|
|
if indent:
|
|
s = ''
|
|
else:
|
|
s = (' ' * indent + '<%s>:\n') % obj.__class__.__name__
|
|
for field in obj._fields_:
|
|
key = field[0]
|
|
val = getattr(obj, key)
|
|
rep = ''
|
|
|
|
if not isinstance(val, c_uint24) and isinstance(val, Structure):
|
|
s += pstr.format(key, val.__class__.__name__)
|
|
s += self.OutputStruct (val, indent + 1)
|
|
else:
|
|
if type(val) in (int, long):
|
|
rep = hex(val)
|
|
elif isinstance(val, str) and (len(val) == 16):
|
|
rep = str(uuid.UUID(bytes = val))
|
|
elif isinstance(val, c_uint24):
|
|
rep = hex(val.get_value())
|
|
elif 'c_ubyte_Array' in str(type(val)):
|
|
rep = str(list(bytearray(val)))
|
|
else:
|
|
rep = str(val)
|
|
s += pstr.format(key, rep)
|
|
return s
|
|
|
|
def PrintFv (self):
|
|
print ("FV LIST:")
|
|
idx = 0
|
|
for (fvh, fvhe, offset) in self.FvList:
|
|
guid = uuid.UUID(bytes = fvhe.FvName)
|
|
print ("FV%d FV GUID:%s Offset:0x%08X Length:0x%08X" % (idx, str(guid), offset, fvh.FvLength))
|
|
idx = idx + 1
|
|
print ("\nFSP LIST:")
|
|
for fsp in self.FspFv:
|
|
print "FSP%s contains FV%s" % (fsp, str(self.FspFv[fsp][1]))
|
|
print "\nFSP%s Info Header:" % fsp
|
|
fih = self.FspFv[fsp][0]
|
|
|
|
def AlaignPtr (self, offset, alignment = 8):
|
|
return (offset + alignment - 1) & ~(alignment - 1)
|
|
|
|
def GetFspInfoHdr (self, fvh, fvhe, fvoffset):
|
|
if fvhe:
|
|
offset = fvh.ExtHeaderOffset + fvhe.ExtHeaderSize
|
|
else:
|
|
offset = fvh.HeaderLength
|
|
offset = self.AlaignPtr(offset)
|
|
|
|
# Now it should be 1st FFS
|
|
ffs = EFI_FFS_FILE_HEADER.from_buffer (self.FspDat, offset)
|
|
offset += sizeof(ffs)
|
|
offset = self.AlaignPtr(offset)
|
|
|
|
# Now it should be 1st Section
|
|
sec = EFI_COMMON_SECTION_HEADER.from_buffer (self.FspDat, offset)
|
|
offset += sizeof(sec)
|
|
|
|
# Now it should be FSP_INFO_HEADER
|
|
offset += fvoffset
|
|
fih = FSP_INFORMATION_HEADER.from_buffer (self.FspDat, offset)
|
|
if 'FSPH' != bytearray.fromhex('%08X' % fih.Signature)[::-1]:
|
|
return None
|
|
|
|
return fih
|
|
|
|
def GetFvHdr (self, offset):
|
|
fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FspDat, offset)
|
|
if '_FVH' != bytearray.fromhex('%08X' % fvh.Signature)[::-1]:
|
|
return None, None
|
|
if fvh.ExtHeaderOffset > 0:
|
|
offset += fvh.ExtHeaderOffset
|
|
fvhe = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer (self.FspDat, offset)
|
|
else:
|
|
fvhe = None
|
|
return fvh, fvhe
|
|
|
|
def GetFvData(self, idx):
|
|
(fvh, fvhe, offset) = self.FvList[idx]
|
|
return self.FspDat[offset:offset+fvh.FvLength]
|
|
|
|
def CheckFsp (self):
|
|
if len(self.FspFv) == 0:
|
|
return
|
|
|
|
fih = None
|
|
for fv in self.FspFv:
|
|
if not fih:
|
|
fih = self.FspFv[fv][0]
|
|
else:
|
|
newfih = self.FspFv[fv][0]
|
|
if (newfih.ImageId != fih.ImageId) or (newfih.ImageRevision != fih.ImageRevision):
|
|
raise Exception("Inconsistent FSP ImageId or ImageRevision detected !")
|
|
return
|
|
|
|
def WriteFsp(self, dir, name):
|
|
if not name:
|
|
name = self.FspBin
|
|
fspname, ext = os.path.splitext(os.path.basename(name))
|
|
for fv in self.FspFv:
|
|
filename = os.path.join(dir, fspname + fv + ext)
|
|
hfsp = open(filename, 'w+b')
|
|
for fvidx in self.FspFv[fv][1]:
|
|
hfsp.write (self.GetFvData(fvidx))
|
|
hfsp.close()
|
|
|
|
def WriteMap(self, dir, hfile):
|
|
if not hfile:
|
|
hfile = os.path.splitext(os.path.basename(self.FspBin))[0] + '.h'
|
|
fspname, ext = os.path.splitext(os.path.basename(hfile))
|
|
filename = os.path.join(dir, fspname + ext)
|
|
hfsp = open(filename, 'w')
|
|
hfsp.write ('%s\n\n' % self.HeaderFile)
|
|
|
|
firstfv = True
|
|
for fsp in self.FspFv:
|
|
fih = self.FspFv[fsp][0]
|
|
fvs = self.FspFv[fsp][1]
|
|
if firstfv:
|
|
IdStr = str(bytearray.fromhex('%016X' % fih.ImageId)[::-1])
|
|
hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (fih.ImageId, IdStr))
|
|
hfsp.write("#define FSP_IMAGE_REV 0x%08X \n\n" % fih.ImageRevision)
|
|
firstfv = False
|
|
hfsp.write ('#define FSP%s_BASE 0x%08X\n' % (fsp, fih.ImageBase))
|
|
hfsp.write ('#define FSP%s_OFFSET 0x%08X\n' % (fsp, self.FvList[fvs[0]][-1]))
|
|
hfsp.write ('#define FSP%s_LENGTH 0x%08X\n\n' % (fsp, fih.ImageSize))
|
|
hfsp.close()
|
|
|
|
def ParseFsp (self):
|
|
self.FspFv = {}
|
|
flen = 0
|
|
for (fvh, fvhe, offset) in self.FvList:
|
|
fih = self.GetFspInfoHdr (fvh, fvhe, offset)
|
|
if fih:
|
|
if flen != 0:
|
|
raise Exception("Incorrect FV size in image !")
|
|
ftype = str((fih.ImageAttribute >> 28) & 0xF)
|
|
if ftype not in self.FspNameDict:
|
|
raise Exception("Unknown Attribute in image !")
|
|
fname = self.FspNameDict[str(ftype)]
|
|
if fname in self.FspFv:
|
|
raise Exception("Multiple '%s' in image !" % fname)
|
|
self.FspFv[fname] = (copy.deepcopy(fih), [])
|
|
flen = fih.ImageSize
|
|
if flen > 0:
|
|
flen = flen - fvh.FvLength
|
|
if flen < 0:
|
|
raise Exception("Incorrect FV size in image !")
|
|
self.FspFv[fname][1].append(self.FvList.index((fvh, fvhe, offset)))
|
|
|
|
def AddFv(self, offset):
|
|
fvh, fvhe = self.GetFvHdr (offset)
|
|
if fvh is None:
|
|
raise Exception('FV signature is not valid !')
|
|
fvitem = (copy.deepcopy(fvh), copy.deepcopy(fvhe), offset)
|
|
self.FvList.append(fvitem)
|
|
return fvh.FvLength
|
|
|
|
def ParseFv(self):
|
|
offset = 0
|
|
while (offset < len(self.FspDat)):
|
|
fv_len = self.AddFv (offset)
|
|
offset += fv_len
|
|
|
|
def GenFspHdr (fspfile, outdir, hfile, show):
|
|
fsp_fv = FspFv(fspfile)
|
|
fsp_fv.ParseFv()
|
|
fsp_fv.ParseFsp()
|
|
fsp_fv.CheckFsp()
|
|
if show:
|
|
fsp_fv.PrintFv()
|
|
fsp_fv.WriteMap(outdir, hfile)
|
|
|
|
def SplitFspBin (fspfile, outdir, nametemplate, show):
|
|
fsp_fv = FspFv(fspfile)
|
|
fsp_fv.ParseFv()
|
|
fsp_fv.ParseFsp()
|
|
if show:
|
|
fsp_fv.PrintFv()
|
|
fsp_fv.WriteFsp(outdir, nametemplate)
|
|
|
|
def main ():
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(title='commands')
|
|
|
|
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_split.add_argument('-p', action='store_true', help='Print FSP FV information', default = False)
|
|
|
|
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_genhdr.add_argument('-p', action='store_true', help='Print FSP FV information', default = False)
|
|
|
|
args = parser.parse_args()
|
|
if args.which in ['split', 'genhdr']:
|
|
if not os.path.exists(args.FspBinary):
|
|
raise Exception ("Could not locate FSP binary file '%s' !" % args.FspBinary)
|
|
if not os.path.exists(args.OutputDir):
|
|
raise Exception ("Invalid output directory '%s' !" % args.OutputDir)
|
|
|
|
if args.which == 'split':
|
|
SplitFspBin (args.FspBinary, args.OutputDir, args.NameTemplate, args.p)
|
|
elif args.which == 'genhdr':
|
|
GenFspHdr (args.FspBinary, args.OutputDir, args.HFileName, args.p)
|
|
else:
|
|
pass
|
|
|
|
print 'Done!'
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|