Files
slimbootloader/BootloaderCorePkg/Tools/CfgDataTool.py
Guo Dong 45273c82ca Enhance CfgDataTool
Add a check for a corner case.

Signed-off-by: Guo Dong <guo.dong@intel.com>
2024-09-29 16:26:50 -07:00

976 lines
41 KiB
Python

## @ CfgDataTool.py
#
# Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
import sys
import collections
sys.dont_write_bytecode = True
from IfwiUtility import *
from CommonUtility import *
CFGDATA_INT_GUID = b'\xD0\x6C\x6E\x01\x34\x48\x7E\x4C\xBC\xFE\x41\xDF\xB8\x8A\x6A\x6D'
class CCfgData:
DEBUG_FLAG_PARSE = (1 << 0)
DUMP_FLAG_INPUT = (1 << 0)
DUMP_FLAG_OUTPUT = (1 << 1)
DUMP_FLAG_VERBOSE = (1 << 7)
class CDATA_BLOB_HEADER(Structure):
ATTR_SIGNED = 1 << 0
ATTR_MERGED = 1 << 7
_pack_ = 1
_fields_ = [
('Signature', ARRAY(c_char, 4)),
('HeaderLength', c_uint8),
('Attribute', c_uint8),
('Svn', c_uint8),
('Reserved', ARRAY(c_char, 1)),
('UsedLength', c_uint32),
('TotalLength', c_uint32),
]
class CDATA_COND(Structure):
_pack_ = 1
_fields_ = [('Value', c_uint32)]
class CDATA_HEADER(Structure):
FLAG_ITEM_TYPE_NORMAL = 0
FLAG_ITEM_TYPE_ARRAY = 1
FLAG_ITEM_TYPE_REFER = 2
FLAG_ITEM_TYPE_MASK = 3
_pack_ = 1
_fields_ = [
('ConditionNum', c_uint32, 2),
('Length', c_uint32, 10),
('Flags', c_uint32, 4),
('Version', c_uint32, 4),
('Tag', c_uint32, 12),
]
class CDATA_PLATFORM_ID(Structure):
TAG = 0x0F0
_pack_ = 1
_fields_ = [('PlatformId', c_uint16), ('Reserved', c_uint16)]
class CDATA_REFERENCE(Structure):
_pack_ = 1
_fields_ = [('PlatformId', c_uint16), ('Tag', c_uint16, 12), ('IsInternal', c_uint16, 1), ('Reserved', c_uint16, 3)]
class CDATA_ITEM_ARRAY(Structure):
_pack_ = 1
_fields_ = [
('HeaderSize', c_uint8),
('BasePlatformId', c_uint8),
('ItemSize', c_uint16),
('ItemCount', c_uint16),
('ItemIdBitOff', c_uint8),
('ItemIdBitLen', c_uint8),
('ItemValidBitOff', c_uint8),
('ItemUnused', c_uint8),
]
def __init__ (self):
self._Debug = 0 & CCfgData.DEBUG_FLAG_PARSE
self.PlatformId = None
self.CfgDataBase = collections.OrderedDict()
self.CfgDataPid = {}
self.CfgDataItems = []
self.CfgDataDataArrayDict = {}
self.CfgDataArrayPidDict = {}
def NormalizePid (self, PlatformId):
if (PlatformId & ~0x1F):
raise Exception(
"Invalid platform ID 0x%04X, should be in range from 0 to 31!\n"
% PlatformId)
return PlatformId
def DumpTags (self, Flag, CfgItemList):
Offset = sizeof(CCfgData.CDATA_BLOB_HEADER)
for CfgItem in CfgItemList:
CfgData = CfgItem[0]
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(CfgData[0])
DataCond = CCfgData.CDATA_COND.from_buffer(CfgData[1])
PrintData = True
IsArray = False
ExtraInfo = []
if CfgItem[3]:
ExtraInfo.append ('Built-In')
if (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == \
CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_ARRAY:
ArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(CfgData[2])
if ArrayInfo.BasePlatformId < 0x80:
ExtraInfo.append ('BasePid:0x%02X' % ArrayInfo.BasePlatformId)
ExtraInfo.append ('Array:%d*%d' % (ArrayInfo.ItemSize, ArrayInfo.ItemCount))
IsArray = True
if (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == \
CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_REFER:
Reference = CCfgData.CDATA_REFERENCE.from_buffer(CfgData[2])
if Reference.IsInternal:
Internal = '[Int]'
else:
Internal = ''
ExtraInfo.append ('ReferPid:0x%02X%s' % (Reference.PlatformId, Internal))
if Reference.Tag != CfgTagHdr.Tag:
ExtraInfo.append ('ReferTag:0x%03X' % Reference.Tag)
PrintData = False
Extra = ', '.join (ExtraInfo)
if Extra:
Extra = '(%s)' % Extra
print (" TAG %03X: MSK=%08X LEN=%04X OFF=%04X %s" %
(CfgTagHdr.Tag, CfgItem[1], CfgTagHdr.Length * 4, Offset, Extra))
if Flag & CCfgData.DUMP_FLAG_VERBOSE:
if PrintData:
if not IsArray:
if (len(CfgData[2]) > 1):
print_bytes (CfgData[2], 5)
else:
Offset = 0
DataOffset = sizeof(CCfgData.CDATA_ITEM_ARRAY)
BitMaskLen = ArrayInfo.HeaderSize - DataOffset
print(" ARRAY HEADER:")
print_bytes (CfgData[2][:DataOffset], 5, Offset)
Offset += DataOffset
print(" ARRAY MASK:")
print_bytes (CfgData[2][DataOffset:DataOffset+BitMaskLen], 5, Offset)
Offset += BitMaskLen
if ArrayInfo.ItemCount > 0:
print(" ARRAY DATA:")
ArrayData = CfgData[2][ArrayInfo.HeaderSize:]
DataOffset = 0
for Idx in range (ArrayInfo.ItemCount):
print_bytes (ArrayData[DataOffset:DataOffset + ArrayInfo.ItemSize], 5, Offset)
DataOffset += ArrayInfo.ItemSize
Offset += ArrayInfo.ItemSize
Offset += CfgTagHdr.Length * 4
def Dump (self, Flag, Input = True):
if Flag & CCfgData.DUMP_FLAG_INPUT:
print("%sPUT:" % ("IN" if Input else "OUT"))
for CfgFile, (CfgItemList, CfgBlobHeader, IsBuiltIn) in list(self.CfgDataBase.items()):
if CfgFile in self.CfgDataPid:
Pid = self.CfgDataPid[CfgFile]
else:
Pid = 0
BuiltIn = '*' if IsBuiltIn else ''
print("PID=%04X LEN=%04X (%s%s)" % (Pid, CfgBlobHeader.UsedLength, CfgFile, BuiltIn))
self.DumpTags (Flag, CfgItemList)
if Flag & CCfgData.DUMP_FLAG_OUTPUT:
print("MERGED:")
self.DumpTags (Flag, self.CfgDataItems)
print('')
def ProcessCfgArray (self, Header, Data, PidMask, CfgBinFile):
ArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(Data)
ActualLen = ArrayInfo.ItemCount * ArrayInfo.ItemSize + ArrayInfo.HeaderSize + \
sizeof(Header) + sizeof(CCfgData.CDATA_COND) * Header.ConditionNum
if ActualLen % 4 > 0:
raise Exception(
"The full array config size must be DWORD aligned in TAG '%03X'!" % Header.Tag)
if ArrayInfo.ItemSize % 4 > 0:
raise Exception(
"Each config item size must be DWORD aligned in TAG '%03X'!" %
Header.Tag)
if Header.Length != ActualLen // 4:
raise Exception(
"Invalid array item count/size field in TAG '0x%03X'!" %
Header.Tag)
BitMaskLen = ArrayInfo.HeaderSize - sizeof (ArrayInfo)
ByteWidth = (ArrayInfo.ItemCount + 7) // 8
if ByteWidth < 2:
ByteWidth = 2
if BitMaskLen < ByteWidth:
raise Exception(
"Bit mask array is too small in TAG '0x%03X', at least %d bytes required!"
% (Header.Tag, ByteWidth))
BitMaskDat = bytearray('1' * ArrayInfo.ItemCount + '0' *
(BitMaskLen * 8 - ArrayInfo.ItemCount), 'utf-8')
ItemValidByteOffset = ArrayInfo.ItemValidBitOff // 8
ItemValidByteMask = 1 << (ArrayInfo.ItemValidBitOff & (8 - 1))
DataOff = ArrayInfo.HeaderSize
ArrayTagKey = '%03X' % Header.Tag
if ArrayInfo.BasePlatformId == 0x80:
# The bit mask has been processed for base table
if ArrayTagKey in self.CfgDataDataArrayDict:
raise Exception(
"Base configuration already exists for TAG '0x%s'!" % ArrayTagKey)
Pid = (PidMask&-PidMask).bit_length() - 1
if Pid < 0:
raise Exception("Invalid condition value '%08X'!" % PidMask)
self.CfgDataDataArrayDict[ArrayTagKey] = []
self.CfgDataArrayPidDict[ArrayTagKey] = Pid
while DataOff < len(Data):
self.CfgDataDataArrayDict[ArrayTagKey].append(Data[DataOff:DataOff +
ArrayInfo.ItemSize])
DataOff += ArrayInfo.ItemSize
elif ArrayInfo.BasePlatformId == 0xFF:
# The bit mask has not been processed yet for base table
if ItemValidByteOffset >= ArrayInfo.ItemSize:
raise Exception(
"Item valid byte offset (%d) must be less than the item size (%d) in TAG '0x%03X'!"
% (ItemValidByteOffset, ArrayInfo.ItemSize, Header.Tag))
if ArrayTagKey in self.CfgDataDataArrayDict:
if self.CfgDataPid[CfgBinFile] == self.CfgDataArrayPidDict[ArrayTagKey]:
ArrayInfo.BasePlatformId = 0x80
else:
ArrayInfo.BasePlatformId = self.CfgDataArrayPidDict[ArrayTagKey]
else:
# Mark it as a base config item
ArrayInfo.BasePlatformId = 0x80
self.CfgDataDataArrayDict[ArrayTagKey] = []
self.CfgDataArrayPidDict[ArrayTagKey] = self.CfgDataPid[CfgBinFile]
# Check the invliad flag and remove those items
ItemDict = {}
RemovedItem = 0
Index = 0
DataLen = len(Data)
while DataOff < DataLen:
Remove = False
if ArrayInfo.BasePlatformId == 0x80:
# Check ItemID to make sure it is unique
ItemId = get_bits_from_bytes (Data[DataOff:DataOff + ArrayInfo.ItemSize], ArrayInfo.ItemIdBitOff, ArrayInfo.ItemIdBitLen)
if ItemId not in ItemDict.keys():
ItemDict[ItemId] = 1
else:
raise Exception("ItemId '0x%X' is not unique indicated by ItemIdBitOff/ItemIdBitLen in array header !" % ItemId)
# It is a base table, remove marker and assemble mask
if Data[DataOff + ItemValidByteOffset] & ItemValidByteMask:
Data[DataOff + ItemValidByteOffset] = Data[
DataOff + ItemValidByteOffset] ^ ItemValidByteMask
BitMaskDat[Index] = ord('0')
self.CfgDataDataArrayDict[ArrayTagKey].append(Data[DataOff:DataOff +
ArrayInfo.ItemSize])
else:
if ArrayTagKey in self.CfgDataDataArrayDict:
if Data[DataOff:DataOff + ArrayInfo.ItemSize] != self.CfgDataDataArrayDict[ArrayTagKey][Index]:
BitMaskDat[Index] = ord('0')
else:
Remove = True
if Data[DataOff + ItemValidByteOffset] & ItemValidByteMask:
Remove = True
if Remove:
Data[DataOff:] = Data[DataOff + ArrayInfo.ItemSize:] + b'\x00' * ArrayInfo.ItemSize
DataLen -= ArrayInfo.ItemSize
RemovedItem += 1
else:
DataOff += ArrayInfo.ItemSize
Index += 1
ArrayInfo.ItemCount -= RemovedItem
Header.Length -= (RemovedItem * ArrayInfo.ItemSize) // 4
# Update mask
BitWidth = BitMaskLen * 8
MaskHexStr = '{0:0{w}x}'.format(int(BitMaskDat.decode()[::-1], 2), w=BitWidth // 4)
BinData = bytearray.fromhex(MaskHexStr)[::-1]
Offset = sizeof (CCfgData.CDATA_ITEM_ARRAY)
Data[Offset:Offset + BitMaskLen] = BinData
return DataLen
def Parse (self, CfgBinFile):
if CfgBinFile.endswith('*'):
IsBuiltIn = True
CfgBinFile = CfgBinFile[:-1]
else:
IsBuiltIn = False
if self._Debug & CCfgData.DEBUG_FLAG_PARSE:
MiscStr = ' built-in' if IsBuiltIn else ''
print ("Parsing%s config binary '%s'" % (MiscStr, CfgBinFile))
with open(CfgBinFile, "rb") as Fin:
FileData = bytearray(Fin.read())
CfgBlobHeader = CCfgData.CDATA_BLOB_HEADER.from_buffer(FileData)
if CfgBlobHeader.Signature != b'CFGD':
raise Exception("Invalid config binary file '%s' !" % CfgBinFile)
IsMergedCfg = True if CfgBlobHeader.Attribute & CCfgData.CDATA_BLOB_HEADER.ATTR_MERGED else False
CfgItemList = []
Length = min(len(FileData), CfgBlobHeader.UsedLength)
# Find Platform ID first
if not IsMergedCfg:
Offset = sizeof(CCfgData.CDATA_BLOB_HEADER)
while Offset < Length:
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(FileData, Offset)
CfgDlen = CfgTagHdr.Length * 4
CfgHdrLen = sizeof(CCfgData.CDATA_HEADER) + CfgTagHdr.ConditionNum * sizeof(CCfgData.CDATA_COND)
if CfgTagHdr.Tag == CCfgData.CDATA_PLATFORM_ID.TAG:
NextOff = Offset + CfgHdrLen
DataBin = FileData[NextOff:Offset + CfgDlen]
Pid = CCfgData.CDATA_PLATFORM_ID.from_buffer(DataBin)
self.CfgDataPid[CfgBinFile] = self.NormalizePid(Pid.PlatformId)
break
if (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == \
CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_ARRAY:
NextOff = Offset + CfgHdrLen
ArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(FileData[NextOff:])
ActualLen = ArrayInfo.ItemCount * ArrayInfo.ItemSize + ArrayInfo.HeaderSize + \
sizeof(CfgTagHdr) + sizeof(CCfgData.CDATA_COND) * CfgTagHdr.ConditionNum
if ActualLen % 4 > 0:
raise Exception(
"The full array config size must be DWORD aligned in TAG '%03X'!" % CfgTagHdr.Tag)
if ActualLen != CfgDlen:
raise Exception(
"Actual config data length does not match the length indicated "
"by the config header in TAG '%03X'!" % CfgTagHdr.Tag)
Offset += CfgDlen
if CfgBinFile not in self.CfgDataPid:
raise Exception("TAG PlatformId cannot be found in file '%s'!" % CfgBinFile)
# Add config tags
Offset = sizeof(CCfgData.CDATA_BLOB_HEADER)
while Offset < Length:
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(FileData, Offset)
NextOff = Offset + sizeof(CCfgData.CDATA_HEADER)
CondBin = bytearray()
for Idx in range(0, CfgTagHdr.ConditionNum):
CondBin.extend(FileData[NextOff:NextOff + sizeof(
CCfgData.CDATA_COND)])
NextOff += sizeof(CCfgData.CDATA_COND)
CfgDlen = CfgTagHdr.Length * 4
DataBin = FileData[NextOff:Offset + CfgDlen]
DataCond = CCfgData.CDATA_COND.from_buffer(CondBin)
if IsMergedCfg:
PidMask = DataCond.Value
else:
PidMask = 1 << self.CfgDataPid[CfgBinFile]
DataCond.Value = 0x00000000
if self._Debug & CCfgData.DEBUG_FLAG_PARSE:
print (" TAG %03X: OFF=%04X PIDMSK=%08X LEN=%04X" %
(CfgTagHdr.Tag, Offset, PidMask, CfgDlen))
if (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == \
CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_ARRAY:
DataLen = self.ProcessCfgArray(CfgTagHdr, DataBin, PidMask, CfgBinFile)
else:
DataLen = len(DataBin)
CfgItemList.append([(bytearray(CfgTagHdr), CondBin, DataBin[:DataLen]), PidMask, PidMask, IsBuiltIn])
Offset += CfgDlen
if (Offset != Length) or (Length % 4 != 0):
raise Exception("Invalid CFGDATA binary blob format for file '%s' !" % CfgBinFile)
self.CfgDataBase[CfgBinFile] = (CfgItemList, CfgBlobHeader, IsBuiltIn)
def Merge(self, CfgItem, PidMask):
CfgData = CfgItem[0]
# Try to find a match on TAG and DATA
Idx = next((i for i, v in enumerate(self.CfgDataItems) if v[0] == CfgData), -1)
if Idx >= 0:
# Found one. Change the MASK to reuse the existing data
self.CfgDataItems[Idx][1] |= PidMask
return
Append = True
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(CfgData[0])
if CfgTagHdr.Tag == CCfgData.CDATA_PLATFORM_ID.TAG:
if not self.PlatformId:
self.PlatformId = CCfgData.CDATA_PLATFORM_ID.TAG
DataCond = CCfgData.CDATA_COND.from_buffer(CfgData[1])
DataCond.Value = 0x00000000
Pid = CCfgData.CDATA_PLATFORM_ID.from_buffer(CfgData[2])
Pid.PlatformId = 0
else:
Append = False
if Append:
OrgPidMask = CfgItem[1]
IsBuiltIn = CfgItem[3]
CfgTag = 0
NewPidMask = PidMask | OrgPidMask
if len(CfgData[2]) > 4:
# Try to find a match on DATA only
Idx = next((i for i, v in enumerate(self.CfgDataItems) if v[0][2] == CfgData[2]), -1)
if Idx >= 0:
RefCfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(self.CfgDataItems[Idx][0][0])
CfgTag = RefCfgTagHdr.Tag
NewPidMask = self.CfgDataItems[Idx][2]
CfgData = (CfgData[0], CfgData[1], '\x00')
IsBuiltIn = IsBuiltIn or self.CfgDataItems[Idx][3]
self.CfgDataItems.append ([CfgData, NewPidMask, OrgPidMask, IsBuiltIn, CfgTag])
def Create(self, CfgOutFile, PlatformIdStr):
self.CfgDataItems = []
self.PlatformId = None
if PlatformIdStr:
if PlatformIdStr.startswith('0x'):
PlatformId = int(PlatformIdStr, 16)
else:
PlatformId = int(PlatformIdStr)
else:
PlatformId = -1
if PlatformId >= 32 and PlatformId != 0xFF:
raise Exception("Invalid platfrom ID '%d' is specified !" % PlatformId)
for CfgFile, (CfgItemList, CfgBlobHeader, IsBuiltIn) in list(self.CfgDataBase.items()):
for CfgItem in CfgItemList:
if CfgBlobHeader.Attribute & CCfgData.CDATA_BLOB_HEADER.ATTR_MERGED:
PidMask = 0
else:
PidMask = (1 << self.CfgDataPid[CfgFile]) & 0xFFFFFFFF
self.Merge(CfgItem, PidMask)
# CfgGrp: (HEADER, CONDITION, DATA)
PidMaskUpdate = []
BinDat = bytearray()
for CfgItem in self.CfgDataItems:
IsBuiltIn = CfgItem[3]
ReferTag = CfgItem[4]
TagHdr, CondBin, DataBin = CfgItem[0]
CfgDataHdr = CCfgData.CDATA_HEADER.from_buffer(TagHdr)
if CfgDataHdr.Tag == CCfgData.CDATA_PLATFORM_ID.TAG:
if PlatformId >=0:
print("Set platform ID to %d" % PlatformId)
DataCond = CCfgData.CDATA_COND.from_buffer(CondBin)
DataCond.Value = 0xFFFFFFFF
Pid = CCfgData.CDATA_PLATFORM_ID.from_buffer(DataBin)
Pid.PlatformId = PlatformId
BinDat.extend (TagHdr + CondBin + DataBin)
continue
CfgDataCond = CCfgData.CDATA_COND.from_buffer(CondBin)
CfgDataCond.Value = CfgItem[1]
if IsBuiltIn or ReferTag > 0:
NewPidMask = CfgItem[1]
OldPidMask = CfgItem[2]
if ((NewPidMask != OldPidMask) and (OldPidMask != 0)) or (ReferTag > 0):
# A built-in pidmask cannot be modified directly.
# Build a speical refer tag to reuse this item
if ReferTag > 0:
PidMask = OldPidMask
BasePid = NewPidMask.bit_length() - 1
else:
PidMask = NewPidMask ^ OldPidMask
BasePid = OldPidMask.bit_length() - 1
CfgTagHdr = CCfgData.CDATA_HEADER()
CfgDataCond = CCfgData.CDATA_COND()
CfgDataRefer = CCfgData.CDATA_REFERENCE()
CfgTagHdr.ConditionNum = 1
CfgTagHdr.Tag = CfgDataHdr.Tag
CfgTagHdr.Flags = CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_REFER
CfgTagHdr.Length = (sizeof(CfgDataRefer) + sizeof(CfgTagHdr) + \
sizeof(CfgDataCond) * CfgTagHdr.ConditionNum + 3) // 4
CfgDataCond.Value = PidMask
CfgDataRefer.PlatformId = BasePid
CfgDataRefer.IsInternal = 1 if IsBuiltIn else 0
CfgTag = CfgDataHdr.Tag if ReferTag == 0 else ReferTag
CfgDataRefer.Tag = CfgTag
BinDat.extend (bytearray(CfgTagHdr) + bytearray(CfgDataCond) + bytearray(CfgDataRefer))
else:
BinDat.extend (TagHdr + CondBin + DataBin)
CfgdHdr = CCfgData.CDATA_BLOB_HEADER()
CfgdHdr.Signature = b'CFGD'
CfgdHdr.Attribute = CCfgData.CDATA_BLOB_HEADER.ATTR_MERGED
CfgdHdr.HeaderLength = sizeof(CfgdHdr)
CfgdHdr.UsedLength = len(BinDat) + CfgdHdr.HeaderLength
CfgdHdr.TotalLength = CfgdHdr.UsedLength
with open(CfgOutFile, "wb") as Fout:
Fout.write (CfgdHdr)
Fout.write (BinDat)
def GetCfgDataByTag (CfgData, Pid, Tag, IsInternal = False):
Idx = 0 if IsInternal else 1
CfgFile, (CfgItemList, CfgBlobHdr, IsBuiltIn) = list(CfgData[Idx].CfgDataBase.items())[0]
for CfgItem in CfgItemList:
TagHdr, CondBin, DataBin = CfgItem[0]
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(TagHdr)
if CfgTagHdr.Tag != Tag:
continue
if (CfgTagHdr.Tag != CCfgData.CDATA_PLATFORM_ID.TAG) and (CfgItem[1] & (1 << Pid) == 0):
continue
if (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_ARRAY:
ArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(DataBin)
Offset = ArrayInfo.HeaderSize
MaskOff = sizeof(ArrayInfo)
MaskLen = Offset - MaskOff
if ArrayInfo.BasePlatformId < 0x80:
RefPid = ArrayInfo.BasePlatformId
TagHdr, CondBin, BaseDataBin = GetCfgDataByTag (CfgData, RefPid, Tag, True)
CurrArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(DataBin)
BaseArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(BaseDataBin)
NewDataBin = bytearray (BaseDataBin)
# Copy entries from base table
ItemDict = {}
ItemLen = BaseArrayInfo.ItemSize
for Idx1 in range (BaseArrayInfo.ItemCount):
Off1 = Offset + Idx1 * ItemLen
BaseItem = BaseDataBin[Off1 : Off1 + ItemLen]
ItemId = get_bits_from_bytes (BaseItem, BaseArrayInfo.ItemIdBitOff, BaseArrayInfo.ItemIdBitLen)
NewItem = NewDataBin[Off1 : Off1 + ItemLen]
if DataBin[MaskOff + (Idx1 >> 3)] & (1 << (Idx1 & 7)):
set_bits_to_bytes (NewItem, BaseArrayInfo.ItemValidBitOff, 1, 0)
else:
ItemDict[ItemId] = Idx1
set_bits_to_bytes (NewItem, BaseArrayInfo.ItemValidBitOff, 1, 1)
NewDataBin[Off1 : Off1 + ItemLen] = NewItem
for Idx2 in range (CurrArrayInfo.ItemCount):
Off2 = Offset + Idx2 * ItemLen
CurrItem = DataBin[Off2 : Off2 + ItemLen]
ItemId = get_bits_from_bytes (CurrItem, BaseArrayInfo.ItemIdBitOff, BaseArrayInfo.ItemIdBitLen)
Idx1 = ItemDict[ItemId]
Off1 = Offset + Idx1 * ItemLen
NewDataBin[Off1 : Off1 + ItemLen] = CurrItem
elif ArrayInfo.BasePlatformId == 0x80:
NewDataBin = bytearray (DataBin)
# Zero masks and base pid
NewDataBin[MaskOff : MaskOff + MaskLen] = b'\x00' * MaskLen
NewArrayInfo = CCfgData.CDATA_ITEM_ARRAY.from_buffer(NewDataBin)
NewArrayInfo.BasePlatformId = 0xFF
DataBin = NewDataBin
elif (CfgTagHdr.Flags & CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_MASK) == CCfgData.CDATA_HEADER.FLAG_ITEM_TYPE_REFER:
Refer = CCfgData.CDATA_REFERENCE.from_buffer(DataBin)
TagHdrInt, CondBinInt, DataBin = GetCfgDataByTag (CfgData, Refer.PlatformId, Refer.Tag, True)
return TagHdr, CondBin, DataBin
if Idx == 1:
# Try to find it in internal database
return GetCfgDataByTag (CfgData, Pid, Tag, True)
else:
raise Exception ('Could not find TAG:0x%03X for PID:0x%02X in internal or external CFGDATA !' % Tag)
def CmdExport(Args):
BrdNameDict = {}
if Args.board_name_list:
Parts = Args.board_name_list.split(',')
for Part in Parts:
Info = Part.split(':')
if len(Info) == 2:
BrdNameDict[int(Info[0],0)] = Info[1].strip()
OutputDir = Args.output_dir
if not os.path.exists(OutputDir):
os.mkdir (OutputDir)
# Locate CFGDATA in BIOS region
IfwiBin = bytearray (get_file_data(Args.ifwi_file))
IfwiParser = IFWI_PARSER ()
Ifwi = IfwiParser.parse_ifwi_binary (IfwiBin)
Cfgs = IfwiParser.find_components(Ifwi, 'CNFG')
if not Cfgs:
IsBpdt = True
Cfgs = IfwiParser.find_components(Ifwi, 'CFGD')
else:
IsBpdt = False
PartFmt = '/RD%%d/'
if len(Cfgs) == 0:
print ("ERROR: Conld not find external CFGDATA !")
return -1
# Adjust path to point to proper boot partition
Bp = int(Args.boot_part)
CfgdPath = ''
for Cfgd in Cfgs:
CfgdPath = IfwiParser.get_component_path (Cfgd)
if IsBpdt:
PartStr = '/BP%d/' % Bp
else:
PartStr = '/RD%d/' % Bp
if PartStr in CfgdPath:
break
# For non-redundant layout, just use the 1st CFGD found
if CfgdPath == '':
print ('INFO: No redundant boot partition found !')
CfgdPath = Cfgs[0]
# Locate Stage1B image
Stage1bName = 'IBB' if IsBpdt else 'SG1B'
Stage1bPath = '/'.join(CfgdPath.split('/')[:-1]) + '/%s' % Stage1bName
Stage1bComp = IfwiParser.locate_component (Ifwi, Stage1bPath)
if not Stage1bComp:
print ('ERROR: Failed to extract external STAGE1B !')
return -2
# Decompress Stage1B image if required
Stage1bBin = IfwiBin[Stage1bComp.offset : Stage1bComp.offset + Stage1bComp.length]
if Stage1bBin[0:2] == b'LZ':
if Args.tool_dir == '':
print ("ERROR: '-t' is required to specify compress tool directory !")
return -3
Stage1bLz = OutputDir + '/Stage1b.lz'
Stage1bFd = OutputDir + '/Stage1b.fd'
gen_file_from_object (Stage1bLz, Stage1bBin)
decompress (Stage1bLz, Stage1bFd, tool_dir = Args.tool_dir)
Stage1bBin = bytearray (get_file_data (Stage1bFd))
# Locate and generate internal CFGDATA
Offset = Stage1bBin.find (CFGDATA_INT_GUID)
if Offset < 0:
print ('ERROR: Failed to locate internal CFGDATA !')
return -4
Offset += 0x1C
CfgBlobHeader = CCfgData.CDATA_BLOB_HEADER.from_buffer(Stage1bBin, Offset)
if CfgBlobHeader.Signature != b'CFGD':
print ('ERROR: Invalid internal CFGDATA format !')
return -5
CfgDataInt = Stage1bBin[Offset : Offset + CfgBlobHeader.TotalLength]
CfgBinIntFile = OutputDir + '/CfgDataInt.bin'
gen_file_from_object (CfgBinIntFile, CfgDataInt)
# Generate external CFGDATA
CfgBinExtFile = OutputDir + '/CfgDataExt.bin'
gen_file_from_object (CfgBinExtFile, IfwiBin[Cfgd.offset : Cfgd.offset + Cfgd.length])
# Parse CFGDATA blobs
CfgDataInt = CCfgData()
CfgDataInt.Parse(CfgBinIntFile)
CfgDataExt = CCfgData()
CfgDataExt.Parse(CfgBinExtFile)
# Generate CfgDataDef blob
CfgFile, (CfgIntItemList, CfgIntBlobHdr, IsBuiltIn) = list(CfgDataInt.CfgDataBase.items())[0]
CfgDef = bytearray(CfgIntBlobHdr)
TagDict = collections.OrderedDict()
for Idx, CfgIntItem in enumerate(CfgIntItemList):
TagHdr, CondBin, DataBin = CfgIntItem[0]
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(TagHdr)
if CfgTagHdr.Tag in TagDict.keys():
break
else:
TagDict[CfgTagHdr.Tag] = Idx
CfgDef.extend(TagHdr + CondBin + DataBin)
CfgDefLen = len(CfgDef)
CfgDefBlobHdr = CCfgData.CDATA_BLOB_HEADER.from_buffer(bytearray(CfgIntBlobHdr))
CfgDefBlobHdr.UsedLength = CfgDefLen
CfgDefBlobHdr.TotalLength = CfgDefLen
CfgDefBlobHdr.Attribute = 0
# Collect available platform ID
PidMask = 0
CfgFile, (CfgExtItemList, CfgExtBlobHdr, IsBuiltIn) = list(CfgDataExt.CfgDataBase.items())[0]
for CfgItem in CfgExtItemList:
PidMask |= CfgItem[1]
# Export board specific external CFGDATA
for Pid in range(32):
if (1 << Pid) & PidMask == 0:
continue
print ('Exporting external CFGDATA for PlatformID = 0x%02X' % Pid)
CfgDataBrd = bytearray (CfgDefBlobHdr)
CfgData = [CfgDataInt, CfgDataExt]
for Tag in TagDict.keys():
TagHdr, CondBin, DataBin = GetCfgDataByTag (CfgData, Pid, Tag)
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(TagHdr)
CondBin = b'\x00' * sizeof(CCfgData.CDATA_COND)
TagHdr = bytearray (CfgIntItemList[TagDict[Tag]][0][0])
NewData = bytearray (DataBin)
if CfgTagHdr.Tag == CCfgData.CDATA_PLATFORM_ID.TAG:
PidCfg = CCfgData.CDATA_PLATFORM_ID.from_buffer(NewData)
PidCfg.PlatformId = Pid
CfgDataBrd.extend (TagHdr + CondBin + NewData)
if Pid in BrdNameDict.keys():
Ext = BrdNameDict[Pid]
else:
Ext = 'CfgDataExt_%02X' % Pid
gen_file_from_object (OutputDir + '/%s.bin' % Ext, CfgDataBrd)
def CmdView(Args):
CfgData = CCfgData()
for CfgBinFile in Args.cfg_in_file:
CfgData.Parse(CfgBinFile)
Flag = CCfgData.DUMP_FLAG_INPUT
if Args.dbg_lvl > 2:
Flag |= CCfgData.DUMP_FLAG_VERBOSE
CfgData.Dump(Flag)
def CmdMerge(Args):
CfgData = CCfgData()
for CfgBinFile in Args.cfg_in_file:
CfgData.Parse(CfgBinFile)
if Args.dbg_lvl > 0:
Flag = CCfgData.DUMP_FLAG_INPUT
if Args.dbg_lvl > 2:
Flag |= CCfgData.DUMP_FLAG_VERBOSE
CfgData.Dump (Flag)
CfgData.Create(Args.cfg_out_file, Args.platform_id)
if Args.dbg_lvl > 1:
Flag = CCfgData.DUMP_FLAG_OUTPUT
if Args.dbg_lvl > 2:
Flag |= CCfgData.DUMP_FLAG_VERBOSE
CfgData.Dump (Flag)
if Args.dbg_lvl > 0:
CfgData = CCfgData()
CfgData.Parse(Args.cfg_out_file)
Flag = CCfgData.DUMP_FLAG_INPUT
if Args.dbg_lvl > 2:
Flag |= CCfgData.DUMP_FLAG_VERBOSE
CfgData.Dump(Flag, False)
print ("%d config binary files were merged successfully!" % len(Args.cfg_in_file))
def CmdSign(Args):
Fd = open (Args.cfg_in_file, 'rb')
FileData = bytearray (Fd.read ())
Fd.close ()
CfgBlobHeader = CCfgData.CDATA_BLOB_HEADER.from_buffer(FileData)
if CfgBlobHeader.Signature != b'CFGD':
raise Exception("Invalid config binary file '%s' !" % CfgDataFile)
CfgBlobHeader.Attribute |= CCfgData.CDATA_BLOB_HEADER.ATTR_SIGNED
CfgBlobHeader.Svn = Args.svn
TmpFile = Args.cfg_in_file + '.tmp'
Fd = open (TmpFile, 'wb')
Fd.write (FileData)
Fd.close ()
if Args.hash_alg == 'AUTO':
Args.hash_alg = adjust_hash_type(Args.cfg_pri_key)
rsa_sign_file (Args.cfg_pri_key, None, Args.hash_alg, Args.sign_scheme, TmpFile, Args.cfg_out_file, True, True)
if os.path.exists(TmpFile):
os.remove(TmpFile)
print ("Config file was signed successfully!")
def CmdExtract(Args):
Found = False
TagNo = int(Args.cfg_tag, 0)
CfgData = CCfgData()
for CfgBinFile in Args.cfg_in_file:
CfgData.Parse(CfgBinFile)
for CfgFile, (CfgItemList, CfgBlobHeader, IsBuiltIn) in list(CfgData.CfgDataBase.items()):
for CfgItem in CfgItemList:
TagHdr, CondBin, DataBin = CfgItem[0]
CfgTagHdr = CCfgData.CDATA_HEADER.from_buffer(TagHdr)
if CfgTagHdr.Tag == TagNo:
Found = True
break
if Found:
break
if Found:
BinDat = bytearray()
BinDat.extend (TagHdr + CondBin + DataBin)
print_bytes (BinDat)
if Args.cfg_out_file != None:
with open(Args.cfg_out_file, "wb") as Fout:
Fout.write (BinDat)
print ("Config data (Tag=0x%X) was saved to a file - %s" % (TagNo, Args.cfg_out_file))
else:
print ("Config data (Tag=0x%X) was not found!" % TagNo)
def CmdReplace(Args):
IfwiParser = IFWI_PARSER ()
CfgFile = Args.cfg_in_file
if not os.path.exists(CfgFile):
raise Exception("Cannot find CFGDATA binary file '%s'" % CfgFile)
IfwiImgIn = Args.ifwi_in_file
if not os.path.exists(IfwiImgIn):
raise Exception("Cannot find IFWI image file '%s'" % IfwiImgIn)
# Get cfg binary
Fh = open(CfgFile, 'rb')
CfgBins = bytearray(Fh.read())
Fh.close()
CfgHdr = CCfgData.CDATA_BLOB_HEADER.from_buffer(CfgBins)
if CfgHdr.Signature != b'CFGD':
raise Exception("Invalid CFGDATA image file '%s'" % CfgFile)
if not CfgHdr.Attribute & CCfgData.CDATA_BLOB_HEADER.ATTR_MERGED:
raise Exception("CFGDATA image file '%s' is not merged yet!" % CfgFile)
# Get flash image
Fh = open(IfwiImgIn, 'rb')
BiosBins = bytearray(Fh.read())
Fh.close()
CfgLen = len(CfgBins)
# Check to see if the IFWI is
if Args.pdr:
# CFGDATA in PDR region
RegionName = 'pdr'
else:
# Assume CFGDATA in BIOS region
RegionName = 'bios'
CompList = []
StartOff = 0
EndOff = 0
if IfwiParser.is_ifwi_image(BiosBins):
#Check if it has BPDT
SpiDesc = SPI_DESCRIPTOR.from_buffer(BiosBins, 0)
Comp = IfwiParser.find_ifwi_region(SpiDesc, RegionName)
if len(Comp) < 1:
raise Exception("Cannot not find CFGDATA in SPI flash region '%s' !" % RegionName)
if not CompList:
if RegionName == 'bios':
Ifwi = IfwiParser.parse_ifwi_binary (BiosBins)
cfgs = IfwiParser.find_components(Ifwi, 'CNFG')
if not cfgs:
cfgs = IfwiParser.find_components(Ifwi, 'CFGD')
for cfgd in cfgs:
print (IfwiParser.get_component_path (cfgd))
CompList.append((cfgd.offset, cfgd.length))
else:
# For PDR region, always assume CFGDATA starts from offset 0
CfgBlobHeader = CCfgData.CDATA_BLOB_HEADER.from_buffer(BiosBins[StartOff:])
if CfgBlobHeader.Signature != b'CFGD':
raise Exception("Cannot not find CFGDATA in SPI flash PDR region!")
if CfgBlobHeader.TotalLength > EndOff - StartOff:
raise Exception("Invalid CFGDATA length in PDR region ")
CompList = [(StartOff, EndOff - StartOff)]
for Offset, Size in CompList:
if Offset < 0 or Offset >= len(BiosBins):
raise Exception("Invalid CFGDATA region offset 0x%X!" % Offset)
if CfgLen > Size:
raise Exception("CfgData file size 0x%X shall not be greater than CFGDATA region size 0x%X !" % (CfgLen, Size))
print("Patching CFGDATA region at image offset 0x%X (len: 0x%X)!" % (Offset, Size))
BiosBins[Offset:Offset + CfgLen] = CfgBins
IfwiImgOut = Args.ifwi_out_file
if not IfwiImgOut:
IfwiImgOut = IfwiImgIn
Fh = open(IfwiImgOut, 'wb')
Fh.write(BiosBins)
Fh.close()
if len(CompList):
print("%d CFGDATA region has been patched successfully !" % len(
CompList))
else:
print("No CFGDATA region has been patched!")
return
def Main():
#
# Parse the options and args
#
ArgParser = argparse.ArgumentParser()
SubParser = ArgParser.add_subparsers(help='command')
ViewParser = SubParser.add_parser('view', help='display config data')
ViewParser.add_argument('cfg_in_file',
type=str,
nargs='+',
help='Configuration input binary file')
ViewParser.add_argument('-v', dest='dbg_lvl', type=int, help='Display verbose info:: 0,1,2.Default=0', default = 0)
ViewParser.set_defaults(func=CmdView)
MergeParser = SubParser.add_parser('merge', help='merge config data')
MergeParser.add_argument('cfg_in_file',
type=str,
nargs='+',
help='Configuration input binary file(s) - Input files can be: xxx.rom generated from BCT, xxx.bin generated from SBL source. xxx.bin* - Star represents internal cfg data bin generated from source to be added to merged cfg_out_file')
MergeParser.add_argument('-o', dest='cfg_out_file', type=str, help='Specify Configuration output binary file name to be generated', required=True)
MergeParser.add_argument('-p', dest='platform_id', type=str, help='Force a given platform ID to be used', default = '')
MergeParser.add_argument('-v', dest='dbg_lvl', type=int, help='Display verbose info: 0,1,2.Default=0', default = 0)
MergeParser.set_defaults(func=CmdMerge)
SignParser = SubParser.add_parser('sign', help='sign external config data')
SignParser.add_argument('cfg_in_file',
type=str,
help='Configuration binary file')
SignParser.add_argument('-o', dest='cfg_out_file', type=str, help='Signed configuration output binary file name to be generated', required=True)
SignParser.add_argument('-k', dest='cfg_pri_key', type=str, help='Key Id or Private key file (PEM format) used to sign configuration data', required=True)
SignParser.add_argument('-a', dest='hash_alg', type=str, choices=['SHA2_256', 'SHA2_384', 'AUTO'], help='Hash Type for signing. For AUTO hash type will be choosen based on key length', default = 'AUTO')
SignParser.add_argument('-s', dest='sign_scheme', type=str, choices=['RSA_PKCS1', 'RSA_PSS'], help='Signing Scheme', default = 'RSA_PSS')
SignParser.add_argument('-svn', dest='svn', type=int, help='Security version number for Config Data', default = 0)
SignParser.set_defaults(func=CmdSign)
ExtractParser = SubParser.add_parser('extract', help='extract a single config data to a file')
ExtractParser.add_argument('cfg_in_file',
type=str,
nargs='+',
help='Configuration input binary file')
ExtractParser.add_argument('-t', dest='cfg_tag', type=str, help='Specify tag value to be extracted', required=True)
ExtractParser.add_argument('-o', dest='cfg_out_file', type=str, help='Specify Configuration output binary file name to be generated')
ExtractParser.set_defaults(func=CmdExtract)
ReplaceParser = SubParser.add_parser('replace', help='Replace config data blob within a IFWI')
ReplaceParser.add_argument('cfg_in_file',
type=str,
help='Configuration input binary file')
ReplaceParser.add_argument('-i', dest='ifwi_in_file', type=str, help='Specify IFWI input binary file', required=True)
ReplaceParser.add_argument('-o', dest='ifwi_out_file', type=str, help='Specify IFWI output binary file', default='')
ReplaceParser.add_argument('-p', dest='pdr', action='store_true', help='Replace CFGDATA in PDR region', default=False)
ReplaceParser.set_defaults(func=CmdReplace)
ExportParser = SubParser.add_parser('export', help='Export board external CFGDATA from BIOS or IFWI file')
ExportParser.add_argument('-i', dest='ifwi_file', type=str, help='Specify BIOS or IFWI input binary file', required=True)
ExportParser.add_argument('-b', dest='boot_part', choices=['0', '1'], help='Specify which boot partition to export CFGDATA from', default = '0')
ExportParser.add_argument('-o', dest='output_dir', type=str, help='Specify output directory', default='.')
ExportParser.add_argument('-t', dest='tool_dir', type=str, help='Specify compress tool directory', default='')
ExportParser.add_argument('-n', dest='board_name_list', type=str, help='Specify board name to id map list', default='')
ExportParser.set_defaults(func=CmdExport)
Args = ArgParser.parse_args()
return Args.func(Args)
if __name__ == '__main__':
sys.exit(Main())