# @ GenCfgData.py | |
# | |
# Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR> | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
## | |
import os | |
import re | |
import sys | |
import marshal | |
from functools import reduce | |
from datetime import date | |
# Generated file copyright header | |
__copyright_tmp__ = """/** @file | |
Configuration %s File. | |
Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
This file is automatically generated. Please do NOT modify !!! | |
**/ | |
""" | |
__copyright_dsc__ = """## @file | |
# | |
# Copyright (c) %04d, Intel Corporation. All rights reserved.<BR> | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
## | |
[PcdsDynamicVpd.Upd] | |
# | |
# Global definitions in BSF | |
# !BSF BLOCK:{NAME:"FSP UPD Configuration", VER:"0.1"} | |
# | |
""" | |
def Bytes2Val(Bytes): | |
return reduce(lambda x, y: (x << 8) | y, Bytes[::-1]) | |
def Bytes2Str(Bytes): | |
return '{ %s }' % (', '.join('0x%02X' % i for i in Bytes)) | |
def Str2Bytes(Value, Blen): | |
Result = bytearray(Value[1:-1], 'utf-8') # Excluding quotes | |
if len(Result) < Blen: | |
Result.extend(b'\x00' * (Blen - len(Result))) | |
return Result | |
def Val2Bytes(Value, Blen): | |
return [(Value >> (i * 8) & 0xff) for i in range(Blen)] | |
def Array2Val(ValStr): | |
ValStr = ValStr.strip() | |
if ValStr.startswith('{'): | |
ValStr = ValStr[1:] | |
if ValStr.endswith('}'): | |
ValStr = ValStr[:-1] | |
if ValStr.startswith("'"): | |
ValStr = ValStr[1:] | |
if ValStr.endswith("'"): | |
ValStr = ValStr[:-1] | |
Value = 0 | |
for Each in ValStr.split(',')[::-1]: | |
Each = Each.strip() | |
if Each.startswith('0x'): | |
Base = 16 | |
else: | |
Base = 10 | |
Value = (Value << 8) | int(Each, Base) | |
return Value | |
def GetCopyrightHeader(FileType, AllowModify=False): | |
FileDescription = { | |
'bsf': 'Boot Setting', | |
'dsc': 'Definition', | |
'dlt': 'Delta', | |
'inc': 'C Binary Blob', | |
'h': 'C Struct Header' | |
} | |
if FileType in ['bsf', 'dsc', 'dlt']: | |
CommentChar = '#' | |
else: | |
CommentChar = '' | |
Lines = __copyright_tmp__.split('\n') | |
if AllowModify: | |
Lines = [Line for Line in Lines if 'Please do NOT modify' not in Line] | |
CopyrightHdr = '\n'.join('%s%s' % ( | |
CommentChar, Line) for Line in Lines)[:-1] + '\n' | |
return CopyrightHdr % (FileDescription[FileType], date.today().year) | |
class CLogicalExpression: | |
def __init__(self): | |
self.index = 0 | |
self.string = '' | |
def errExit(self, err=''): | |
print("ERROR: Express parsing for:") | |
print(" %s" % self.string) | |
print(" %s^" % (' ' * self.index)) | |
if err: | |
print("INFO : %s" % err) | |
raise SystemExit | |
def getNonNumber(self, n1, n2): | |
if not n1.isdigit(): | |
return n1 | |
if not n2.isdigit(): | |
return n2 | |
return None | |
def getCurr(self, lens=1): | |
try: | |
if lens == -1: | |
return self.string[self.index:] | |
else: | |
if self.index + lens > len(self.string): | |
lens = len(self.string) - self.index | |
return self.string[self.index: self.index + lens] | |
except Exception: | |
return '' | |
def isLast(self): | |
return self.index == len(self.string) | |
def moveNext(self, len=1): | |
self.index += len | |
def skipSpace(self): | |
while not self.isLast(): | |
if self.getCurr() in ' \t': | |
self.moveNext() | |
else: | |
return | |
def normNumber(self, val): | |
return True if val else False | |
def getNumber(self, var): | |
var = var.strip() | |
if re.match('^0x[a-fA-F0-9]+$', var): | |
value = int(var, 16) | |
elif re.match('^[+-]?\\d+$', var): | |
value = int(var, 10) | |
else: | |
value = None | |
return value | |
def parseValue(self): | |
self.skipSpace() | |
var = '' | |
while not self.isLast(): | |
char = self.getCurr() | |
if re.match('^[\\w.]', char): | |
var += char | |
self.moveNext() | |
else: | |
break | |
val = self.getNumber(var) | |
if val is None: | |
value = var | |
else: | |
value = "%d" % val | |
return value | |
def parseSingleOp(self): | |
self.skipSpace() | |
if re.match('^NOT\\W', self.getCurr(-1)): | |
self.moveNext(3) | |
op = self.parseBrace() | |
val = self.getNumber(op) | |
if val is None: | |
self.errExit("'%s' is not a number" % op) | |
return "%d" % (not self.normNumber(int(op))) | |
else: | |
return self.parseValue() | |
def parseBrace(self): | |
self.skipSpace() | |
char = self.getCurr() | |
if char == '(': | |
self.moveNext() | |
value = self.parseExpr() | |
self.skipSpace() | |
if self.getCurr() != ')': | |
self.errExit("Expecting closing brace or operator") | |
self.moveNext() | |
return value | |
else: | |
value = self.parseSingleOp() | |
return value | |
def parseCompare(self): | |
value = self.parseBrace() | |
while True: | |
self.skipSpace() | |
char = self.getCurr() | |
if char in ['<', '>']: | |
self.moveNext() | |
next = self.getCurr() | |
if next == '=': | |
op = char + next | |
self.moveNext() | |
else: | |
op = char | |
result = self.parseBrace() | |
test = self.getNonNumber(result, value) | |
if test is None: | |
value = "%d" % self.normNumber(eval(value + op + result)) | |
else: | |
self.errExit("'%s' is not a valid number for comparision" | |
% test) | |
elif char in ['=', '!']: | |
op = self.getCurr(2) | |
if op in ['==', '!=']: | |
self.moveNext(2) | |
result = self.parseBrace() | |
test = self.getNonNumber(result, value) | |
if test is None: | |
value = "%d" % self.normNumber((eval(value + op | |
+ result))) | |
else: | |
value = "%d" % self.normNumber(eval("'" + value + | |
"'" + op + "'" + | |
result + "'")) | |
else: | |
break | |
else: | |
break | |
return value | |
def parseAnd(self): | |
value = self.parseCompare() | |
while True: | |
self.skipSpace() | |
if re.match('^AND\\W', self.getCurr(-1)): | |
self.moveNext(3) | |
result = self.parseCompare() | |
test = self.getNonNumber(result, value) | |
if test is None: | |
value = "%d" % self.normNumber(int(value) & int(result)) | |
else: | |
self.errExit("'%s' is not a valid op number for AND" % | |
test) | |
else: | |
break | |
return value | |
def parseOrXor(self): | |
value = self.parseAnd() | |
op = None | |
while True: | |
self.skipSpace() | |
op = None | |
if re.match('^XOR\\W', self.getCurr(-1)): | |
self.moveNext(3) | |
op = '^' | |
elif re.match('^OR\\W', self.getCurr(-1)): | |
self.moveNext(2) | |
op = '|' | |
else: | |
break | |
if op: | |
result = self.parseAnd() | |
test = self.getNonNumber(result, value) | |
if test is None: | |
value = "%d" % self.normNumber(eval(value + op + result)) | |
else: | |
self.errExit("'%s' is not a valid op number for XOR/OR" % | |
test) | |
return value | |
def parseExpr(self): | |
return self.parseOrXor() | |
def getResult(self): | |
value = self.parseExpr() | |
self.skipSpace() | |
if not self.isLast(): | |
self.errExit("Unexpected character found '%s'" % self.getCurr()) | |
test = self.getNumber(value) | |
if test is None: | |
self.errExit("Result '%s' is not a number" % value) | |
return int(value) | |
def evaluateExpress(self, Expr): | |
self.index = 0 | |
self.string = Expr | |
if self.getResult(): | |
Result = True | |
else: | |
Result = False | |
return Result | |
class CFspBsf2Dsc: | |
def __init__(self, bsf_file): | |
self.cfg_list = CFspBsf2Dsc.parse_bsf(bsf_file) | |
def get_dsc_lines(self): | |
return CFspBsf2Dsc.generate_dsc(self.cfg_list) | |
def save_dsc(self, dsc_file): | |
return CFspBsf2Dsc.generate_dsc(self.cfg_list, dsc_file) | |
@staticmethod | |
def parse_bsf(bsf_file): | |
fd = open(bsf_file, 'r') | |
bsf_txt = fd.read() | |
fd.close() | |
find_list = [] | |
regex = re.compile(r'\s+Find\s+"(.*?)"(.*?)^\s+(\$(.*?)|Skip)\s+', | |
re.S | re.MULTILINE) | |
for match in regex.finditer(bsf_txt): | |
find = match.group(1) | |
name = match.group(3) | |
line = bsf_txt[:match.end()].count("\n") | |
find_list.append((name, find, line)) | |
idx = 0 | |
count = 0 | |
prefix = '' | |
chk_dict = {} | |
cfg_list = [] | |
cfg_temp = {'find': '', 'cname': '', 'length': 0, 'value': '0', | |
'type': 'Reserved', 'isbit': False, | |
'embed': '', 'page': '', 'option': '', 'instance': 0} | |
regex = re.compile( | |
r'^\s+(\$(.*?)|Skip)\s+(\d+)\s+(bits|bytes)(\s+\$_DEFAULT_\s' | |
r'+=\s+(.+?))?$', re.S | | |
re.MULTILINE) | |
for match in regex.finditer(bsf_txt): | |
dlen = int(match.group(3)) | |
if match.group(1) == 'Skip': | |
key = 'gPlatformFspPkgTokenSpaceGuid_BsfSkip%d' % idx | |
val = ', '.join(['%02X' % ord(i) for i in '\x00' * dlen]) | |
idx += 1 | |
option = '$SKIP' | |
else: | |
key = match.group(2) | |
val = match.group(6) | |
option = '' | |
is_bit = True if match.group(4) == 'bits' else False | |
cfg_item = dict(cfg_temp) | |
line = bsf_txt[:match.end()].count("\n") | |
finds = [i for i in find_list if line >= i[2]] | |
if len(finds) > 0: | |
prefix = finds[0][1] | |
cfg_item['embed'] = '%s:TAG_%03X:START' % \ | |
(prefix, ord(prefix[-1])) | |
cfg_item['find'] = prefix | |
cfg_item['cname'] = 'Signature' | |
cfg_item['length'] = len(finds[0][1]) | |
str2byte = Str2Bytes("'" + finds[0][1] + "'", | |
len(finds[0][1])) | |
cfg_item['value'] = '0x%X' % Bytes2Val(str2byte) | |
cfg_list.append(dict(cfg_item)) | |
cfg_item = dict(cfg_temp) | |
find_list.pop(0) | |
count = 0 | |
cfg_item['cname'] = key | |
cfg_item['length'] = dlen | |
cfg_item['value'] = val | |
cfg_item['option'] = option | |
cfg_item['isbit'] = is_bit | |
if key not in chk_dict.keys(): | |
chk_dict[key] = 0 | |
else: | |
chk_dict[key] += 1 | |
cfg_item['instance'] = chk_dict[key] | |
cfg_list.append(cfg_item) | |
count += 1 | |
if prefix: | |
cfg_item = dict(cfg_temp) | |
cfg_item['cname'] = 'Dummy' | |
cfg_item['embed'] = '%s:%03X:END' % (prefix, ord(prefix[-1])) | |
cfg_list.append(cfg_item) | |
option_dict = {} | |
selreg = re.compile( | |
r'\s+Selection\s*(.+?)\s*,\s*"(.*?)"$', re.S | | |
re.MULTILINE) | |
regex = re.compile( | |
r'^List\s&(.+?)$(.+?)^EndList$', re.S | re.MULTILINE) | |
for match in regex.finditer(bsf_txt): | |
key = match.group(1) | |
option_dict[key] = [] | |
for select in selreg.finditer(match.group(2)): | |
option_dict[key].append( | |
(int(select.group(1), 0), select.group(2))) | |
chk_dict = {} | |
pagereg = re.compile( | |
r'^Page\s"(.*?)"$(.+?)^EndPage$', re.S | re.MULTILINE) | |
for match in pagereg.finditer(bsf_txt): | |
page = match.group(1) | |
for line in match.group(2).splitlines(): | |
match = re.match( | |
r'\s+(Combo|EditNum)\s\$(.+?),\s"(.*?)",\s(.+?),$', line) | |
if match: | |
cname = match.group(2) | |
if cname not in chk_dict.keys(): | |
chk_dict[cname] = 0 | |
else: | |
chk_dict[cname] += 1 | |
instance = chk_dict[cname] | |
cfg_idxs = [i for i, j in enumerate(cfg_list) | |
if j['cname'] == cname and | |
j['instance'] == instance] | |
if len(cfg_idxs) != 1: | |
raise Exception( | |
"Multiple CFG item '%s' found !" % cname) | |
cfg_item = cfg_list[cfg_idxs[0]] | |
cfg_item['page'] = page | |
cfg_item['type'] = match.group(1) | |
cfg_item['prompt'] = match.group(3) | |
cfg_item['range'] = None | |
if cfg_item['type'] == 'Combo': | |
cfg_item['option'] = option_dict[match.group(4)[1:]] | |
elif cfg_item['type'] == 'EditNum': | |
cfg_item['option'] = match.group(4) | |
match = re.match(r'\s+ Help\s"(.*?)"$', line) | |
if match: | |
cfg_item['help'] = match.group(1) | |
match = re.match(r'\s+"Valid\srange:\s(.*)"$', line) | |
if match: | |
parts = match.group(1).split() | |
cfg_item['option'] = ( | |
(int(parts[0], 0), int(parts[2], 0), | |
cfg_item['option'])) | |
return cfg_list | |
@staticmethod | |
def generate_dsc(option_list, dsc_file=None): | |
dsc_lines = [] | |
header = '%s' % (__copyright_dsc__ % date.today().year) | |
dsc_lines.extend(header.splitlines()) | |
pages = [] | |
for cfg_item in option_list: | |
if cfg_item['page'] and (cfg_item['page'] not in pages): | |
pages.append(cfg_item['page']) | |
page_id = 0 | |
for page in pages: | |
dsc_lines.append(' # !BSF PAGES:{PG%02X::"%s"}' % (page_id, page)) | |
page_id += 1 | |
dsc_lines.append('') | |
last_page = '' | |
is_bit = False | |
dlen = 0 | |
dval = 0 | |
bit_fields = [] | |
for idx, option in enumerate(option_list): | |
if not is_bit and option['isbit']: | |
is_bit = True | |
dlen = 0 | |
dval = 0 | |
idxs = idx | |
if is_bit and not option['isbit']: | |
is_bit = False | |
if dlen % 8 != 0: | |
raise Exception("Bit fields are not aligned at " | |
"byte boundary !") | |
bit_fields.append((idxs, idx, dlen, dval)) | |
if is_bit: | |
blen = option['length'] | |
bval = int(option['value'], 0) | |
dval = dval + ((bval & ((1 << blen) - 1)) << dlen) | |
print(dlen, blen, bval, hex(dval)) | |
dlen += blen | |
struct_idx = 0 | |
for idx, option in enumerate(option_list): | |
dsc_lines.append('') | |
default = option['value'] | |
pos = option['cname'].find('_') | |
name = option['cname'][pos + 1:] | |
for start_idx, end_idx, bits_len, bits_val in bit_fields: | |
if idx == start_idx: | |
val_str = Bytes2Str(Val2Bytes(bits_val, bits_len // 8)) | |
dsc_lines.append(' # !HDR STRUCT:{BIT_FIELD_DATA_%d}' | |
% struct_idx) | |
dsc_lines.append(' # !BSF NAME:{BIT_FIELD_STRUCT}') | |
dsc_lines.append(' gCfgData.BitFiledStruct%d ' | |
' | * | 0x%04X | %s' % | |
(struct_idx, bits_len // 8, val_str)) | |
dsc_lines.append('') | |
struct_idx += 1 | |
if option['find']: | |
dsc_lines.append(' # !BSF FIND:{%s}' % option['find']) | |
dsc_lines.append('') | |
if option['instance'] > 0: | |
name = name + '_%s' % option['instance'] | |
if option['embed']: | |
dsc_lines.append(' # !HDR EMBED:{%s}' % option['embed']) | |
if option['type'] == 'Reserved': | |
dsc_lines.append(' # !BSF NAME:{Reserved} TYPE:{Reserved}') | |
if option['option'] == '$SKIP': | |
dsc_lines.append(' # !BSF OPTION:{$SKIP}') | |
else: | |
prompt = option['prompt'] | |
if last_page != option['page']: | |
last_page = option['page'] | |
dsc_lines.append(' # !BSF PAGE:{PG%02X}' % | |
(pages.index(option['page']))) | |
if option['type'] == 'Combo': | |
dsc_lines.append(' # !BSF NAME:{%s} TYPE:{%s}' % | |
(prompt, option['type'])) | |
ops = [] | |
for val, text in option['option']: | |
ops.append('0x%x:%s' % (val, text)) | |
dsc_lines.append(' # !BSF OPTION:{%s}' % (', '.join(ops))) | |
elif option['type'] == 'EditNum': | |
cfg_len = option['length'] | |
if ',' in default and cfg_len > 8: | |
dsc_lines.append(' # !BSF NAME:{%s} TYPE:{Table}' % | |
(prompt)) | |
if cfg_len > 16: | |
cfg_len = 16 | |
ops = [] | |
for i in range(cfg_len): | |
ops.append('%X:1:HEX' % i) | |
dsc_lines.append(' # !BSF OPTION:{%s}' % | |
(', '.join(ops))) | |
else: | |
dsc_lines.append( | |
' # !BSF NAME:{%s} TYPE:{%s, %s, (0x%X, 0x%X)}' % | |
(prompt, option['type'], option['option'][2], | |
option['option'][0], option['option'][1])) | |
dsc_lines.append(' # !BSF HELP:{%s}' % option['help']) | |
if ',' in default: | |
default = '{%s}' % default | |
if option['isbit']: | |
dsc_lines.append(' # !BSF FIELD:{%s:%db}' | |
% (name, option['length'])) | |
else: | |
dsc_lines.append(' gCfgData.%-30s | * | 0x%04X | %s' % | |
(name, option['length'], default)) | |
if dsc_file: | |
fd = open(dsc_file, 'w') | |
fd.write('\n'.join(dsc_lines)) | |
fd.close() | |
return dsc_lines | |
class CGenCfgData: | |
def __init__(self, Mode=''): | |
self.Debug = False | |
self.Error = '' | |
self.ReleaseMode = True | |
self.Mode = Mode | |
self._GlobalDataDef = """ | |
GlobalDataDef | |
SKUID = 0, "DEFAULT" | |
EndGlobalData | |
""" | |
self._BuidinOptionTxt = """ | |
List &EN_DIS | |
Selection 0x1 , "Enabled" | |
Selection 0x0 , "Disabled" | |
EndList | |
""" | |
self._StructType = ['UINT8', 'UINT16', 'UINT32', 'UINT64'] | |
self._BsfKeyList = ['FIND', 'NAME', 'HELP', 'TYPE', 'PAGE', 'PAGES', | |
'BLOCK', 'OPTION', 'CONDITION', 'ORDER', 'MARKER', | |
'SUBT'] | |
self._HdrKeyList = ['HEADER', 'STRUCT', 'EMBED', 'COMMENT'] | |
self._BuidinOption = {'$EN_DIS': 'EN_DIS'} | |
self._MacroDict = {} | |
self._VarDict = {} | |
self._PcdsDict = {} | |
self._CfgBlkDict = {} | |
self._CfgPageDict = {} | |
self._CfgOptsDict = {} | |
self._BsfTempDict = {} | |
self._CfgItemList = [] | |
self._DscLines = [] | |
self._DscFile = '' | |
self._CfgPageTree = {} | |
self._MapVer = 0 | |
self._MinCfgTagId = 0x100 | |
def ParseMacros(self, MacroDefStr): | |
# ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build'] | |
self._MacroDict = {} | |
IsExpression = False | |
for Macro in MacroDefStr: | |
if Macro.startswith('-D'): | |
IsExpression = True | |
if len(Macro) > 2: | |
Macro = Macro[2:] | |
else: | |
continue | |
if IsExpression: | |
IsExpression = False | |
Match = re.match("(\\w+)=(.+)", Macro) | |
if Match: | |
self._MacroDict[Match.group(1)] = Match.group(2) | |
else: | |
Match = re.match("(\\w+)", Macro) | |
if Match: | |
self._MacroDict[Match.group(1)] = '' | |
if len(self._MacroDict) == 0: | |
Error = 1 | |
else: | |
Error = 0 | |
if self.Debug: | |
print("INFO : Macro dictionary:") | |
for Each in self._MacroDict: | |
print(" $(%s) = [ %s ]" % (Each, | |
self._MacroDict[Each])) | |
return Error | |
def EvaulateIfdef(self, Macro): | |
Result = Macro in self._MacroDict | |
if self.Debug: | |
print("INFO : Eval Ifdef [%s] : %s" % (Macro, Result)) | |
return Result | |
def ExpandMacros(self, Input, Preserve=False): | |
Line = Input | |
Match = re.findall("\\$\\(\\w+\\)", Input) | |
if Match: | |
for Each in Match: | |
Variable = Each[2:-1] | |
if Variable in self._MacroDict: | |
Line = Line.replace(Each, self._MacroDict[Variable]) | |
else: | |
if self.Debug: | |
print("WARN : %s is not defined" % Each) | |
if not Preserve: | |
Line = Line.replace(Each, Each[2:-1]) | |
return Line | |
def ExpandPcds(self, Input): | |
Line = Input | |
Match = re.findall("(\\w+\\.\\w+)", Input) | |
if Match: | |
for PcdName in Match: | |
if PcdName in self._PcdsDict: | |
Line = Line.replace(PcdName, self._PcdsDict[PcdName]) | |
else: | |
if self.Debug: | |
print("WARN : %s is not defined" % PcdName) | |
return Line | |
def EvaluateExpress(self, Expr): | |
ExpExpr = self.ExpandPcds(Expr) | |
ExpExpr = self.ExpandMacros(ExpExpr) | |
LogExpr = CLogicalExpression() | |
Result = LogExpr.evaluateExpress(ExpExpr) | |
if self.Debug: | |
print("INFO : Eval Express [%s] : %s" % (Expr, Result)) | |
return Result | |
def ValueToByteArray(self, ValueStr, Length): | |
Match = re.match("\\{\\s*FILE:(.+)\\}", ValueStr) | |
if Match: | |
FileList = Match.group(1).split(',') | |
Result = bytearray() | |
for File in FileList: | |
File = File.strip() | |
BinPath = os.path.join(os.path.dirname(self._DscFile), File) | |
Result.extend(bytearray(open(BinPath, 'rb').read())) | |
else: | |
try: | |
Result = bytearray(self.ValueToList(ValueStr, Length)) | |
except ValueError: | |
raise Exception("Bytes in '%s' must be in range 0~255 !" % | |
ValueStr) | |
if len(Result) < Length: | |
Result.extend(b'\x00' * (Length - len(Result))) | |
elif len(Result) > Length: | |
raise Exception("Value '%s' is too big to fit into %d bytes !" % | |
(ValueStr, Length)) | |
return Result[:Length] | |
def ValueToList(self, ValueStr, Length): | |
if ValueStr[0] == '{': | |
Result = [] | |
BinList = ValueStr[1:-1].split(',') | |
InBitField = False | |
LastInBitField = False | |
Value = 0 | |
BitLen = 0 | |
for Element in BinList: | |
InBitField = False | |
Each = Element.strip() | |
if len(Each) == 0: | |
pass | |
else: | |
if Each[0] in ['"', "'"]: | |
Result.extend(list(bytearray(Each[1:-1], 'utf-8'))) | |
elif ':' in Each: | |
Match = re.match("(.+):(\\d+)b", Each) | |
if Match is None: | |
raise Exception("Invald value list format '%s' !" | |
% Each) | |
InBitField = True | |
CurrentBitLen = int(Match.group(2)) | |
CurrentValue = ((self.EvaluateExpress(Match.group(1)) | |
& (1 << CurrentBitLen) - 1)) << BitLen | |
else: | |
Result.append(self.EvaluateExpress(Each.strip())) | |
if InBitField: | |
Value += CurrentValue | |
BitLen += CurrentBitLen | |
if LastInBitField and ((not InBitField) or (Element == | |
BinList[-1])): | |
if BitLen % 8 != 0: | |
raise Exception("Invald bit field length!") | |
Result.extend(Val2Bytes(Value, BitLen // 8)) | |
Value = 0 | |
BitLen = 0 | |
LastInBitField = InBitField | |
elif ValueStr.startswith("'") and ValueStr.endswith("'"): | |
Result = Str2Bytes(ValueStr, Length) | |
elif ValueStr.startswith('"') and ValueStr.endswith('"'): | |
Result = Str2Bytes(ValueStr, Length) | |
else: | |
Result = Val2Bytes(self.EvaluateExpress(ValueStr), Length) | |
return Result | |
def FormatDeltaValue(self, ConfigDict): | |
ValStr = ConfigDict['value'] | |
if ValStr[0] == "'": | |
# Remove padding \x00 in the value string | |
ValStr = "'%s'" % ValStr[1:-1].rstrip('\x00') | |
Struct = ConfigDict['struct'] | |
if Struct in self._StructType: | |
# Format the array using its struct type | |
Unit = int(Struct[4:]) // 8 | |
Value = Array2Val(ConfigDict['value']) | |
Loop = ConfigDict['length'] // Unit | |
Values = [] | |
for Each in range(Loop): | |
Values.append(Value & ((1 << (Unit * 8)) - 1)) | |
Value = Value >> (Unit * 8) | |
ValStr = '{ ' + ', '.join([('0x%%0%dX' % (Unit * 2)) % | |
x for x in Values]) + ' }' | |
return ValStr | |
def FormatListValue(self, ConfigDict): | |
Struct = ConfigDict['struct'] | |
if Struct not in self._StructType: | |
return | |
DataList = self.ValueToList(ConfigDict['value'], ConfigDict['length']) | |
Unit = int(Struct[4:]) // 8 | |
if int(ConfigDict['length']) != Unit * len(DataList): | |
# Fallback to byte array | |
Unit = 1 | |
if int(ConfigDict['length']) != len(DataList): | |
raise Exception("Array size is not proper for '%s' !" % | |
ConfigDict['cname']) | |
ByteArray = [] | |
for Value in DataList: | |
for Loop in range(Unit): | |
ByteArray.append("0x%02X" % (Value & 0xFF)) | |
Value = Value >> 8 | |
NewValue = '{' + ','.join(ByteArray) + '}' | |
ConfigDict['value'] = NewValue | |
return "" | |
def GetOrderNumber(self, Offset, Order, BitOff=0): | |
if isinstance(Order, int): | |
if Order == -1: | |
Order = Offset << 16 | |
else: | |
(Major, Minor) = Order.split('.') | |
Order = (int(Major, 16) << 16) + ((int(Minor, 16) & 0xFF) << 8) | |
return Order + (BitOff & 0xFF) | |
def SubtituteLine(self, Line, Args): | |
Args = Args.strip() | |
Vars = Args.split(':') | |
Line = self.ExpandMacros(Line, True) | |
for Idx in range(len(Vars)-1, 0, -1): | |
Line = Line.replace('$(%d)' % Idx, Vars[Idx].strip()) | |
return Line | |
def CfgDuplicationCheck(self, CfgDict, Name): | |
if not self.Debug: | |
return | |
if Name == 'Dummy': | |
return | |
if Name not in CfgDict: | |
CfgDict[Name] = 1 | |
else: | |
print("WARNING: Duplicated item found '%s' !" % | |
CfgDict['cname']) | |
def AddBsfChildPage(self, Child, Parent='root'): | |
def AddBsfChildPageRecursive(PageTree, Parent, Child): | |
Key = next(iter(PageTree)) | |
if Parent == Key: | |
PageTree[Key].append({Child: []}) | |
return True | |
else: | |
Result = False | |
for Each in PageTree[Key]: | |
if AddBsfChildPageRecursive(Each, Parent, Child): | |
Result = True | |
break | |
return Result | |
return AddBsfChildPageRecursive(self._CfgPageTree, Parent, Child) | |
def ParseDscFile(self, DscFile): | |
self._DscLines = [] | |
self._CfgItemList = [] | |
self._CfgPageDict = {} | |
self._CfgBlkDict = {} | |
self._BsfTempDict = {} | |
self._CfgPageTree = {'root': []} | |
CfgDict = {} | |
SectionNameList = ["Defines".lower(), "PcdsFeatureFlag".lower(), | |
"PcdsDynamicVpd.Tmp".lower(), | |
"PcdsDynamicVpd.Upd".lower()] | |
IsDefSect = False | |
IsPcdSect = False | |
IsUpdSect = False | |
IsTmpSect = False | |
TemplateName = '' | |
IfStack = [] | |
ElifStack = [] | |
Error = 0 | |
ConfigDict = {} | |
if type(DscFile) is list: | |
# it is DSC lines already | |
DscLines = DscFile | |
self._DscFile = '.' | |
else: | |
DscFd = open(DscFile, "r") | |
DscLines = DscFd.readlines() | |
DscFd.close() | |
self._DscFile = DscFile | |
BsfRegExp = re.compile("(%s):{(.+?)}(?:$|\\s+)" % '|'. | |
join(self._BsfKeyList)) | |
HdrRegExp = re.compile("(%s):{(.+?)}" % '|'.join(self._HdrKeyList)) | |
CfgRegExp = re.compile("^([_a-zA-Z0-9]+)\\s*\\|\\s*\ | |
(0x[0-9A-F]+|\\*)\\s*\\|\\s*(\\d+|0x[0-9a-fA-F]+)\\s*\\|\\s*(.+)") | |
TksRegExp = re.compile("^(g[_a-zA-Z0-9]+\\.)(.+)") | |
SkipLines = 0 | |
while len(DscLines): | |
DscLine = DscLines.pop(0).strip() | |
if SkipLines == 0: | |
self._DscLines.append(DscLine) | |
else: | |
SkipLines = SkipLines - 1 | |
if len(DscLine) == 0: | |
continue | |
Handle = False | |
Match = re.match("^\\[(.+)\\]", DscLine) | |
if Match is not None: | |
IsDefSect = False | |
IsPcdSect = False | |
IsUpdSect = False | |
IsTmpSect = False | |
SectionName = Match.group(1).lower() | |
if SectionName == SectionNameList[0]: | |
IsDefSect = True | |
if SectionName == SectionNameList[1]: | |
IsPcdSect = True | |
elif SectionName == SectionNameList[2]: | |
IsTmpSect = True | |
elif SectionName == SectionNameList[3]: | |
ConfigDict = { | |
'header': 'ON', | |
'page': '', | |
'name': '', | |
'find': '', | |
'struct': '', | |
'embed': '', | |
'marker': '', | |
'option': '', | |
'comment': '', | |
'condition': '', | |
'order': -1, | |
'subreg': [] | |
} | |
IsUpdSect = True | |
Offset = 0 | |
else: | |
if IsDefSect or IsPcdSect or IsUpdSect or IsTmpSect: | |
Match = False if DscLine[0] != '!' else True | |
if Match: | |
Match = re.match("^!(else|endif|ifdef|ifndef|if|elseif\ | |
|include)\\s*(.+)?$", DscLine.split("#")[0]) | |
Keyword = Match.group(1) if Match else '' | |
Remaining = Match.group(2) if Match else '' | |
Remaining = '' if Remaining is None else Remaining.strip() | |
if Keyword in ['if', 'elseif', 'ifdef', 'ifndef', 'include' | |
] and not Remaining: | |
raise Exception("ERROR: Expression is expected after \ | |
'!if' or !elseif' for line '%s'" % DscLine) | |
if Keyword == 'else': | |
if IfStack: | |
IfStack[-1] = not IfStack[-1] | |
else: | |
raise Exception("ERROR: No paired '!if' found for \ | |
'!else' for line '%s'" % DscLine) | |
elif Keyword == 'endif': | |
if IfStack: | |
IfStack.pop() | |
Level = ElifStack.pop() | |
if Level > 0: | |
del IfStack[-Level:] | |
else: | |
raise Exception("ERROR: No paired '!if' found for \ | |
'!endif' for line '%s'" % DscLine) | |
elif Keyword == 'ifdef' or Keyword == 'ifndef': | |
Result = self.EvaulateIfdef(Remaining) | |
if Keyword == 'ifndef': | |
Result = not Result | |
IfStack.append(Result) | |
ElifStack.append(0) | |
elif Keyword == 'if' or Keyword == 'elseif': | |
Result = self.EvaluateExpress(Remaining) | |
if Keyword == "if": | |
ElifStack.append(0) | |
IfStack.append(Result) | |
else: # elseif | |
if IfStack: | |
IfStack[-1] = not IfStack[-1] | |
IfStack.append(Result) | |
ElifStack[-1] = ElifStack[-1] + 1 | |
else: | |
raise Exception("ERROR: No paired '!if' found for \ | |
'!elif' for line '%s'" % DscLine) | |
else: | |
if IfStack: | |
Handle = reduce(lambda x, y: x and y, IfStack) | |
else: | |
Handle = True | |
if Handle: | |
if Keyword == 'include': | |
Remaining = self.ExpandMacros(Remaining) | |
# Relative to DSC filepath | |
IncludeFilePath = os.path.join( | |
os.path.dirname(self._DscFile), Remaining) | |
if not os.path.exists(IncludeFilePath): | |
# Relative to repository to find \ | |
# dsc in common platform | |
IncludeFilePath = os.path.join( | |
os.path.dirname(self._DscFile), "..", | |
Remaining) | |
try: | |
IncludeDsc = open(IncludeFilePath, "r") | |
except Exception: | |
raise Exception("ERROR: Cannot open \ | |
file '%s'." % IncludeFilePath) | |
NewDscLines = IncludeDsc.readlines() | |
IncludeDsc.close() | |
DscLines = NewDscLines + DscLines | |
del self._DscLines[-1] | |
else: | |
if DscLine.startswith('!'): | |
raise Exception("ERROR: Unrecoginized \ | |
directive for line '%s'" % DscLine) | |
if not Handle: | |
del self._DscLines[-1] | |
continue | |
if IsDefSect: | |
Match = re.match("^\\s*(?:DEFINE\\s+)*(\\w+)\\s*=\\s*(.+)", | |
DscLine) | |
if Match: | |
self._MacroDict[Match.group(1)] = Match.group(2) | |
if self.Debug: | |
print("INFO : DEFINE %s = [ %s ]" % (Match.group(1), | |
Match.group(2))) | |
elif IsPcdSect: | |
Match = re.match("^\\s*([\\w\\.]+)\\s*\\|\\s*(\\w+)", DscLine) | |
if Match: | |
self._PcdsDict[Match.group(1)] = Match.group(2) | |
if self.Debug: | |
print("INFO : PCD %s = [ %s ]" % (Match.group(1), | |
Match.group(2))) | |
elif IsTmpSect: | |
# !BSF DEFT:{GPIO_TMPL:START} | |
Match = re.match("^\\s*#\\s+(!BSF)\\s+DEFT:{(.+?):\ | |
(START|END)}", DscLine) | |
if Match: | |
if Match.group(3) == 'START' and not TemplateName: | |
TemplateName = Match.group(2).strip() | |
self._BsfTempDict[TemplateName] = [] | |
if Match.group(3) == 'END' and ( | |
TemplateName == Match.group(2).strip() | |
) and TemplateName: | |
TemplateName = '' | |
else: | |
if TemplateName: | |
Match = re.match("^!include\\s*(.+)?$", DscLine) | |
if Match: | |
continue | |
self._BsfTempDict[TemplateName].append(DscLine) | |
else: | |
Match = re.match("^\\s*#\\s+(!BSF|!HDR)\\s+(.+)", DscLine) | |
if Match: | |
Remaining = Match.group(2) | |
if Match.group(1) == '!BSF': | |
Result = BsfRegExp.findall(Remaining) | |
if Result: | |
for Each in Result: | |
Key = Each[0] | |
Remaining = Each[1] | |
if Key == 'BLOCK': | |
Match = re.match( | |
"NAME:\"(.+)\"\\s*,\\s*\ | |
VER:\"(.+)\"\\s*", Remaining) | |
if Match: | |
self._CfgBlkDict['name'] = \ | |
Match.group(1) | |
self._CfgBlkDict['ver'] = Match.group(2 | |
) | |
elif Key == 'SUBT': | |
# GPIO_TMPL:1:2:3 | |
Remaining = Remaining.strip() | |
Match = re.match("(\\w+)\\s*:", Remaining) | |
if Match: | |
TemplateName = Match.group(1) | |
for Line in self._BsfTempDict[ | |
TemplateName][::-1]: | |
NewLine = self.SubtituteLine( | |
Line, Remaining) | |
DscLines.insert(0, NewLine) | |
SkipLines += 1 | |
elif Key == 'PAGES': | |
# !BSF PAGES:{HSW:"Haswell System Agent", \ | |
# LPT:"Lynx Point PCH"} | |
PageList = Remaining.split(',') | |
for Page in PageList: | |
Page = Page.strip() | |
Match = re.match('(\\w+):\ | |
(\\w*:)?\\"(.+)\\"', Page) | |
if Match: | |
PageName = Match.group(1) | |
ParentName = Match.group(2) | |
if not ParentName or \ | |
ParentName == ':': | |
ParentName = 'root' | |
else: | |
ParentName = ParentName[:-1] | |
if not self.AddBsfChildPage( | |
PageName, ParentName): | |
raise Exception("Cannot find \ | |
parent page '%s'!" % ParentName) | |
self._CfgPageDict[ | |
PageName] = Match.group(3) | |
else: | |
raise Exception("Invalid page \ | |
definitions '%s'!" % Page) | |
elif Key in ['NAME', 'HELP', 'OPTION' | |
] and Remaining.startswith('+'): | |
# Allow certain options to be extended \ | |
# to multiple lines | |
ConfigDict[Key.lower()] += Remaining[1:] | |
else: | |
if Key == 'NAME': | |
Remaining = Remaining.strip() | |
elif Key == 'CONDITION': | |
Remaining = self.ExpandMacros( | |
Remaining.strip()) | |
ConfigDict[Key.lower()] = Remaining | |
else: | |
Match = HdrRegExp.match(Remaining) | |
if Match: | |
Key = Match.group(1) | |
Remaining = Match.group(2) | |
if Key == 'EMBED': | |
Parts = Remaining.split(':') | |
Names = Parts[0].split(',') | |
DummyDict = ConfigDict.copy() | |
if len(Names) > 1: | |
Remaining = Names[0] + ':' + ':'.join( | |
Parts[1:]) | |
DummyDict['struct'] = Names[1] | |
else: | |
DummyDict['struct'] = Names[0] | |
DummyDict['cname'] = 'Dummy' | |
DummyDict['name'] = '' | |
DummyDict['embed'] = Remaining | |
DummyDict['offset'] = Offset | |
DummyDict['length'] = 0 | |
DummyDict['value'] = '0' | |
DummyDict['type'] = 'Reserved' | |
DummyDict['help'] = '' | |
DummyDict['subreg'] = [] | |
self._CfgItemList.append(DummyDict) | |
else: | |
ConfigDict[Key.lower()] = Remaining | |
# Check CFG line | |
# gCfgData.VariableName | * | 0x01 | 0x1 | |
Clear = False | |
Match = TksRegExp.match(DscLine) | |
if Match: | |
DscLine = 'gCfgData.%s' % Match.group(2) | |
if DscLine.startswith('gCfgData.'): | |
Match = CfgRegExp.match(DscLine[9:]) | |
else: | |
Match = None | |
if Match: | |
ConfigDict['space'] = 'gCfgData' | |
ConfigDict['cname'] = Match.group(1) | |
if Match.group(2) != '*': | |
Offset = int(Match.group(2), 16) | |
ConfigDict['offset'] = Offset | |
ConfigDict['order'] = self.GetOrderNumber( | |
ConfigDict['offset'], ConfigDict['order']) | |
Value = Match.group(4).strip() | |
if Match.group(3).startswith("0x"): | |
Length = int(Match.group(3), 16) | |
else: | |
Length = int(Match.group(3)) | |
Offset += Length | |
ConfigDict['length'] = Length | |
Match = re.match("\\$\\((\\w+)\\)", Value) | |
if Match: | |
if Match.group(1) in self._MacroDict: | |
Value = self._MacroDict[Match.group(1)] | |
ConfigDict['value'] = Value | |
if re.match("\\{\\s*FILE:(.+)\\}", Value): | |
# Expand embedded binary file | |
ValArray = self.ValueToByteArray(ConfigDict['value'], | |
ConfigDict['length']) | |
NewValue = Bytes2Str(ValArray) | |
self._DscLines[-1] = re.sub(r'(.*)(\{\s*FILE:.+\})', | |
r'\1 %s' % NewValue, | |
self._DscLines[-1]) | |
ConfigDict['value'] = NewValue | |
if ConfigDict['name'] == '': | |
# Clear BSF specific items | |
ConfigDict['bsfname'] = '' | |
ConfigDict['help'] = '' | |
ConfigDict['type'] = '' | |
ConfigDict['option'] = '' | |
self.CfgDuplicationCheck(CfgDict, ConfigDict['cname']) | |
self._CfgItemList.append(ConfigDict.copy()) | |
Clear = True | |
else: | |
# It could be a virtual item as below | |
# !BSF FIELD:{SerialDebugPortAddress0:1} | |
# or | |
# @Bsf FIELD:{SerialDebugPortAddress0:1b} | |
Match = re.match(r"^\s*#\s+(!BSF)\s+FIELD:{(.+)}", DscLine) | |
if Match: | |
BitFieldTxt = Match.group(2) | |
Match = re.match("(.+):(\\d+)b([BWDQ])?", BitFieldTxt) | |
if not Match: | |
raise Exception("Incorrect bit field \ | |
format '%s' !" % BitFieldTxt) | |
UnitBitLen = 1 | |
SubCfgDict = ConfigDict.copy() | |
SubCfgDict['cname'] = Match.group(1) | |
SubCfgDict['bitlength'] = int( | |
Match.group(2)) * UnitBitLen | |
if SubCfgDict['bitlength'] > 0: | |
LastItem = self._CfgItemList[-1] | |
if len(LastItem['subreg']) == 0: | |
SubOffset = 0 | |
else: | |
SubOffset = \ | |
LastItem['subreg'][-1]['bitoffset'] \ | |
+ LastItem['subreg'][-1]['bitlength'] | |
if Match.group(3) == 'B': | |
SubCfgDict['bitunit'] = 1 | |
elif Match.group(3) == 'W': | |
SubCfgDict['bitunit'] = 2 | |
elif Match.group(3) == 'Q': | |
SubCfgDict['bitunit'] = 8 | |
else: | |
SubCfgDict['bitunit'] = 4 | |
SubCfgDict['bitoffset'] = SubOffset | |
SubCfgDict['order'] = self.GetOrderNumber( | |
SubCfgDict['offset'], SubCfgDict['order'], | |
SubOffset) | |
SubCfgDict['value'] = '' | |
SubCfgDict['cname'] = '%s_%s' % (LastItem['cname'], | |
Match.group(1)) | |
self.CfgDuplicationCheck(CfgDict, | |
SubCfgDict['cname']) | |
LastItem['subreg'].append(SubCfgDict.copy()) | |
Clear = True | |
if Clear: | |
ConfigDict['name'] = '' | |
ConfigDict['find'] = '' | |
ConfigDict['struct'] = '' | |
ConfigDict['embed'] = '' | |
ConfigDict['marker'] = '' | |
ConfigDict['comment'] = '' | |
ConfigDict['order'] = -1 | |
ConfigDict['subreg'] = [] | |
ConfigDict['option'] = '' | |
ConfigDict['condition'] = '' | |
return Error | |
def GetBsfBitFields(self, subitem, bytes): | |
start = subitem['bitoffset'] | |
end = start + subitem['bitlength'] | |
bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1]) | |
bitsvalue = bitsvalue[::-1] | |
bitslen = len(bitsvalue) | |
if start > bitslen or end > bitslen: | |
raise Exception("Invalid bits offset [%d,%d] %d for %s" % | |
(start, end, bitslen, subitem['name'])) | |
return '0x%X' % (int(bitsvalue[start:end][::-1], 2)) | |
def UpdateBsfBitFields(self, SubItem, NewValue, ValueArray): | |
Start = SubItem['bitoffset'] | |
End = Start + SubItem['bitlength'] | |
Blen = len(ValueArray) | |
BitsValue = ''.join('{0:08b}'.format(i) for i in ValueArray[::-1]) | |
BitsValue = BitsValue[::-1] | |
BitsLen = len(BitsValue) | |
if Start > BitsLen or End > BitsLen: | |
raise Exception("Invalid bits offset [%d,%d] %d for %s" % | |
(Start, End, BitsLen, SubItem['name'])) | |
BitsValue = BitsValue[:Start] + '{0:0{1}b}'.format( | |
NewValue, SubItem['bitlength'])[::-1] + BitsValue[End:] | |
ValueArray[:] = bytearray.fromhex( | |
'{0:0{1}x}'.format(int(BitsValue[::-1], 2), Blen * 2))[::-1] | |
def CreateVarDict(self): | |
Error = 0 | |
self._VarDict = {} | |
if len(self._CfgItemList) > 0: | |
Item = self._CfgItemList[-1] | |
self._VarDict['_LENGTH_'] = '%d' % (Item['offset'] + | |
Item['length']) | |
for Item in self._CfgItemList: | |
Embed = Item['embed'] | |
Match = re.match("^(\\w+):(\\w+):(START|END)", Embed) | |
if Match: | |
StructName = Match.group(1) | |
VarName = '_%s_%s_' % (Match.group(3), StructName) | |
if Match.group(3) == 'END': | |
self._VarDict[VarName] = Item['offset'] + Item['length'] | |
self._VarDict['_LENGTH_%s_' % StructName] = \ | |
self._VarDict['_END_%s_' % StructName] - \ | |
self._VarDict['_START_%s_' % StructName] | |
if Match.group(2).startswith('TAG_'): | |
if (self.Mode != 'FSP') and (self._VarDict | |
['_LENGTH_%s_' % | |
StructName] % 4): | |
raise Exception("Size of structure '%s' is %d, \ | |
not DWORD aligned !" % (StructName, self._VarDict['_LENGTH_%s_' % StructName])) | |
self._VarDict['_TAG_%s_' % StructName] = int( | |
Match.group(2)[4:], 16) & 0xFFF | |
else: | |
self._VarDict[VarName] = Item['offset'] | |
if Item['marker']: | |
self._VarDict['_OFFSET_%s_' % Item['marker'].strip()] = \ | |
Item['offset'] | |
return Error | |
def UpdateBsfBitUnit(self, Item): | |
BitTotal = 0 | |
BitOffset = 0 | |
StartIdx = 0 | |
Unit = None | |
UnitDec = {1: 'BYTE', 2: 'WORD', 4: 'DWORD', 8: 'QWORD'} | |
for Idx, SubItem in enumerate(Item['subreg']): | |
if Unit is None: | |
Unit = SubItem['bitunit'] | |
BitLength = SubItem['bitlength'] | |
BitTotal += BitLength | |
BitOffset += BitLength | |
if BitOffset > 64 or BitOffset > Unit * 8: | |
break | |
if BitOffset == Unit * 8: | |
for SubIdx in range(StartIdx, Idx + 1): | |
Item['subreg'][SubIdx]['bitunit'] = Unit | |
BitOffset = 0 | |
StartIdx = Idx + 1 | |
Unit = None | |
if BitOffset > 0: | |
raise Exception("Bit fields cannot fit into %s for \ | |
'%s.%s' !" % (UnitDec[Unit], Item['cname'], SubItem['cname'])) | |
ExpectedTotal = Item['length'] * 8 | |
if Item['length'] * 8 != BitTotal: | |
raise Exception("Bit fields total length (%d) does not match \ | |
length (%d) of '%s' !" % (BitTotal, ExpectedTotal, Item['cname'])) | |
def UpdateDefaultValue(self): | |
Error = 0 | |
for Idx, Item in enumerate(self._CfgItemList): | |
if len(Item['subreg']) == 0: | |
Value = Item['value'] | |
if (len(Value) > 0) and (Value[0] == '{' or Value[0] == "'" or | |
Value[0] == '"'): | |
# {XXX} or 'XXX' strings | |
self.FormatListValue(self._CfgItemList[Idx]) | |
else: | |
Match = re.match("(0x[0-9a-fA-F]+|[0-9]+)", Value) | |
if not Match: | |
NumValue = self.EvaluateExpress(Value) | |
Item['value'] = '0x%X' % NumValue | |
else: | |
ValArray = self.ValueToByteArray(Item['value'], Item['length']) | |
for SubItem in Item['subreg']: | |
SubItem['value'] = self.GetBsfBitFields(SubItem, ValArray) | |
self.UpdateBsfBitUnit(Item) | |
return Error | |
@staticmethod | |
def ExpandIncludeFiles(FilePath, CurDir=''): | |
if CurDir == '': | |
CurDir = os.path.dirname(FilePath) | |
FilePath = os.path.basename(FilePath) | |
InputFilePath = os.path.join(CurDir, FilePath) | |
File = open(InputFilePath, "r") | |
Lines = File.readlines() | |
File.close() | |
NewLines = [] | |
for LineNum, Line in enumerate(Lines): | |
Match = re.match("^!include\\s*(.+)?$", Line) | |
if Match: | |
IncPath = Match.group(1) | |
TmpPath = os.path.join(CurDir, IncPath) | |
OrgPath = TmpPath | |
if not os.path.exists(TmpPath): | |
CurDir = os.path.join(os.path.dirname( | |
os.path.realpath(__file__)), "..", "..") | |
TmpPath = os.path.join(CurDir, IncPath) | |
if not os.path.exists(TmpPath): | |
raise Exception("ERROR: Cannot open include file '%s'." % | |
OrgPath) | |
else: | |
NewLines.append(('# Included from file: %s\n' % | |
IncPath, TmpPath, 0)) | |
NewLines.append(('# %s\n' % ('=' * 80), TmpPath, 0)) | |
NewLines.extend(CGenCfgData.ExpandIncludeFiles | |
(IncPath, CurDir)) | |
else: | |
NewLines.append((Line, InputFilePath, LineNum)) | |
return NewLines | |
def OverrideDefaultValue(self, DltFile): | |
Error = 0 | |
DltLines = CGenCfgData.ExpandIncludeFiles(DltFile) | |
PlatformId = None | |
for Line, FilePath, LineNum in DltLines: | |
Line = Line.strip() | |
if not Line or Line.startswith('#'): | |
continue | |
Match = re.match("\\s*(\\w+)\\.(\\w+)(\\.\\w+)?\\s*\\|\\s*(.+)", | |
Line) | |
if not Match: | |
raise Exception("Unrecognized line '%s' (File:'%s' Line:%d) !" | |
% (Line, FilePath, LineNum + 1)) | |
Found = False | |
InScope = False | |
for Idx, Item in enumerate(self._CfgItemList): | |
if not InScope: | |
if not (Item['embed'].endswith(':START') and | |
Item['embed'].startswith(Match.group(1))): | |
continue | |
InScope = True | |
if Item['cname'] == Match.group(2): | |
Found = True | |
break | |
if Item['embed'].endswith(':END') and \ | |
Item['embed'].startswith(Match.group(1)): | |
break | |
Name = '%s.%s' % (Match.group(1), Match.group(2)) | |
if not Found: | |
ErrItem = Match.group(2) if InScope else Match.group(1) | |
raise Exception("Invalid configuration '%s' in '%s' \ | |
(File:'%s' Line:%d) !" % (ErrItem, Name, FilePath, LineNum + 1)) | |
ValueStr = Match.group(4).strip() | |
if Match.group(3) is not None: | |
# This is a subregion item | |
BitField = Match.group(3)[1:] | |
Found = False | |
if len(Item['subreg']) > 0: | |
for SubItem in Item['subreg']: | |
if SubItem['cname'] == '%s_%s' % \ | |
(Item['cname'], BitField): | |
Found = True | |
break | |
if not Found: | |
raise Exception("Invalid configuration bit field \ | |
'%s' in '%s.%s' (File:'%s' Line:%d) !" % (BitField, Name, BitField, | |
FilePath, LineNum + 1)) | |
try: | |
Value = int(ValueStr, 16) if ValueStr.startswith('0x') \ | |
else int(ValueStr, 10) | |
except Exception: | |
raise Exception("Invalid value '%s' for bit field '%s.%s' \ | |
(File:'%s' Line:%d) !" % (ValueStr, Name, BitField, FilePath, LineNum + 1)) | |
if Value >= 2 ** SubItem['bitlength']: | |
raise Exception("Invalid configuration bit field value \ | |
'%s' for '%s.%s' (File:'%s' Line:%d) !" % (Value, Name, BitField, | |
FilePath, LineNum + 1)) | |
ValArray = self.ValueToByteArray(Item['value'], Item['length']) | |
self.UpdateBsfBitFields(SubItem, Value, ValArray) | |
if Item['value'].startswith('{'): | |
Item['value'] = '{' + ', '.join('0x%02X' % i | |
for i in ValArray) + '}' | |
else: | |
BitsValue = ''.join('{0:08b}'.format(i) | |
for i in ValArray[::-1]) | |
Item['value'] = '0x%X' % (int(BitsValue, 2)) | |
else: | |
if Item['value'].startswith('{') and \ | |
not ValueStr.startswith('{'): | |
raise Exception("Data array required for '%s' \ | |
(File:'%s' Line:%d) !" % (Name, FilePath, LineNum + 1)) | |
Item['value'] = ValueStr | |
if Name == 'PLATFORMID_CFG_DATA.PlatformId': | |
PlatformId = ValueStr | |
if (PlatformId is None) and (self.Mode != 'FSP'): | |
raise Exception("PLATFORMID_CFG_DATA.PlatformId is missing \ | |
in file '%s' !" % (DltFile)) | |
return Error | |
def ProcessMultilines(self, String, MaxCharLength): | |
Multilines = '' | |
StringLength = len(String) | |
CurrentStringStart = 0 | |
StringOffset = 0 | |
BreakLineDict = [] | |
if len(String) <= MaxCharLength: | |
while (StringOffset < StringLength): | |
if StringOffset >= 1: | |
if String[StringOffset - 1] == '\\' and \ | |
String[StringOffset] == 'n': | |
BreakLineDict.append(StringOffset + 1) | |
StringOffset += 1 | |
if BreakLineDict != []: | |
for Each in BreakLineDict: | |
Multilines += " %s\n" % String[CurrentStringStart:Each].\ | |
lstrip() | |
CurrentStringStart = Each | |
if StringLength - CurrentStringStart > 0: | |
Multilines += " %s\n" % String[CurrentStringStart:].\ | |
lstrip() | |
else: | |
Multilines = " %s\n" % String | |
else: | |
NewLineStart = 0 | |
NewLineCount = 0 | |
FoundSpaceChar = False | |
while(StringOffset < StringLength): | |
if StringOffset >= 1: | |
if NewLineCount >= MaxCharLength - 1: | |
if String[StringOffset] == ' ' and \ | |
StringLength - StringOffset > 10: | |
BreakLineDict.append(NewLineStart + NewLineCount) | |
NewLineStart = NewLineStart + NewLineCount | |
NewLineCount = 0 | |
FoundSpaceChar = True | |
elif StringOffset == StringLength - 1 \ | |
and FoundSpaceChar is False: | |
BreakLineDict.append(0) | |
if String[StringOffset - 1] == '\\' and \ | |
String[StringOffset] == 'n': | |
BreakLineDict.append(StringOffset + 1) | |
NewLineStart = StringOffset + 1 | |
NewLineCount = 0 | |
StringOffset += 1 | |
NewLineCount += 1 | |
if BreakLineDict != []: | |
BreakLineDict.sort() | |
for Each in BreakLineDict: | |
if Each > 0: | |
Multilines += " %s\n" % String[ | |
CurrentStringStart:Each].lstrip() | |
CurrentStringStart = Each | |
if StringLength - CurrentStringStart > 0: | |
Multilines += " %s\n" % String[CurrentStringStart:].\ | |
lstrip() | |
return Multilines | |
def CreateField(self, Item, Name, Length, Offset, Struct, | |
BsfName, Help, Option, BitsLength=None): | |
PosName = 28 | |
NameLine = '' | |
HelpLine = '' | |
OptionLine = '' | |
if Length == 0 and Name == 'Dummy': | |
return '\n' | |
IsArray = False | |
if Length in [1, 2, 4, 8]: | |
Type = "UINT%d" % (Length * 8) | |
else: | |
IsArray = True | |
Type = "UINT8" | |
if Item and Item['value'].startswith('{'): | |
Type = "UINT8" | |
IsArray = True | |
if Struct != '': | |
Type = Struct | |
if Struct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']: | |
IsArray = True | |
Unit = int(Type[4:]) // 8 | |
Length = Length / Unit | |
else: | |
IsArray = False | |
if IsArray: | |
Name = Name + '[%d]' % Length | |
if len(Type) < PosName: | |
Space1 = PosName - len(Type) | |
else: | |
Space1 = 1 | |
if BsfName != '': | |
NameLine = " %s\n" % BsfName | |
else: | |
NameLine = "\n" | |
if Help != '': | |
HelpLine = self.ProcessMultilines(Help, 80) | |
if Option != '': | |
OptionLine = self.ProcessMultilines(Option, 80) | |
if BitsLength is None: | |
BitsLength = '' | |
else: | |
BitsLength = ' : %d' % BitsLength | |
return "\n/** %s%s%s**/\n %s%s%s%s;\n" % \ | |
(NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name, | |
BitsLength) | |
def SplitTextBody(self, TextBody): | |
Marker1 = '{ /* _COMMON_STRUCT_START_ */' | |
Marker2 = '; /* _COMMON_STRUCT_END_ */' | |
ComBody = [] | |
TxtBody = [] | |
IsCommon = False | |
for Line in TextBody: | |
if Line.strip().endswith(Marker1): | |
Line = Line.replace(Marker1[1:], '') | |
IsCommon = True | |
if Line.strip().endswith(Marker2): | |
Line = Line.replace(Marker2[1:], '') | |
if IsCommon: | |
ComBody.append(Line) | |
IsCommon = False | |
continue | |
if IsCommon: | |
ComBody.append(Line) | |
else: | |
TxtBody.append(Line) | |
return ComBody, TxtBody | |
def GetStructArrayInfo(self, Input): | |
ArrayStr = Input.split('[') | |
Name = ArrayStr[0] | |
if len(ArrayStr) > 1: | |
NumStr = ''.join(c for c in ArrayStr[-1] if c.isdigit()) | |
NumStr = '1000' if len(NumStr) == 0 else NumStr | |
ArrayNum = int(NumStr) | |
else: | |
ArrayNum = 0 | |
return Name, ArrayNum | |
def PostProcessBody(self, TextBody, IncludeEmbedOnly=True): | |
NewTextBody = [] | |
OldTextBody = [] | |
IncTextBody = [] | |
StructBody = [] | |
IncludeLine = False | |
EmbedFound = False | |
StructName = '' | |
ArrayVarName = '' | |
VariableName = '' | |
Count = 0 | |
Level = 0 | |
IsCommonStruct = False | |
for Line in TextBody: | |
if Line.startswith('#define '): | |
IncTextBody.append(Line) | |
continue | |
if not Line.startswith('/* EMBED_STRUCT:'): | |
Match = False | |
else: | |
Match = re.match("^/\\*\\sEMBED_STRUCT:([\\w\\[\\]\\*]+):\ | |
([\\w\\[\\]\\*]+):(\\w+):(START|END)([\\s\\d]+)\\*/([\\s\\S]*)", Line) | |
if Match: | |
ArrayMarker = Match.group(5) | |
if Match.group(4) == 'END': | |
Level -= 1 | |
if Level == 0: | |
Line = Match.group(6) | |
else: # 'START' | |
Level += 1 | |
if Level == 1: | |
Line = Match.group(6) | |
else: | |
EmbedFound = True | |
TagStr = Match.group(3) | |
if TagStr.startswith('TAG_'): | |
try: | |
TagVal = int(TagStr[4:], 16) | |
except Exception: | |
TagVal = -1 | |
if (TagVal >= 0) and (TagVal < self._MinCfgTagId): | |
IsCommonStruct = True | |
if Level == 1: | |
if IsCommonStruct: | |
Suffix = ' /* _COMMON_STRUCT_START_ */' | |
else: | |
Suffix = '' | |
StructBody = ['typedef struct {%s' % Suffix] | |
StructName = Match.group(1) | |
StructType = Match.group(2) | |
VariableName = Match.group(3) | |
MatchOffset = re.search('/\\*\\*\\sOffset\\s0x\ | |
([a-fA-F0-9]+)', Line) | |
if MatchOffset: | |
Offset = int(MatchOffset.group(1), 16) | |
else: | |
Offset = None | |
IncludeLine = True | |
ModifiedStructType = StructType.rstrip() | |
if ModifiedStructType.endswith(']'): | |
Idx = ModifiedStructType.index('[') | |
if ArrayMarker != ' ': | |
# Auto array size | |
OldTextBody.append('') | |
ArrayVarName = VariableName | |
if int(ArrayMarker) == 1000: | |
Count = 1 | |
else: | |
Count = int(ArrayMarker) + 1000 | |
else: | |
if Count < 1000: | |
Count += 1 | |
VariableTemp = ArrayVarName + '[%d]' % ( | |
Count if Count < 1000 else Count - 1000) | |
OldTextBody[-1] = self.CreateField( | |
None, VariableTemp, 0, Offset, | |
ModifiedStructType[:Idx], '', | |
'Structure Array', '') | |
else: | |
ArrayVarName = '' | |
OldTextBody.append(self.CreateField( | |
None, VariableName, 0, Offset, | |
ModifiedStructType, '', '', '')) | |
if IncludeLine: | |
StructBody.append(Line) | |
else: | |
OldTextBody.append(Line) | |
if Match and Match.group(4) == 'END': | |
if Level == 0: | |
if (StructType != Match.group(2)) or \ | |
(VariableName != Match.group(3)): | |
print("Unmatched struct name '%s' and '%s' !" % | |
(StructName, Match.group(2))) | |
else: | |
if IsCommonStruct: | |
Suffix = ' /* _COMMON_STRUCT_END_ */' | |
else: | |
Suffix = '' | |
Line = '} %s;%s\n\n\n' % (StructName, Suffix) | |
StructBody.append(Line) | |
if (Line not in NewTextBody) and \ | |
(Line not in OldTextBody): | |
NewTextBody.extend(StructBody) | |
IncludeLine = False | |
IsCommonStruct = False | |
if not IncludeEmbedOnly: | |
NewTextBody.extend(OldTextBody) | |
if EmbedFound: | |
NewTextBody = self.PostProcessBody(NewTextBody, False) | |
NewTextBody = IncTextBody + NewTextBody | |
return NewTextBody | |
def WriteHeaderFile(self, TxtBody, FileName, Type='h'): | |
FileNameDef = os.path.basename(FileName).replace('.', '_') | |
FileNameDef = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', FileNameDef) | |
FileNameDef = re.sub('([a-z0-9])([A-Z])', r'\1_\2', | |
FileNameDef).upper() | |
Lines = [] | |
Lines.append("%s\n" % GetCopyrightHeader(Type)) | |
Lines.append("#ifndef __%s__\n" % FileNameDef) | |
Lines.append("#define __%s__\n\n" % FileNameDef) | |
if Type == 'h': | |
Lines.append("#pragma pack(1)\n\n") | |
Lines.extend(TxtBody) | |
if Type == 'h': | |
Lines.append("#pragma pack()\n\n") | |
Lines.append("#endif\n") | |
# Don't rewrite if the contents are the same | |
Create = True | |
if os.path.exists(FileName): | |
HdrFile = open(FileName, "r") | |
OrgTxt = HdrFile.read() | |
HdrFile.close() | |
NewTxt = ''.join(Lines) | |
if OrgTxt == NewTxt: | |
Create = False | |
if Create: | |
HdrFile = open(FileName, "w") | |
HdrFile.write(''.join(Lines)) | |
HdrFile.close() | |
def CreateHeaderFile(self, HdrFileName, ComHdrFileName=''): | |
LastStruct = '' | |
SpaceIdx = 0 | |
Offset = 0 | |
FieldIdx = 0 | |
LastFieldIdx = 0 | |
ResvOffset = 0 | |
ResvIdx = 0 | |
TxtBody = [] | |
LineBuffer = [] | |
CfgTags = [] | |
LastVisible = True | |
TxtBody.append("typedef struct {\n") | |
for Item in self._CfgItemList: | |
# Search for CFGDATA tags | |
Embed = Item["embed"].upper() | |
if Embed.endswith(':START'): | |
Match = re.match(r'(\w+)_CFG_DATA:TAG_([0-9A-F]+):START', | |
Embed) | |
if Match: | |
TagName = Match.group(1) | |
TagId = int(Match.group(2), 16) | |
CfgTags.append((TagId, TagName)) | |
# Only process visible items | |
NextVisible = LastVisible | |
if LastVisible and (Item['header'] == 'OFF'): | |
NextVisible = False | |
ResvOffset = Item['offset'] | |
elif (not LastVisible) and Item['header'] == 'ON': | |
NextVisible = True | |
Name = "ReservedUpdSpace%d" % ResvIdx | |
ResvIdx = ResvIdx + 1 | |
TxtBody.append(self.CreateField( | |
Item, Name, Item["offset"] - ResvOffset, | |
ResvOffset, '', '', '', '')) | |
FieldIdx += 1 | |
if Offset < Item["offset"]: | |
if LastVisible: | |
Name = "UnusedUpdSpace%d" % SpaceIdx | |
LineBuffer.append(self.CreateField | |
(Item, Name, Item["offset"] - | |
Offset, Offset, '', '', '', '')) | |
FieldIdx += 1 | |
SpaceIdx = SpaceIdx + 1 | |
Offset = Item["offset"] | |
LastVisible = NextVisible | |
Offset = Offset + Item["length"] | |
if LastVisible: | |
for Each in LineBuffer: | |
TxtBody.append(Each) | |
LineBuffer = [] | |
Embed = Item["embed"].upper() | |
if Embed.endswith(':START') or Embed.endswith(':END'): | |
# EMBED_STRUCT: StructName : \ | |
# ItemName : VariableName : START|END | |
Name, ArrayNum = self.GetStructArrayInfo(Item["struct"]) | |
Remaining = Item["embed"] | |
if (LastFieldIdx + 1 == FieldIdx) and (LastStruct == Name): | |
ArrayMarker = ' ' | |
else: | |
ArrayMarker = '%d' % ArrayNum | |
LastFieldIdx = FieldIdx | |
LastStruct = Name | |
Marker = '/* EMBED_STRUCT:%s:%s%s*/ ' % (Name, Remaining, | |
ArrayMarker) | |
# if Embed.endswith(':START') and Comment != '': | |
# Marker = '/* COMMENT:%s */ \n' % Item["comment"] + Marker | |
else: | |
if Embed == '': | |
Marker = '' | |
else: | |
self.Error = "Invalid embedded structure \ | |
format '%s'!\n" % Item["embed"] | |
return 4 | |
# Generate bit fields for structure | |
if len(Item['subreg']) > 0 and Item["struct"]: | |
StructType = Item["struct"] | |
StructName, ArrayNum = self.GetStructArrayInfo(StructType) | |
if (LastFieldIdx + 1 == FieldIdx) and \ | |
(LastStruct == Item["struct"]): | |
ArrayMarker = ' ' | |
else: | |
ArrayMarker = '%d' % ArrayNum | |
TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:START%s*/\n' % | |
(StructName, StructType, Item["cname"], | |
ArrayMarker)) | |
for SubItem in Item['subreg']: | |
Name = SubItem["cname"] | |
if Name.startswith(Item["cname"]): | |
Name = Name[len(Item["cname"]) + 1:] | |
Line = self.CreateField( | |
SubItem, Name, SubItem["bitunit"], | |
SubItem["offset"], SubItem['struct'], | |
SubItem['name'], SubItem['help'], | |
SubItem['option'], SubItem['bitlength']) | |
TxtBody.append(Line) | |
TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:END%s*/\n' % | |
(StructName, StructType, Item["cname"], | |
ArrayMarker)) | |
LastFieldIdx = FieldIdx | |
LastStruct = Item["struct"] | |
FieldIdx += 1 | |
else: | |
FieldIdx += 1 | |
Line = Marker + self.CreateField( | |
Item, Item["cname"], Item["length"], Item["offset"], | |
Item['struct'], Item['name'], Item['help'], | |
Item['option']) | |
TxtBody.append(Line) | |
TxtBody.append("}\n\n") | |
# Handle the embedded data structure | |
TxtBody = self.PostProcessBody(TxtBody) | |
ComBody, TxtBody = self.SplitTextBody(TxtBody) | |
# Prepare TAG defines | |
PltTagDefTxt = ['\n'] | |
ComTagDefTxt = ['\n'] | |
for TagId, TagName in sorted(CfgTags): | |
TagLine = '#define %-30s 0x%03X\n' % ('CDATA_%s_TAG' % | |
TagName, TagId) | |
if TagId < self._MinCfgTagId: | |
# TAG ID < 0x100, it is a generic TAG | |
ComTagDefTxt.append(TagLine) | |
else: | |
PltTagDefTxt.append(TagLine) | |
PltTagDefTxt.append('\n\n') | |
ComTagDefTxt.append('\n\n') | |
# Write file back | |
self.WriteHeaderFile(PltTagDefTxt + TxtBody, HdrFileName) | |
if ComHdrFileName: | |
self.WriteHeaderFile(ComTagDefTxt + ComBody, ComHdrFileName) | |
return 0 | |
def UpdateConfigItemValue(self, Item, ValueStr): | |
IsArray = True if Item['value'].startswith('{') else False | |
IsString = True if Item['value'].startswith("'") else False | |
Bytes = self.ValueToByteArray(ValueStr, Item['length']) | |
if IsString: | |
NewValue = "'%s'" % Bytes.decode("utf-8") | |
elif IsArray: | |
NewValue = Bytes2Str(Bytes) | |
else: | |
Fmt = '0x%X' if Item['value'].startswith('0x') else '%d' | |
NewValue = Fmt % Bytes2Val(Bytes) | |
Item['value'] = NewValue | |
def LoadDefaultFromBinaryArray(self, BinDat, IgnoreFind=False): | |
FindOff = 0 | |
StartOff = 0 | |
for Item in self._CfgItemList: | |
if Item['length'] == 0: | |
continue | |
if not IgnoreFind and Item['find']: | |
FindBin = Item['find'].encode() | |
Offset = BinDat.find(FindBin) | |
if Offset >= 0: | |
TestOff = BinDat[Offset+len(FindBin):].find(FindBin) | |
if TestOff >= 0: | |
raise Exception('Multiple match found for "%s" !' % | |
Item['find']) | |
FindOff = Offset + len(FindBin) | |
StartOff = Item['offset'] | |
else: | |
raise Exception('Could not find "%s" !' % Item['find']) | |
if Item['offset'] + Item['length'] > len(BinDat): | |
raise Exception('Mismatching format between DSC \ | |
and BIN files !') | |
Offset = FindOff + (Item['offset'] - StartOff) | |
ValStr = Bytes2Str(BinDat[Offset: Offset + Item['length']]) | |
self.UpdateConfigItemValue(Item, ValStr) | |
self.UpdateDefaultValue() | |
def PatchBinaryArray(self, BinDat): | |
FileOff = 0 | |
Offset = 0 | |
FindOff = 0 | |
PatchList = [] | |
CfgBin = bytearray() | |
for Item in self._CfgItemList: | |
if Item['length'] == 0: | |
continue | |
if Item['find']: | |
if len(CfgBin) > 0: | |
PatchList.append((FileOff, CfgBin)) | |
FindBin = Item['find'].encode() | |
FileOff = BinDat.find(FindBin) | |
if FileOff < 0: | |
raise Exception('Could not find "%s" !' % Item['find']) | |
else: | |
TestOff = BinDat[FileOff+len(FindBin):].find(FindBin) | |
if TestOff >= 0: | |
raise Exception('Multiple match found for "%s" !' % | |
Item['find']) | |
FileOff += len(FindBin) | |
Offset = Item['offset'] | |
FindOff = Offset | |
CfgBin = bytearray() | |
if Item['offset'] > Offset: | |
Gap = Item['offset'] - Offset | |
CfgBin.extend(b'\x00' * Gap) | |
if Item['type'] == 'Reserved' and Item['option'] == '$SKIP': | |
# keep old data | |
NewOff = FileOff + (Offset - FindOff) | |
FileData = bytearray(BinDat[NewOff: NewOff + Item['length']]) | |
CfgBin.extend(FileData) | |
else: | |
CfgBin.extend(self.ValueToByteArray(Item['value'], | |
Item['length'])) | |
Offset = Item['offset'] + Item['length'] | |
if len(CfgBin) > 0: | |
PatchList.append((FileOff, CfgBin)) | |
for FileOff, CfgBin in PatchList: | |
Length = len(CfgBin) | |
if FileOff + Length < len(BinDat): | |
BinDat[FileOff:FileOff+Length] = CfgBin[:] | |
return BinDat | |
def GenerateBinaryArray(self): | |
Offset = 0 | |
BinDat = bytearray() | |
for Item in self._CfgItemList: | |
if Item['offset'] > Offset: | |
Gap = Item['offset'] - Offset | |
BinDat.extend(b'\x00' * Gap) | |
BinDat.extend(self.ValueToByteArray(Item['value'], Item['length'])) | |
Offset = Item['offset'] + Item['length'] | |
return BinDat | |
def GenerateBinary(self, BinFileName): | |
BinFile = open(BinFileName, "wb") | |
BinFile.write(self.GenerateBinaryArray()) | |
BinFile.close() | |
return 0 | |
def GenerateDataIncFile(self, DatIncFileName, BinFile=None): | |
# Put a prefix GUID before CFGDATA so that it can be located later on | |
Prefix = b'\xa7\xbd\x7f\x73\x20\x1e\x46\xd6\xbe\x8f\ | |
x64\x12\x05\x8d\x0a\xa8' | |
if BinFile: | |
Fin = open(BinFile, 'rb') | |
BinDat = Prefix + bytearray(Fin.read()) | |
Fin.close() | |
else: | |
BinDat = Prefix + self.GenerateBinaryArray() | |
FileName = os.path.basename(DatIncFileName).upper() | |
FileName = FileName.replace('.', '_') | |
TxtLines = [] | |
TxtLines.append("UINT8 mConfigDataBlob[%d] = {\n" % len(BinDat)) | |
Count = 0 | |
Line = [' '] | |
for Each in BinDat: | |
Line.append('0x%02X, ' % Each) | |
Count = Count + 1 | |
if (Count & 0x0F) == 0: | |
Line.append('\n') | |
TxtLines.append(''.join(Line)) | |
Line = [' '] | |
if len(Line) > 1: | |
TxtLines.append(''.join(Line) + '\n') | |
TxtLines.append("};\n\n") | |
self.WriteHeaderFile(TxtLines, DatIncFileName, 'inc') | |
return 0 | |
def CheckCfgData(self): | |
# Check if CfgData contains any duplicated name | |
def AddItem(Item, ChkList): | |
Name = Item['cname'] | |
if Name in ChkList: | |
return Item | |
if Name not in ['Dummy', 'Reserved', 'CfgHeader', 'CondValue']: | |
ChkList.append(Name) | |
return None | |
Duplicate = None | |
ChkList = [] | |
for Item in self._CfgItemList: | |
Duplicate = AddItem(Item, ChkList) | |
if not Duplicate: | |
for SubItem in Item['subreg']: | |
Duplicate = AddItem(SubItem, ChkList) | |
if Duplicate: | |
break | |
if Duplicate: | |
break | |
if Duplicate: | |
self.Error = "Duplicated CFGDATA '%s' found !\n" % \ | |
Duplicate['cname'] | |
return -1 | |
return 0 | |
def PrintData(self): | |
for Item in self._CfgItemList: | |
if not Item['length']: | |
continue | |
print("%-10s @Offset:0x%04X Len:%3d Val:%s" % | |
(Item['cname'], Item['offset'], Item['length'], | |
Item['value'])) | |
for SubItem in Item['subreg']: | |
print(" %-20s BitOff:0x%04X BitLen:%-3d Val:%s" % | |
(SubItem['cname'], SubItem['bitoffset'], | |
SubItem['bitlength'], SubItem['value'])) | |
def FormatArrayValue(self, Input, Length): | |
Dat = self.ValueToByteArray(Input, Length) | |
return ','.join('0x%02X' % Each for Each in Dat) | |
def GetItemOptionList(self, Item): | |
TmpList = [] | |
if Item['type'] == "Combo": | |
if not Item['option'] in self._BuidinOption: | |
OptList = Item['option'].split(',') | |
for Option in OptList: | |
Option = Option.strip() | |
try: | |
(OpVal, OpStr) = Option.split(':') | |
except Exception: | |
raise Exception("Invalide option format '%s' !" % | |
Option) | |
TmpList.append((OpVal, OpStr)) | |
return TmpList | |
def WriteBsfStruct(self, BsfFd, Item): | |
if Item['type'] == "None": | |
Space = "gPlatformFspPkgTokenSpaceGuid" | |
else: | |
Space = Item['space'] | |
Line = " $%s_%s" % (Space, Item['cname']) | |
Match = re.match("\\s*(\\{.+\\})\\s*", Item['value']) | |
if Match: | |
DefaultValue = self.FormatArrayValue(Match.group(1).strip(), | |
Item['length']) | |
else: | |
DefaultValue = Item['value'].strip() | |
if 'bitlength' in Item: | |
if Item['bitlength']: | |
BsfFd.write(" %s%s%4d bits $_DEFAULT_ = %s\n" % | |
(Line, ' ' * (64 - len(Line)), Item['bitlength'], | |
DefaultValue)) | |
else: | |
if Item['length']: | |
BsfFd.write(" %s%s%4d bytes $_DEFAULT_ = %s\n" % | |
(Line, ' ' * (64 - len(Line)), Item['length'], | |
DefaultValue)) | |
return self.GetItemOptionList(Item) | |
def GetBsfOption(self, OptionName): | |
if OptionName in self._CfgOptsDict: | |
return self._CfgOptsDict[OptionName] | |
else: | |
return OptionName | |
def WriteBsfOption(self, BsfFd, Item): | |
PcdName = Item['space'] + '_' + Item['cname'] | |
WriteHelp = 0 | |
BsfLines = [] | |
if Item['type'] == "Combo": | |
if Item['option'] in self._BuidinOption: | |
Options = self._BuidinOption[Item['option']] | |
else: | |
Options = self.GetBsfOption(PcdName) | |
BsfLines.append(' %s $%s, "%s", &%s,\n' % ( | |
Item['type'], PcdName, Item['name'], Options)) | |
WriteHelp = 1 | |
elif Item['type'].startswith("EditNum"): | |
Match = re.match("EditNum\\s*,\\s*(HEX|DEC)\\s*,\\s*\\(\ | |
(\\d+|0x[0-9A-Fa-f]+)\\s*,\\s*(\\d+|0x[0-9A-Fa-f]+)\\)", Item['type']) | |
if Match: | |
BsfLines.append(' EditNum $%s, "%s", %s,\n' % ( | |
PcdName, Item['name'], Match.group(1))) | |
WriteHelp = 2 | |
elif Item['type'].startswith("EditText"): | |
BsfLines.append(' %s $%s, "%s",\n' % (Item['type'], PcdName, | |
Item['name'])) | |
WriteHelp = 1 | |
elif Item['type'] == "Table": | |
Columns = Item['option'].split(',') | |
if len(Columns) != 0: | |
BsfLines.append(' %s $%s "%s",' % (Item['type'], PcdName, | |
Item['name'])) | |
for Col in Columns: | |
Fmt = Col.split(':') | |
if len(Fmt) != 3: | |
raise Exception("Column format '%s' is invalid !" % | |
Fmt) | |
try: | |
Dtype = int(Fmt[1].strip()) | |
except Exception: | |
raise Exception("Column size '%s' is invalid !" % | |
Fmt[1]) | |
BsfLines.append('\n Column "%s", %d bytes, %s' % | |
(Fmt[0].strip(), Dtype, Fmt[2].strip())) | |
BsfLines.append(',\n') | |
WriteHelp = 1 | |
if WriteHelp > 0: | |
HelpLines = Item['help'].split('\\n\\r') | |
FirstLine = True | |
for HelpLine in HelpLines: | |
if FirstLine: | |
FirstLine = False | |
BsfLines.append(' Help "%s"\n' % (HelpLine)) | |
else: | |
BsfLines.append(' "%s"\n' % (HelpLine)) | |
if WriteHelp == 2: | |
BsfLines.append(' "Valid range: %s ~ %s"\n' % | |
(Match.group(2), Match.group(3))) | |
if len(Item['condition']) > 4: | |
CondList = Item['condition'].split(',') | |
Idx = 0 | |
for Cond in CondList: | |
Cond = Cond.strip() | |
if Cond.startswith('#'): | |
BsfLines.insert(Idx, Cond + '\n') | |
Idx += 1 | |
elif Cond.startswith('@#'): | |
BsfLines.append(Cond[1:] + '\n') | |
for Line in BsfLines: | |
BsfFd.write(Line) | |
def WriteBsfPages(self, PageTree, BsfFd): | |
BsfFd.write('\n') | |
Key = next(iter(PageTree)) | |
for Page in PageTree[Key]: | |
PageName = next(iter(Page)) | |
BsfFd.write('Page "%s"\n' % self._CfgPageDict[PageName]) | |
if len(PageTree[Key]): | |
self.WriteBsfPages(Page, BsfFd) | |
BsfItems = [] | |
for Item in self._CfgItemList: | |
if Item['name'] != '': | |
if Item['page'] != PageName: | |
continue | |
if len(Item['subreg']) > 0: | |
for SubItem in Item['subreg']: | |
if SubItem['name'] != '': | |
BsfItems.append(SubItem) | |
else: | |
BsfItems.append(Item) | |
BsfItems.sort(key=lambda x: x['order']) | |
for Item in BsfItems: | |
self.WriteBsfOption(BsfFd, Item) | |
BsfFd.write("EndPage\n\n") | |
def GenerateBsfFile(self, BsfFile): | |
if BsfFile == '': | |
self.Error = "BSF output file '%s' is invalid" % BsfFile | |
return 1 | |
Error = 0 | |
OptionDict = {} | |
BsfFd = open(BsfFile, "w") | |
BsfFd.write("%s\n" % GetCopyrightHeader('bsf')) | |
BsfFd.write("%s\n" % self._GlobalDataDef) | |
BsfFd.write("StructDef\n") | |
NextOffset = -1 | |
for Item in self._CfgItemList: | |
if Item['find'] != '': | |
BsfFd.write('\n Find "%s"\n' % Item['find']) | |
NextOffset = Item['offset'] + Item['length'] | |
if Item['name'] != '': | |
if NextOffset != Item['offset']: | |
BsfFd.write(" Skip %d bytes\n" % | |
(Item['offset'] - NextOffset)) | |
if len(Item['subreg']) > 0: | |
NextOffset = Item['offset'] | |
BitsOffset = NextOffset * 8 | |
for SubItem in Item['subreg']: | |
BitsOffset += SubItem['bitlength'] | |
if SubItem['name'] == '': | |
if 'bitlength' in SubItem: | |
BsfFd.write(" Skip %d bits\n" % | |
(SubItem['bitlength'])) | |
else: | |
BsfFd.write(" Skip %d bytes\n" % | |
(SubItem['length'])) | |
else: | |
Options = self.WriteBsfStruct(BsfFd, SubItem) | |
if len(Options) > 0: | |
OptionDict[SubItem | |
['space']+'_'+SubItem | |
['cname']] = Options | |
NextBitsOffset = (Item['offset'] + Item['length']) * 8 | |
if NextBitsOffset > BitsOffset: | |
BitsGap = NextBitsOffset - BitsOffset | |
BitsRemain = BitsGap % 8 | |
if BitsRemain: | |
BsfFd.write(" Skip %d bits\n" % BitsRemain) | |
BitsGap -= BitsRemain | |
BytesRemain = BitsGap // 8 | |
if BytesRemain: | |
BsfFd.write(" Skip %d bytes\n" % | |
BytesRemain) | |
NextOffset = Item['offset'] + Item['length'] | |
else: | |
NextOffset = Item['offset'] + Item['length'] | |
Options = self.WriteBsfStruct(BsfFd, Item) | |
if len(Options) > 0: | |
OptionDict[Item['space']+'_'+Item['cname']] = Options | |
BsfFd.write("\nEndStruct\n\n") | |
BsfFd.write("%s" % self._BuidinOptionTxt) | |
NameList = [] | |
OptionList = [] | |
for Each in sorted(OptionDict): | |
if OptionDict[Each] not in OptionList: | |
NameList.append(Each) | |
OptionList.append(OptionDict[Each]) | |
BsfFd.write("List &%s\n" % Each) | |
for Item in OptionDict[Each]: | |
BsfFd.write(' Selection %s , "%s"\n' % | |
(self.EvaluateExpress(Item[0]), Item[1])) | |
BsfFd.write("EndList\n\n") | |
else: | |
# Item has idential options as other item | |
# Try to reuse the previous options instead | |
Idx = OptionList.index(OptionDict[Each]) | |
self._CfgOptsDict[Each] = NameList[Idx] | |
BsfFd.write("BeginInfoBlock\n") | |
BsfFd.write(' PPVer "%s"\n' % (self._CfgBlkDict['ver'])) | |
BsfFd.write(' Description "%s"\n' % (self._CfgBlkDict['name'])) | |
BsfFd.write("EndInfoBlock\n\n") | |
self.WriteBsfPages(self._CfgPageTree, BsfFd) | |
BsfFd.close() | |
return Error | |
def WriteDeltaLine(self, OutLines, Name, ValStr, IsArray): | |
if IsArray: | |
Output = '%s | { %s }' % (Name, ValStr) | |
else: | |
Output = '%s | 0x%X' % (Name, Array2Val(ValStr)) | |
OutLines.append(Output) | |
def WriteDeltaFile(self, OutFile, PlatformId, OutLines): | |
DltFd = open(OutFile, "w") | |
DltFd.write("%s\n" % GetCopyrightHeader('dlt', True)) | |
if PlatformId is not None: | |
DltFd.write('#\n') | |
DltFd.write('# Delta configuration values \ | |
for platform ID 0x%04X\n' % PlatformId) | |
DltFd.write('#\n\n') | |
for Line in OutLines: | |
DltFd.write('%s\n' % Line) | |
DltFd.close() | |
def GenerateDeltaFile(self, OutFile, AbsfFile): | |
# Parse ABSF Build in dict | |
if not os.path.exists(AbsfFile): | |
Lines = [] | |
else: | |
with open(AbsfFile) as Fin: | |
Lines = Fin.readlines() | |
AbsfBuiltValDict = {} | |
Process = False | |
for Line in Lines: | |
Line = Line.strip() | |
if Line.startswith('StructDef'): | |
Process = True | |
if Line.startswith('EndStruct'): | |
break | |
if not Process: | |
continue | |
Match = re.match('\\s*\\$gCfgData_(\\w+)\\s+\ | |
(\\d+)\\s+(bits|bytes)\\s+\\$_AS_BUILT_\\s+=\\s+(.+)\\$', Line) | |
if Match: | |
if Match.group(1) not in AbsfBuiltValDict: | |
AbsfBuiltValDict[Match.group(1)] = Match.group(4).strip() | |
else: | |
raise Exception("Duplicated configuration \ | |
name '%s' found !", Match.group(1)) | |
# Match config item in DSC | |
PlatformId = None | |
OutLines = [] | |
TagName = '' | |
Level = 0 | |
for Item in self._CfgItemList: | |
Name = None | |
if Level == 0 and Item['embed'].endswith(':START'): | |
TagName = Item['embed'].split(':')[0] | |
Level += 1 | |
if Item['cname'] in AbsfBuiltValDict: | |
ValStr = AbsfBuiltValDict[Item['cname']] | |
Name = '%s.%s' % (TagName, Item['cname']) | |
if not Item['subreg'] and Item['value'].startswith('{'): | |
Value = Array2Val(Item['value']) | |
IsArray = True | |
else: | |
Value = int(Item['value'], 16) | |
IsArray = False | |
AbsfVal = Array2Val(ValStr) | |
if AbsfVal != Value: | |
if 'PLATFORMID_CFG_DATA.PlatformId' == Name: | |
PlatformId = AbsfVal | |
self.WriteDeltaLine(OutLines, Name, ValStr, IsArray) | |
else: | |
if 'PLATFORMID_CFG_DATA.PlatformId' == Name: | |
raise Exception("'PlatformId' has the \ | |
same value as DSC default !") | |
if Item['subreg']: | |
for SubItem in Item['subreg']: | |
if SubItem['cname'] in AbsfBuiltValDict: | |
ValStr = AbsfBuiltValDict[SubItem['cname']] | |
if Array2Val(ValStr) == int(SubItem['value'], 16): | |
continue | |
Name = '%s.%s.%s' % (TagName, Item['cname'], | |
SubItem['cname']) | |
self.WriteDeltaLine(OutLines, Name, ValStr, False) | |
if Item['embed'].endswith(':END'): | |
Level -= 1 | |
if PlatformId is None and Lines: | |
raise Exception("'PlatformId' configuration \ | |
is missing in ABSF file!") | |
else: | |
PlatformId = 0 | |
self.WriteDeltaFile(OutFile, PlatformId, Lines) | |
return 0 | |
def GenerateDscFile(self, OutFile): | |
DscFd = open(OutFile, "w") | |
for Line in self._DscLines: | |
DscFd.write(Line + '\n') | |
DscFd.close() | |
return 0 | |
def Usage(): | |
print('\n'.join([ | |
"GenCfgData Version 0.01", | |
"Usage:", | |
" GenCfgData GENINC BinFile \ | |
IncOutFile [-D Macros]", | |
" GenCfgData GENPKL DscFile \ | |
PklOutFile [-D Macros]", | |
" GenCfgData GENINC DscFile[;DltFile] \ | |
IncOutFile [-D Macros]", | |
" GenCfgData GENBIN DscFile[;DltFile] \ | |
BinOutFile [-D Macros]", | |
" GenCfgData GENBSF DscFile[;DltFile] \ | |
BsfOutFile [-D Macros]", | |
" GenCfgData GENDLT DscFile[;AbsfFile] \ | |
DltOutFile [-D Macros]", | |
" GenCfgData GENDSC DscFile \ | |
DscOutFile [-D Macros]", | |
" GenCfgData GENHDR DscFile[;DltFile] \ | |
HdrOutFile[;ComHdrOutFile] [-D Macros]" | |
])) | |
def Main(): | |
# | |
# Parse the options and args | |
# | |
argc = len(sys.argv) | |
if argc < 4: | |
Usage() | |
return 1 | |
GenCfgData = CGenCfgData() | |
Command = sys.argv[1].upper() | |
OutFile = sys.argv[3] | |
if argc > 5 and GenCfgData.ParseMacros(sys.argv[4:]) != 0: | |
raise Exception("ERROR: Macro parsing failed !") | |
FileList = sys.argv[2].split(';') | |
if len(FileList) == 2: | |
DscFile = FileList[0] | |
DltFile = FileList[1] | |
elif len(FileList) == 1: | |
DscFile = FileList[0] | |
DltFile = '' | |
else: | |
raise Exception("ERROR: Invalid parameter '%s' !" % sys.argv[2]) | |
if Command == "GENDLT" and DscFile.endswith('.dlt'): | |
# It needs to expand an existing DLT file | |
DltFile = DscFile | |
Lines = CGenCfgData.ExpandIncludeFiles(DltFile) | |
OutTxt = ''.join([x[0] for x in Lines]) | |
OutFile = open(OutFile, "w") | |
OutFile.write(OutTxt) | |
OutFile.close() | |
return 0 | |
if not os.path.exists(DscFile): | |
raise Exception("ERROR: Cannot open file '%s' !" % DscFile) | |
CfgBinFile = '' | |
if DltFile: | |
if not os.path.exists(DltFile): | |
raise Exception("ERROR: Cannot open file '%s' !" % DltFile) | |
if Command == "GENDLT": | |
CfgBinFile = DltFile | |
DltFile = '' | |
BinFile = '' | |
if (DscFile.lower().endswith('.bin')) and (Command == "GENINC"): | |
# It is binary file | |
BinFile = DscFile | |
DscFile = '' | |
if BinFile: | |
if GenCfgData.GenerateDataIncFile(OutFile, BinFile) != 0: | |
raise Exception(GenCfgData.Error) | |
return 0 | |
if DscFile.lower().endswith('.pkl'): | |
with open(DscFile, "rb") as PklFile: | |
GenCfgData.__dict__ = marshal.load(PklFile) | |
else: | |
if GenCfgData.ParseDscFile(DscFile) != 0: | |
raise Exception(GenCfgData.Error) | |
# if GenCfgData.CheckCfgData() != 0: | |
# raise Exception(GenCfgData.Error) | |
if GenCfgData.CreateVarDict() != 0: | |
raise Exception(GenCfgData.Error) | |
if Command == 'GENPKL': | |
with open(OutFile, "wb") as PklFile: | |
marshal.dump(GenCfgData.__dict__, PklFile) | |
return 0 | |
if DltFile and Command in ['GENHDR', 'GENBIN', 'GENINC', 'GENBSF']: | |
if GenCfgData.OverrideDefaultValue(DltFile) != 0: | |
raise Exception(GenCfgData.Error) | |
if GenCfgData.UpdateDefaultValue() != 0: | |
raise Exception(GenCfgData.Error) | |
# GenCfgData.PrintData () | |
if sys.argv[1] == "GENBIN": | |
if GenCfgData.GenerateBinary(OutFile) != 0: | |
raise Exception(GenCfgData.Error) | |
elif sys.argv[1] == "GENHDR": | |
OutFiles = OutFile.split(';') | |
BrdOutFile = OutFiles[0].strip() | |
if len(OutFiles) > 1: | |
ComOutFile = OutFiles[1].strip() | |
else: | |
ComOutFile = '' | |
if GenCfgData.CreateHeaderFile(BrdOutFile, ComOutFile) != 0: | |
raise Exception(GenCfgData.Error) | |
elif sys.argv[1] == "GENBSF": | |
if GenCfgData.GenerateBsfFile(OutFile) != 0: | |
raise Exception(GenCfgData.Error) | |
elif sys.argv[1] == "GENINC": | |
if GenCfgData.GenerateDataIncFile(OutFile) != 0: | |
raise Exception(GenCfgData.Error) | |
elif sys.argv[1] == "GENDLT": | |
if GenCfgData.GenerateDeltaFile(OutFile, CfgBinFile) != 0: | |
raise Exception(GenCfgData.Error) | |
elif sys.argv[1] == "GENDSC": | |
if GenCfgData.GenerateDscFile(OutFile) != 0: | |
raise Exception(GenCfgData.Error) | |
else: | |
raise Exception("Unsuported command '%s' !" % Command) | |
return 0 | |
if __name__ == '__main__': | |
sys.exit(Main()) |