audk/IntelFsp2Pkg/Tools/ConfigEditor/GenYamlCfg.py

2386 lines
86 KiB
Python

# @ GenYamlCfg.py
#
# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
import os
import sys
import re
import marshal
import string
import operator as op
import ast
import tkinter.messagebox as messagebox
import tkinter
from datetime import date
from collections import OrderedDict
from CommonUtility import value_to_bytearray, value_to_bytes, \
bytes_to_value, get_bits_from_bytes, set_bits_to_bytes
# Generated file copyright header
__copyright_tmp__ = """/** @file
Platform 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 !!!
**/
"""
def get_copyright_header(file_type, allow_modify=False):
file_description = {
'yaml': 'Boot Setting',
'dlt': 'Delta',
'inc': 'C Binary Blob',
'h': 'C Struct Header'
}
if file_type in ['yaml', 'dlt']:
comment_char = '#'
else:
comment_char = ''
lines = __copyright_tmp__.split('\n')
if allow_modify:
lines = [line for line in lines if 'Please do NOT modify' not in line]
copyright_hdr = '\n'.join('%s%s' % (comment_char, line)
for line in lines)[:-1] + '\n'
return copyright_hdr % (file_description[file_type], date.today().year)
def check_quote(text):
if (text[0] == "'" and text[-1] == "'") or (text[0] == '"'
and text[-1] == '"'):
return True
return False
def strip_quote(text):
new_text = text.strip()
if check_quote(new_text):
return new_text[1:-1]
return text
def strip_delimiter(text, delim):
new_text = text.strip()
if new_text:
if new_text[0] == delim[0] and new_text[-1] == delim[-1]:
return new_text[1:-1]
return text
def bytes_to_bracket_str(bytes):
return '{ %s }' % (', '.join('0x%02x' % i for i in bytes))
def array_str_to_value(val_str):
val_str = val_str.strip()
val_str = strip_delimiter(val_str, '{}')
val_str = strip_quote(val_str)
value = 0
for each in val_str.split(',')[::-1]:
each = each.strip()
value = (value << 8) | int(each, 0)
return value
def write_lines(lines, file):
fo = open(file, "w")
fo.write(''.join([x[0] for x in lines]))
fo.close()
def read_lines(file):
if not os.path.exists(file):
test_file = os.path.basename(file)
if os.path.exists(test_file):
file = test_file
fi = open(file, 'r')
lines = fi.readlines()
fi.close()
return lines
def expand_file_value(path, value_str):
result = bytearray()
match = re.match("\\{\\s*FILE:(.+)\\}", value_str)
if match:
file_list = match.group(1).split(',')
for file in file_list:
file = file.strip()
bin_path = os.path.join(path, file)
result.extend(bytearray(open(bin_path, 'rb').read()))
print('\n\n result ', result)
return result
class ExpressionEval(ast.NodeVisitor):
operators = {
ast.Add: op.add,
ast.Sub: op.sub,
ast.Mult: op.mul,
ast.Div: op.floordiv,
ast.Mod: op.mod,
ast.Eq: op.eq,
ast.NotEq: op.ne,
ast.Gt: op.gt,
ast.Lt: op.lt,
ast.GtE: op.ge,
ast.LtE: op.le,
ast.BitXor: op.xor,
ast.BitAnd: op.and_,
ast.BitOr: op.or_,
ast.Invert: op.invert,
ast.USub: op.neg
}
def __init__(self):
self._debug = False
self._expression = ''
self._namespace = {}
self._get_variable = None
def eval(self, expr, vars={}):
self._expression = expr
if type(vars) is dict:
self._namespace = vars
self._get_variable = None
else:
self._namespace = {}
self._get_variable = vars
node = ast.parse(self._expression, mode='eval')
result = self.visit(node.body)
if self._debug:
print('EVAL [ %s ] = %s' % (expr, str(result)))
return result
def visit_Name(self, node):
if self._get_variable is not None:
return self._get_variable(node.id)
else:
return self._namespace[node.id]
def visit_Num(self, node):
return node.n
def visit_NameConstant(self, node):
return node.value
def visit_BoolOp(self, node):
result = False
if isinstance(node.op, ast.And):
for value in node.values:
result = self.visit(value)
if not result:
break
elif isinstance(node.op, ast.Or):
for value in node.values:
result = self.visit(value)
if result:
break
return True if result else False
def visit_UnaryOp(self, node):
val = self.visit(node.operand)
return ExpressionEval.operators[type(node.op)](val)
def visit_BinOp(self, node):
lhs = self.visit(node.left)
rhs = self.visit(node.right)
return ExpressionEval.operators[type(node.op)](lhs, rhs)
def visit_Compare(self, node):
right = self.visit(node.left)
result = True
for operation, comp in zip(node.ops, node.comparators):
if not result:
break
left = right
right = self.visit(comp)
result = ExpressionEval.operators[type(operation)](left, right)
return result
def visit_Call(self, node):
if node.func.id in ['ternary']:
condition = self.visit(node.args[0])
val_true = self.visit(node.args[1])
val_false = self.visit(node.args[2])
return val_true if condition else val_false
elif node.func.id in ['offset', 'length']:
if self._get_variable is not None:
return self._get_variable(node.args[0].s, node.func.id)
else:
raise ValueError("Unsupported function: " + repr(node))
def generic_visit(self, node):
raise ValueError("malformed node or string: " + repr(node))
class CFG_YAML():
TEMPLATE = 'template'
CONFIGS = 'configs'
VARIABLE = 'variable'
def __init__(self):
self.log_line = False
self.allow_template = False
self.cfg_tree = None
self.tmp_tree = None
self.var_dict = None
self.def_dict = {}
self.yaml_path = ''
self.lines = []
self.full_lines = []
self.index = 0
self.re_expand = re.compile(
r'(.+:\s+|\s*\-\s*)!expand\s+\{\s*(\w+_TMPL)\s*:\s*\[(.+)]\s*\}')
self.re_include = re.compile(r'(.+:\s+|\s*\-\s*)!include\s+(.+)')
@staticmethod
def count_indent(line):
return next((i for i, c in enumerate(line) if not c.isspace()),
len(line))
@staticmethod
def substitue_args(text, arg_dict):
for arg in arg_dict:
text = text.replace('$' + arg, arg_dict[arg])
return text
@staticmethod
def dprint(*args):
pass
def process_include(self, line, insert=True):
match = self.re_include.match(line)
if not match:
raise Exception("Invalid !include format '%s' !" % line.strip())
prefix = match.group(1)
include = match.group(2)
if prefix.strip() == '-':
prefix = ''
adjust = 0
else:
adjust = 2
include = strip_quote(include)
request = CFG_YAML.count_indent(line) + adjust
if self.log_line:
# remove the include line itself
del self.full_lines[-1]
inc_path = os.path.join(self.yaml_path, include)
if not os.path.exists(inc_path):
# try relative path to project root
try_path = os.path.join(os.path.dirname(os.path.realpath(__file__)
), "../..", include)
if os.path.exists(try_path):
inc_path = try_path
else:
raise Exception("ERROR: Cannot open file '%s'." % inc_path)
lines = read_lines(inc_path)
current = 0
same_line = False
for idx, each in enumerate(lines):
start = each.lstrip()
if start == '' or start[0] == '#':
continue
if start[0] == '>':
# append the content directly at the same line
same_line = True
start = idx
current = CFG_YAML.count_indent(each)
break
lines = lines[start+1:] if same_line else lines[start:]
leading = ''
if same_line:
request = len(prefix)
leading = '>'
lines = [prefix + '%s\n' % leading] + [' ' * request +
i[current:] for i in lines]
if insert:
self.lines = lines + self.lines
return lines
def process_expand(self, line):
match = self.re_expand.match(line)
if not match:
raise Exception("Invalid !expand format '%s' !" % line.strip())
lines = []
prefix = match.group(1)
temp_name = match.group(2)
args = match.group(3)
if prefix.strip() == '-':
indent = 0
else:
indent = 2
lines = self.process_expand_template(temp_name, prefix, args, indent)
self.lines = lines + self.lines
def process_expand_template(self, temp_name, prefix, args, indent=2):
# expand text with arg substitution
if temp_name not in self.tmp_tree:
raise Exception("Could not find template '%s' !" % temp_name)
parts = args.split(',')
parts = [i.strip() for i in parts]
num = len(parts)
arg_dict = dict(zip(['(%d)' % (i + 1) for i in range(num)], parts))
str_data = self.tmp_tree[temp_name]
text = DefTemplate(str_data).safe_substitute(self.def_dict)
text = CFG_YAML.substitue_args(text, arg_dict)
target = CFG_YAML.count_indent(prefix) + indent
current = CFG_YAML.count_indent(text)
padding = target * ' '
if indent == 0:
leading = []
else:
leading = [prefix + '\n']
text = leading + [(padding + i + '\n')[current:]
for i in text.splitlines()]
return text
def load_file(self, yaml_file):
self.index = 0
self.lines = read_lines(yaml_file)
def peek_line(self):
if len(self.lines) == 0:
return None
else:
return self.lines[0]
def put_line(self, line):
self.lines.insert(0, line)
if self.log_line:
del self.full_lines[-1]
def get_line(self):
if len(self.lines) == 0:
return None
else:
line = self.lines.pop(0)
if self.log_line:
self.full_lines.append(line.rstrip())
return line
def get_multiple_line(self, indent):
text = ''
newind = indent + 1
while True:
line = self.peek_line()
if line is None:
break
sline = line.strip()
if sline != '':
newind = CFG_YAML.count_indent(line)
if newind <= indent:
break
self.get_line()
if sline != '':
text = text + line
return text
def traverse_cfg_tree(self, handler):
def _traverse_cfg_tree(root, level=0):
# config structure
for key in root:
if type(root[key]) is OrderedDict:
level += 1
handler(key, root[key], level)
_traverse_cfg_tree(root[key], level)
level -= 1
_traverse_cfg_tree(self.cfg_tree)
def count(self):
def _count(name, cfgs, level):
num[0] += 1
num = [0]
self.traverse_cfg_tree(_count)
return num[0]
def parse(self, parent_name='', curr=None, level=0):
child = None
last_indent = None
key = ''
temp_chk = {}
while True:
line = self.get_line()
if line is None:
break
curr_line = line.strip()
if curr_line == '' or curr_line[0] == '#':
continue
indent = CFG_YAML.count_indent(line)
if last_indent is None:
last_indent = indent
if indent != last_indent:
# outside of current block, put the line back to queue
self.put_line(' ' * indent + curr_line)
if curr_line.endswith(': >'):
# multiline marker
old_count = len(self.full_lines)
line = self.get_multiple_line(indent)
if self.log_line and not self.allow_template \
and '!include ' in line:
# expand include in template
new_lines = []
lines = line.splitlines()
for idx, each in enumerate(lines):
if '!include ' in each:
new_line = ''.join(self.process_include(each,
False))
new_lines.append(new_line)
else:
new_lines.append(each)
self.full_lines = self.full_lines[:old_count] + new_lines
curr_line = curr_line + line
if indent > last_indent:
# child nodes
if child is None:
raise Exception('Unexpected format at line: %s'
% (curr_line))
level += 1
self.parse(key, child, level)
level -= 1
line = self.peek_line()
if line is not None:
curr_line = line.strip()
indent = CFG_YAML.count_indent(line)
if indent >= last_indent:
# consume the line
self.get_line()
else:
# end of file
indent = -1
if curr is None:
curr = OrderedDict()
if indent < last_indent:
return curr
marker1 = curr_line[0]
marker2 = curr_line[-1]
start = 1 if marker1 == '-' else 0
pos = curr_line.find(': ')
if pos > 0:
child = None
key = curr_line[start:pos].strip()
if curr_line[pos + 2] == '>':
curr[key] = curr_line[pos + 3:]
else:
# XXXX: !include / !expand
if '!include ' in curr_line:
self.process_include(line)
elif '!expand ' in curr_line:
if self.allow_template and not self.log_line:
self.process_expand(line)
else:
value_str = curr_line[pos + 2:].strip()
curr[key] = value_str
if self.log_line and value_str[0] == '{':
# expand {FILE: xxxx} format in the log line
if value_str[1:].rstrip().startswith('FILE:'):
value_bytes = expand_file_value(
self.yaml_path, value_str)
value_str = bytes_to_bracket_str(value_bytes)
self.full_lines[-1] = line[
:indent] + curr_line[:pos + 2] + value_str
elif marker2 == ':':
child = OrderedDict()
key = curr_line[start:-1].strip()
if key == '$ACTION':
# special virtual nodes, rename to ensure unique key
key = '$ACTION_%04X' % self.index
self.index += 1
if key in curr:
if key not in temp_chk:
# check for duplicated keys at same level
temp_chk[key] = 1
else:
raise Exception("Duplicated item '%s:%s' found !"
% (parent_name, key))
curr[key] = child
if self.var_dict is None and key == CFG_YAML.VARIABLE:
self.var_dict = child
if self.tmp_tree is None and key == CFG_YAML.TEMPLATE:
self.tmp_tree = child
if self.var_dict:
for each in self.var_dict:
txt = self.var_dict[each]
if type(txt) is str:
self.def_dict['(%s)' % each] = txt
if self.tmp_tree and key == CFG_YAML.CONFIGS:
# apply template for the main configs
self.allow_template = True
else:
child = None
# - !include cfg_opt.yaml
if '!include ' in curr_line:
self.process_include(line)
return curr
def load_yaml(self, opt_file):
self.var_dict = None
self.yaml_path = os.path.dirname(opt_file)
self.load_file(opt_file)
yaml_tree = self.parse()
self.tmp_tree = yaml_tree[CFG_YAML.TEMPLATE]
self.cfg_tree = yaml_tree[CFG_YAML.CONFIGS]
return self.cfg_tree
def expand_yaml(self, opt_file):
self.log_line = True
self.load_yaml(opt_file)
self.log_line = False
text = '\n'.join(self.full_lines)
self.full_lines = []
return text
class DefTemplate(string.Template):
idpattern = '\\([_A-Z][_A-Z0-9]*\\)|[_A-Z][_A-Z0-9]*'
class CGenYamlCfg:
STRUCT = '$STRUCT'
bits_width = {'b': 1, 'B': 8, 'W': 16, 'D': 32, 'Q': 64}
builtin_option = {'$EN_DIS': [('0', 'Disable'), ('1', 'Enable')]}
exclude_struct = ['FSP_UPD_HEADER', 'FSPT_ARCH_UPD',
'FSPM_ARCH_UPD', 'FSPS_ARCH_UPD',
'GPIO_GPP_*', 'GPIO_CFG_DATA',
'GpioConfPad*', 'GpioPinConfig',
'BOOT_OPTION*', 'PLATFORMID_CFG_DATA', '\\w+_Half[01]']
include_tag = ['GPIO_CFG_DATA']
keyword_set = set(['name', 'type', 'option', 'help', 'length',
'value', 'order', 'struct', 'condition'])
def __init__(self):
self._mode = ''
self._debug = False
self._macro_dict = {}
self.binseg_dict = {}
self.initialize()
def initialize(self):
self._old_bin = None
self._cfg_tree = {}
self._tmp_tree = {}
self._cfg_list = []
self._cfg_page = {'root': {'title': '', 'child': []}}
self._cur_page = ''
self._var_dict = {}
self._def_dict = {}
self._yaml_path = ''
@staticmethod
def deep_convert_dict(layer):
# convert OrderedDict to list + dict
new_list = layer
if isinstance(layer, OrderedDict):
new_list = list(layer.items())
for idx, pair in enumerate(new_list):
new_node = CGenYamlCfg.deep_convert_dict(pair[1])
new_list[idx] = dict({pair[0]: new_node})
return new_list
@staticmethod
def deep_convert_list(layer):
if isinstance(layer, list):
od = OrderedDict({})
for each in layer:
if isinstance(each, dict):
key = next(iter(each))
od[key] = CGenYamlCfg.deep_convert_list(each[key])
return od
else:
return layer
@staticmethod
def expand_include_files(file_path, cur_dir=''):
if cur_dir == '':
cur_dir = os.path.dirname(file_path)
file_path = os.path.basename(file_path)
input_file_path = os.path.join(cur_dir, file_path)
file = open(input_file_path, "r")
lines = file.readlines()
file.close()
new_lines = []
for line_num, line in enumerate(lines):
match = re.match("^!include\\s*(.+)?$", line.strip())
if match:
inc_path = match.group(1)
tmp_path = os.path.join(cur_dir, inc_path)
org_path = tmp_path
if not os.path.exists(tmp_path):
cur_dir = os.path.join(os.path.dirname
(os.path.realpath(__file__)
), "..", "..")
tmp_path = os.path.join(cur_dir, inc_path)
if not os.path.exists(tmp_path):
raise Exception("ERROR: Cannot open include\
file '%s'." % org_path)
else:
new_lines.append(('# Included from file: %s\n' % inc_path,
tmp_path, 0))
new_lines.append(('# %s\n' % ('=' * 80), tmp_path, 0))
new_lines.extend(CGenYamlCfg.expand_include_files
(inc_path, cur_dir))
else:
new_lines.append((line, input_file_path, line_num))
return new_lines
@staticmethod
def format_struct_field_name(input, count=0):
name = ''
cap = True
if '_' in input:
input = input.lower()
for each in input:
if each == '_':
cap = True
continue
elif cap:
each = each.upper()
cap = False
name = name + each
if count > 1:
name = '%s[%d]' % (name, count)
return name
def get_mode(self):
return self._mode
def set_mode(self, mode):
self._mode = mode
def get_last_error(self):
return ''
def get_variable(self, var, attr='value'):
if var in self._var_dict:
var = self._var_dict[var]
return var
item = self.locate_cfg_item(var, False)
if item is None:
raise ValueError("Cannot find variable '%s' !" % var)
if item:
if 'indx' in item:
item = self.get_item_by_index(item['indx'])
if attr == 'offset':
var = item['offset']
elif attr == 'length':
var = item['length']
elif attr == 'value':
var = self.get_cfg_item_value(item)
else:
raise ValueError("Unsupported variable attribute '%s' !" %
attr)
return var
def eval(self, expr):
def _handler(pattern):
if pattern.group(1):
target = 1
else:
target = 2
result = self.get_variable(pattern.group(target))
if result is None:
raise ValueError('Unknown variable $(%s) !' %
pattern.group(target))
return hex(result)
expr_eval = ExpressionEval()
if '$' in expr:
# replace known variable first
expr = re.sub(r'\$\(([_a-zA-Z][\w\.]*)\)|\$([_a-zA-Z][\w\.]*)',
_handler, expr)
return expr_eval.eval(expr, self.get_variable)
def parse_macros(self, macro_def_str):
# ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build']
self._macro_dict = {}
is_expression = False
for macro in macro_def_str:
if macro.startswith('-D'):
is_expression = True
if len(macro) > 2:
macro = macro[2:]
else:
continue
if is_expression:
is_expression = False
match = re.match("(\\w+)=(.+)", macro)
if match:
self._macro_dict[match.group(1)] = match.group(2)
else:
match = re.match("(\\w+)", macro)
if match:
self._macro_dict[match.group(1)] = ''
if len(self._macro_dict) == 0:
error = 1
else:
error = 0
if self._debug:
print("INFO : Macro dictionary:")
for each in self._macro_dict:
print(" $(%s) = [ %s ]"
% (each, self._macro_dict[each]))
return error
def get_cfg_list(self, page_id=None):
if page_id is None:
# return full list
return self._cfg_list
else:
# build a new list for items under a page ID
cfgs = [i for i in self._cfg_list if i['cname'] and
(i['page'] == page_id)]
return cfgs
def get_cfg_page(self):
return self._cfg_page
def get_cfg_item_length(self, item):
return item['length']
def get_cfg_item_value(self, item, array=False):
value_str = item['value']
length = item['length']
return self.get_value(value_str, length, array)
def format_value_to_str(self, value, bit_length, old_value=''):
# value is always int
length = (bit_length + 7) // 8
fmt = ''
if old_value.startswith('0x'):
fmt = '0x'
elif old_value and (old_value[0] in ['"', "'", '{']):
fmt = old_value[0]
else:
fmt = ''
bvalue = value_to_bytearray(value, length)
if fmt in ['"', "'"]:
svalue = bvalue.rstrip(b'\x00').decode()
value_str = fmt + svalue + fmt
elif fmt == "{":
value_str = '{ ' + ', '.join(['0x%02x' % i for i in bvalue]) + ' }'
elif fmt == '0x':
hex_len = length * 2
if len(old_value) == hex_len + 2:
fstr = '0x%%0%dx' % hex_len
else:
fstr = '0x%x'
value_str = fstr % value
else:
if length <= 2:
value_str = '%d' % value
elif length <= 8:
value_str = '0x%x' % value
else:
value_str = '{ ' + ', '.join(['0x%02x' % i for i in
bvalue]) + ' }'
return value_str
def reformat_value_str(self, value_str, bit_length, old_value=None):
value = self.parse_value(value_str, bit_length, False)
if old_value is None:
old_value = value_str
new_value = self.format_value_to_str(value, bit_length, old_value)
return new_value
def get_value(self, value_str, bit_length, array=True):
value_str = value_str.strip()
if value_str[0] == "'" and value_str[-1] == "'" or \
value_str[0] == '"' and value_str[-1] == '"':
value_str = value_str[1:-1]
bvalue = bytearray(value_str.encode())
if len(bvalue) == 0:
bvalue = bytearray(b'\x00')
if array:
return bvalue
else:
return bytes_to_value(bvalue)
else:
if value_str[0] in '{':
value_str = value_str[1:-1].strip()
value = 0
for each in value_str.split(',')[::-1]:
each = each.strip()
value = (value << 8) | int(each, 0)
if array:
length = (bit_length + 7) // 8
return value_to_bytearray(value, length)
else:
return value
def parse_value(self, value_str, bit_length, array=True):
length = (bit_length + 7) // 8
if check_quote(value_str):
value_str = bytes_to_bracket_str(value_str[1:-1].encode())
elif (',' in value_str) and (value_str[0] != '{'):
value_str = '{ %s }' % value_str
if value_str[0] == '{':
result = expand_file_value(self._yaml_path, value_str)
if len(result) == 0:
bin_list = value_str[1:-1].split(',')
value = 0
bit_len = 0
unit_len = 1
for idx, element in enumerate(bin_list):
each = element.strip()
if len(each) == 0:
continue
in_bit_field = False
if each[0] in "'" + '"':
each_value = bytearray(each[1:-1], 'utf-8')
elif ':' in each:
match = re.match("^(.+):(\\d+)([b|B|W|D|Q])$", each)
if match is None:
raise SystemExit("Exception: Invald value\
list format '%s' !" % each)
if match.group(1) == '0' and match.group(2) == '0':
unit_len = CGenYamlCfg.bits_width[match.group(3)
] // 8
cur_bit_len = int(match.group(2)
) * CGenYamlCfg.bits_width[
match.group(3)]
value += ((self.eval(match.group(1)) & (
1 << cur_bit_len) - 1)) << bit_len
bit_len += cur_bit_len
each_value = bytearray()
if idx + 1 < len(bin_list):
in_bit_field = True
else:
try:
each_value = value_to_bytearray(
self.eval(each.strip()), unit_len)
except Exception:
raise SystemExit("Exception: Value %d cannot \
fit into %s bytes !" % (each, unit_len))
if not in_bit_field:
if bit_len > 0:
if bit_len % 8 != 0:
raise SystemExit("Exception: Invalid bit \
field alignment '%s' !" % value_str)
result.extend(value_to_bytes(value, bit_len // 8))
value = 0
bit_len = 0
result.extend(each_value)
elif check_quote(value_str):
result = bytearray(value_str[1:-1], 'utf-8') # Excluding quotes
else:
result = value_to_bytearray(self.eval(value_str), length)
if len(result) < length:
result.extend(b'\x00' * (length - len(result)))
elif len(result) > length:
raise SystemExit("Exception: Value '%s' is too big to fit \
into %d bytes !" % (value_str, length))
if array:
return result
else:
return bytes_to_value(result)
return result
def get_cfg_item_options(self, item):
tmp_list = []
if item['type'] == "Combo":
if item['option'] in CGenYamlCfg.builtin_option:
for op_val, op_str in CGenYamlCfg.builtin_option[item['option'
]]:
tmp_list.append((op_val, op_str))
else:
if item['option'].find(';') != -1:
opt_list = item['option'].split(';')
else:
opt_list = re.split(', ', item['option'])
for option in opt_list:
option = option.strip()
try:
if option.find(':') != -1:
(op_val, op_str) = option.split(':')
else:
op_val = option
op_str = option
except Exception:
raise SystemExit("Exception: Invalid \
option format '%s' !" % option)
tmp_list.append((op_val, op_str))
return tmp_list
def get_page_title(self, page_id, top=None):
if top is None:
top = self.get_cfg_page()['root']
for node in top['child']:
page_key = next(iter(node))
if page_id == page_key:
return node[page_key]['title']
else:
result = self.get_page_title(page_id, node[page_key])
if result is not None:
return result
return None
def print_pages(self, top=None, level=0):
if top is None:
top = self.get_cfg_page()['root']
for node in top['child']:
page_id = next(iter(node))
print('%s%s: %s' % (' ' * level, page_id, node[page_id]['title']))
level += 1
self.print_pages(node[page_id], level)
level -= 1
def get_item_by_index(self, index):
return self._cfg_list[index]
def get_item_by_path(self, path):
node = self.locate_cfg_item(path)
if node:
return self.get_item_by_index(node['indx'])
else:
return None
def locate_cfg_path(self, item):
def _locate_cfg_path(root, level=0):
# config structure
if item is root:
return path
for key in root:
if type(root[key]) is OrderedDict:
level += 1
path.append(key)
ret = _locate_cfg_path(root[key], level)
if ret:
return ret
path.pop()
return None
path = []
return _locate_cfg_path(self._cfg_tree)
def locate_cfg_item(self, path, allow_exp=True):
def _locate_cfg_item(root, path, level=0):
if len(path) == level:
return root
next_root = root.get(path[level], None)
if next_root is None:
if allow_exp:
raise Exception('Not a valid CFG config option path: %s' %
'.'.join(path[:level+1]))
else:
return None
return _locate_cfg_item(next_root, path, level + 1)
path_nodes = path.split('.')
return _locate_cfg_item(self._cfg_tree, path_nodes)
def traverse_cfg_tree(self, handler, top=None):
def _traverse_cfg_tree(root, level=0):
# config structure
for key in root:
if type(root[key]) is OrderedDict:
level += 1
handler(key, root[key], level)
_traverse_cfg_tree(root[key], level)
level -= 1
if top is None:
top = self._cfg_tree
_traverse_cfg_tree(top)
def print_cfgs(self, root=None, short=True, print_level=256):
def _print_cfgs(name, cfgs, level):
if 'indx' in cfgs:
act_cfg = self.get_item_by_index(cfgs['indx'])
else:
offset = 0
length = 0
value = ''
if CGenYamlCfg.STRUCT in cfgs:
cfg = cfgs[CGenYamlCfg.STRUCT]
offset = int(cfg['offset'])
length = int(cfg['length'])
if 'value' in cfg:
value = cfg['value']
if length == 0:
return
act_cfg = dict({'value': value, 'offset': offset,
'length': length})
value = act_cfg['value']
bit_len = act_cfg['length']
offset = (act_cfg['offset'] + 7) // 8
if value != '':
try:
value = self.reformat_value_str(act_cfg['value'],
act_cfg['length'])
except Exception:
value = act_cfg['value']
length = bit_len // 8
bit_len = '(%db)' % bit_len if bit_len % 8 else '' * 4
if level <= print_level:
if short and len(value) > 40:
value = '%s ... %s' % (value[:20], value[-20:])
print('%04X:%04X%-6s %s%s : %s' % (offset, length, bit_len,
' ' * level, name, value))
self.traverse_cfg_tree(_print_cfgs)
def build_var_dict(self):
def _build_var_dict(name, cfgs, level):
if level <= 2:
if CGenYamlCfg.STRUCT in cfgs:
struct_info = cfgs[CGenYamlCfg.STRUCT]
self._var_dict['_LENGTH_%s_' % name] = struct_info[
'length'] // 8
self._var_dict['_OFFSET_%s_' % name] = struct_info[
'offset'] // 8
self._var_dict = {}
self.traverse_cfg_tree(_build_var_dict)
self._var_dict['_LENGTH_'] = self._cfg_tree[CGenYamlCfg.STRUCT][
'length'] // 8
return 0
def add_cfg_page(self, child, parent, title=''):
def _add_cfg_page(cfg_page, child, parent):
key = next(iter(cfg_page))
if parent == key:
cfg_page[key]['child'].append({child: {'title': title,
'child': []}})
return True
else:
result = False
for each in cfg_page[key]['child']:
if _add_cfg_page(each, child, parent):
result = True
break
return result
return _add_cfg_page(self._cfg_page, child, parent)
def set_cur_page(self, page_str):
if not page_str:
return
if ',' in page_str:
page_list = page_str.split(',')
else:
page_list = [page_str]
for page_str in page_list:
parts = page_str.split(':')
if len(parts) in [1, 3]:
page = parts[0].strip()
if len(parts) == 3:
# it is a new page definition, add it into tree
parent = parts[1] if parts[1] else 'root'
parent = parent.strip()
if parts[2][0] == '"' and parts[2][-1] == '"':
parts[2] = parts[2][1:-1]
if not self.add_cfg_page(page, parent, parts[2]):
raise SystemExit("Error: Cannot find parent page \
'%s'!" % parent)
else:
raise SystemExit("Error: Invalid page format '%s' !"
% page_str)
self._cur_page = page
def extend_variable(self, line):
# replace all variables
if line == '':
return line
loop = 2
while loop > 0:
line_after = DefTemplate(line).safe_substitute(self._def_dict)
if line == line_after:
break
loop -= 1
line = line_after
return line_after
def reformat_number_per_type(self, itype, value):
if check_quote(value) or value.startswith('{'):
return value
parts = itype.split(',')
if len(parts) > 3 and parts[0] == 'EditNum':
num_fmt = parts[1].strip()
else:
num_fmt = ''
if num_fmt == 'HEX' and not value.startswith('0x'):
value = '0x%X' % int(value, 10)
elif num_fmt == 'DEC' and value.startswith('0x'):
value = '%d' % int(value, 16)
return value
def add_cfg_item(self, name, item, offset, path):
self.set_cur_page(item.get('page', ''))
if name[0] == '$':
# skip all virtual node
return 0
if not set(item).issubset(CGenYamlCfg.keyword_set):
for each in list(item):
if each not in CGenYamlCfg.keyword_set:
raise Exception("Invalid attribute '%s' for '%s'!" %
(each, '.'.join(path)))
length = item.get('length', 0)
if type(length) is str:
match = re.match("^(\\d+)([b|B|W|D|Q])([B|W|D|Q]?)\\s*$", length)
if match:
unit_len = CGenYamlCfg.bits_width[match.group(2)]
length = int(match.group(1), 10) * unit_len
else:
try:
length = int(length, 0) * 8
except Exception:
raise Exception("Invalid length field '%s' for '%s' !" %
(length, '.'.join(path)))
if offset % 8 > 0:
raise Exception("Invalid alignment for field '%s' for \
'%s' !" % (name, '.'.join(path)))
else:
# define is length in bytes
length = length * 8
if not name.isidentifier():
raise Exception("Invalid config name '%s' for '%s' !" %
(name, '.'.join(path)))
itype = str(item.get('type', 'Reserved'))
value = str(item.get('value', ''))
if value:
if not (check_quote(value) or value.startswith('{')):
if ',' in value:
value = '{ %s }' % value
else:
value = self.reformat_number_per_type(itype, value)
help = str(item.get('help', ''))
if '\n' in help:
help = ' '.join([i.strip() for i in help.splitlines()])
option = str(item.get('option', ''))
if '\n' in option:
option = ' '.join([i.strip() for i in option.splitlines()])
# extend variables for value and condition
condition = str(item.get('condition', ''))
if condition:
condition = self.extend_variable(condition)
value = self.extend_variable(value)
order = str(item.get('order', ''))
if order:
if '.' in order:
(major, minor) = order.split('.')
order = int(major, 16)
else:
order = int(order, 16)
else:
order = offset
cfg_item = dict()
cfg_item['length'] = length
cfg_item['offset'] = offset
cfg_item['value'] = value
cfg_item['type'] = itype
cfg_item['cname'] = str(name)
cfg_item['name'] = str(item.get('name', ''))
cfg_item['help'] = help
cfg_item['option'] = option
cfg_item['page'] = self._cur_page
cfg_item['order'] = order
cfg_item['path'] = '.'.join(path)
cfg_item['condition'] = condition
if 'struct' in item:
cfg_item['struct'] = item['struct']
self._cfg_list.append(cfg_item)
item['indx'] = len(self._cfg_list) - 1
# remove used info for reducing pkl size
item.pop('option', None)
item.pop('condition', None)
item.pop('help', None)
item.pop('name', None)
item.pop('page', None)
return length
def build_cfg_list(self, cfg_name='', top=None, path=[],
info={'offset': 0}):
if top is None:
top = self._cfg_tree
info.clear()
info = {'offset': 0}
start = info['offset']
is_leaf = True
for key in top:
path.append(key)
if type(top[key]) is OrderedDict:
is_leaf = False
self.build_cfg_list(key, top[key], path, info)
path.pop()
if is_leaf:
length = self.add_cfg_item(cfg_name, top, info['offset'], path)
info['offset'] += length
elif cfg_name == '' or (cfg_name and cfg_name[0] != '$'):
# check first element for struct
first = next(iter(top))
struct_str = CGenYamlCfg.STRUCT
if first != struct_str:
struct_node = OrderedDict({})
top[struct_str] = struct_node
top.move_to_end(struct_str, False)
else:
struct_node = top[struct_str]
struct_node['offset'] = start
struct_node['length'] = info['offset'] - start
if struct_node['length'] % 8 != 0:
raise SystemExit("Error: Bits length not aligned for %s !" %
str(path))
def get_field_value(self, top=None):
def _get_field_value(name, cfgs, level):
if 'indx' in cfgs:
act_cfg = self.get_item_by_index(cfgs['indx'])
if act_cfg['length'] == 0:
return
value = self.get_value(act_cfg['value'], act_cfg['length'],
False)
set_bits_to_bytes(result, act_cfg['offset'] -
struct_info['offset'], act_cfg['length'],
value)
if top is None:
top = self._cfg_tree
struct_info = top[CGenYamlCfg.STRUCT]
result = bytearray((struct_info['length'] + 7) // 8)
self.traverse_cfg_tree(_get_field_value, top)
return result
data_diff = ''
def find_data_difference(self, act_val, act_cfg):
# checks for any difference between BSF and Binary file
config_val = ''
if act_val != act_cfg['value']:
if 'DEC' in act_cfg['type']:
bsf_val = '0x%x' % int(act_val)
if bsf_val != act_cfg['value']:
config_val = bsf_val
else:
config_val = ''
else:
config_val = act_val
available_fv1 = 'none'
available_fv2 = 'none'
if self.detect_fsp():
if len(self.available_fv) >= 1:
if len(self.available_fv) > 1:
available_fv1 = self.available_fv[1]
if self.available_fv[2]:
available_fv2 = self.available_fv[2]
else:
available_fv1 = self.available_fv[1]
if act_cfg['length'] == 16:
config_val = int(config_val, 16)
config_val = '0x%x' % config_val
act_cfg['value'] = int(
act_cfg['value'], 16)
act_cfg['value'] = '0x%x' % \
act_cfg['value']
if config_val:
string = ('.' + act_cfg['cname'])
if (act_cfg['path'].endswith(self.available_fv[0] + string)
or act_cfg['path'].endswith(available_fv1 + string)
or act_cfg['path'].endswith(available_fv2 + string)) \
and 'BsfSkip' not in act_cfg['cname'] \
and 'Reserved' not in act_cfg['name']:
if act_cfg['option'] != '':
if act_cfg['length'] == 8:
config_val = int(config_val, 16)
config_val = '0x%x' % config_val
act_cfg['value'] = int(
act_cfg['value'], 16)
act_cfg['value'] = '0x%x' % \
act_cfg['value']
option = act_cfg['option']
cfg_val = ''
bin_val = ''
for i in option.split(','):
if act_cfg['value'] in i:
bin_val = i
elif config_val in i:
cfg_val = i
if cfg_val != '' and bin_val != '':
self.data_diff += '\n\nBinary: ' \
+ act_cfg['name'] \
+ ': ' + bin_val.replace(' ', '') \
+ '\nConfig file: ' \
+ act_cfg['name'] + ': ' \
+ cfg_val.replace(' ', '') + '\n'
else:
self.data_diff += '\n\nBinary: ' \
+ act_cfg['name'] + ': ' + act_cfg['value'] \
+ '\nConfig file: ' + act_cfg['name'] \
+ ': ' + config_val + '\n'
def set_field_value(self, top, value_bytes, force=False):
def _set_field_value(name, cfgs, level):
if 'indx' not in cfgs:
return
act_cfg = self.get_item_by_index(cfgs['indx'])
actual_offset = act_cfg['offset'] - struct_info['offset']
if force or act_cfg['value'] == '':
value = get_bits_from_bytes(full_bytes,
actual_offset,
act_cfg['length'])
act_val = act_cfg['value']
if act_val == '':
act_val = '%d' % value
act_val = self.reformat_number_per_type(act_cfg
['type'],
act_val)
act_cfg['value'] = self.format_value_to_str(
value, act_cfg['length'], act_val)
self.find_data_difference(act_val, act_cfg)
if 'indx' in top:
# it is config option
value = bytes_to_value(value_bytes)
act_cfg = self.get_item_by_index(top['indx'])
act_cfg['value'] = self.format_value_to_str(
value, act_cfg['length'], act_cfg['value'])
else:
# it is structure
struct_info = top[CGenYamlCfg.STRUCT]
length = struct_info['length'] // 8
full_bytes = bytearray(value_bytes[:length])
if len(full_bytes) < length:
full_bytes.extend(bytearray(length - len(value_bytes)))
self.traverse_cfg_tree(_set_field_value, top)
def update_def_value(self):
def _update_def_value(name, cfgs, level):
if 'indx' in cfgs:
act_cfg = self.get_item_by_index(cfgs['indx'])
if act_cfg['value'] != '' and act_cfg['length'] > 0:
try:
act_cfg['value'] = self.reformat_value_str(
act_cfg['value'], act_cfg['length'])
except Exception:
raise Exception("Invalid value expression '%s' \
for '%s' !" % (act_cfg['value'], act_cfg['path']))
else:
if CGenYamlCfg.STRUCT in cfgs and 'value' in \
cfgs[CGenYamlCfg.STRUCT]:
curr = cfgs[CGenYamlCfg.STRUCT]
value_bytes = self.get_value(curr['value'],
curr['length'], True)
self.set_field_value(cfgs, value_bytes)
self.traverse_cfg_tree(_update_def_value, self._cfg_tree)
def evaluate_condition(self, item):
expr = item['condition']
result = self.parse_value(expr, 1, False)
return result
def detect_fsp(self):
cfg_segs = self.get_cfg_segment()
if len(cfg_segs) == 3:
fsp = True
for idx, seg in enumerate(cfg_segs):
if not seg[0].endswith('UPD_%s' % 'TMS'[idx]):
fsp = False
break
else:
fsp = False
if fsp:
self.set_mode('FSP')
return fsp
def get_cfg_segment(self):
def _get_cfg_segment(name, cfgs, level):
if 'indx' not in cfgs:
if name.startswith('$ACTION_'):
if 'find' in cfgs:
find[0] = cfgs['find']
else:
if find[0]:
act_cfg = self.get_item_by_index(cfgs['indx'])
segments.append([find[0], act_cfg['offset'] // 8, 0])
find[0] = ''
return
find = ['']
segments = []
self.traverse_cfg_tree(_get_cfg_segment, self._cfg_tree)
cfg_len = self._cfg_tree[CGenYamlCfg.STRUCT]['length'] // 8
if len(segments) == 0:
segments.append(['', 0, cfg_len])
segments.append(['', cfg_len, 0])
cfg_segs = []
for idx, each in enumerate(segments[:-1]):
cfg_segs.append((each[0], each[1],
segments[idx+1][1] - each[1]))
return cfg_segs
def get_bin_segment(self, bin_data):
cfg_segs = self.get_cfg_segment()
bin_segs = []
for seg in cfg_segs:
key = seg[0].encode()
if key == 0:
bin_segs.append([seg[0], 0, len(bin_data)])
break
pos = bin_data.find(key)
if pos >= 0:
# ensure no other match for the key
next_pos = bin_data.find(key, pos + len(seg[0]))
if next_pos >= 0:
if key == b'$SKLFSP$' or key == b'$BSWFSP$':
string = ('Warning: Multiple matches for %s in '
'binary!\n\nA workaround applied to such '
'FSP 1.x binary to use second'
' match instead of first match!' % key)
messagebox.showwarning('Warning!', string)
pos = next_pos
else:
print("Warning: Multiple matches for '%s' "
"in binary, the 1st instance will be used !"
% seg[0])
bin_segs.append([seg[0], pos, seg[2]])
self.binseg_dict[seg[0]] = pos
else:
bin_segs.append([seg[0], -1, seg[2]])
self.binseg_dict[seg[0]] = -1
continue
return bin_segs
available_fv = []
missing_fv = []
def extract_cfg_from_bin(self, bin_data):
# get cfg bin length
cfg_bins = bytearray()
bin_segs = self.get_bin_segment(bin_data)
Dummy_offset = 0
for each in bin_segs:
if each[1] != -1:
cfg_bins.extend(bin_data[each[1]:each[1] + each[2]])
self.available_fv.append(each[0])
else:
self.missing_fv.append(each[0])
string = each[0] + ' is not availabe.'
messagebox.showinfo('', string)
cfg_bins.extend(bytearray(each[2]))
Dummy_offset += each[2]
return cfg_bins
def save_current_to_bin(self):
cfg_bins = self.generate_binary_array()
if self._old_bin is None:
return cfg_bins
bin_data = bytearray(self._old_bin)
bin_segs = self.get_bin_segment(self._old_bin)
cfg_off = 0
for each in bin_segs:
length = each[2]
if each[1] != -1:
bin_data[each[1]:each[1] + length] = cfg_bins[cfg_off:
cfg_off
+ length]
cfg_off += length
else:
cfg_off += length
print('Patched the loaded binary successfully !')
return bin_data
def show_data_difference(self, data_diff):
# Displays if any data difference detected in BSF and Binary file
pop_up_text = 'There are differences in Config file and binary '\
'data detected!\n'
pop_up_text += data_diff
window = tkinter.Tk()
window.title("Data Difference")
window.resizable(1, 1)
# Window Size
window.geometry("800x400")
frame = tkinter.Frame(window, height=800, width=700)
frame.pack(side=tkinter.BOTTOM)
# Vertical (y) Scroll Bar
scroll = tkinter.Scrollbar(window)
scroll.pack(side=tkinter.RIGHT, fill=tkinter.Y)
text = tkinter.Text(window, wrap=tkinter.NONE,
yscrollcommand=scroll.set,
width=700, height=400)
text.insert(tkinter.INSERT, pop_up_text)
text.pack()
# Configure the scrollbars
scroll.config(command=text.yview)
exit_button = tkinter.Button(
window, text="Close", command=window.destroy)
exit_button.pack(in_=frame, side=tkinter.RIGHT, padx=20, pady=10)
def load_default_from_bin(self, bin_data):
self._old_bin = bin_data
cfg_bins = self.extract_cfg_from_bin(bin_data)
self.set_field_value(self._cfg_tree, cfg_bins, True)
if self.data_diff:
self.show_data_difference(self.data_diff)
return cfg_bins
def generate_binary_array(self, path=''):
if path == '':
top = None
else:
top = self.locate_cfg_item(path)
if not top:
raise Exception("Invalid configuration path '%s' !"
% path)
return self.get_field_value(top)
def generate_binary(self, bin_file_name, path=''):
bin_file = open(bin_file_name, "wb")
bin_file.write(self.generate_binary_array(path))
bin_file.close()
return 0
def write_delta_file(self, out_file, platform_id, out_lines):
dlt_fd = open(out_file, "w")
dlt_fd.write("%s\n" % get_copyright_header('dlt', True))
if platform_id is not None:
dlt_fd.write('#\n')
dlt_fd.write('# Delta configuration values for '
'platform ID 0x%04X\n'
% platform_id)
dlt_fd.write('#\n\n')
for line in out_lines:
dlt_fd.write('%s\n' % line)
dlt_fd.close()
def override_default_value(self, dlt_file):
error = 0
dlt_lines = CGenYamlCfg.expand_include_files(dlt_file)
platform_id = None
for line, file_path, line_num in dlt_lines:
line = line.strip()
if not line or line.startswith('#'):
continue
match = re.match("\\s*([\\w\\.]+)\\s*\\|\\s*(.+)", line)
if not match:
raise Exception("Unrecognized line '%s' "
"(File:'%s' Line:%d) !"
% (line, file_path, line_num + 1))
path = match.group(1)
value_str = match.group(2)
top = self.locate_cfg_item(path)
if not top:
raise Exception(
"Invalid configuration '%s' (File:'%s' Line:%d) !" %
(path, file_path, line_num + 1))
if 'indx' in top:
act_cfg = self.get_item_by_index(top['indx'])
bit_len = act_cfg['length']
else:
struct_info = top[CGenYamlCfg.STRUCT]
bit_len = struct_info['length']
value_bytes = self.parse_value(value_str, bit_len)
self.set_field_value(top, value_bytes, True)
if path == 'PLATFORMID_CFG_DATA.PlatformId':
platform_id = value_str
if platform_id is None:
raise Exception(
"PLATFORMID_CFG_DATA.PlatformId is missing "
"in file '%s' !" %
(dlt_file))
return error
def generate_delta_file_from_bin(self, delta_file, old_data,
new_data, full=False):
new_data = self.load_default_from_bin(new_data)
lines = []
platform_id = None
def_platform_id = 0
for item in self._cfg_list:
if not full and (item['type'] in ['Reserved']):
continue
old_val = get_bits_from_bytes(old_data, item['offset'],
item['length'])
new_val = get_bits_from_bytes(new_data, item['offset'],
item['length'])
full_name = item['path']
if 'PLATFORMID_CFG_DATA.PlatformId' == full_name:
def_platform_id = old_val
if new_val != old_val or full:
val_str = self.reformat_value_str(item['value'],
item['length'])
text = '%-40s | %s' % (full_name, val_str)
lines.append(text)
if self.get_mode() != 'FSP':
if platform_id is None or def_platform_id == platform_id:
platform_id = def_platform_id
print("WARNING: 'PlatformId' configuration is "
"same as default %d!" % platform_id)
lines.insert(0, '%-40s | %s\n\n' %
('PLATFORMID_CFG_DATA.PlatformId',
'0x%04X' % platform_id))
else:
platform_id = None
self.write_delta_file(delta_file, platform_id, lines)
return 0
def generate_delta_file(self, delta_file, bin_file, bin_file2, full=False):
fd = open(bin_file, 'rb')
new_data = self.extract_cfg_from_bin(bytearray(fd.read()))
fd.close()
if bin_file2 == '':
old_data = self.generate_binary_array()
else:
old_data = new_data
fd = open(bin_file2, 'rb')
new_data = self.extract_cfg_from_bin(bytearray(fd.read()))
fd.close()
return self.generate_delta_file_from_bin(delta_file,
old_data, new_data, full)
def prepare_marshal(self, is_save):
if is_save:
# Ordered dict is not marshallable, convert to list
self._cfg_tree = CGenYamlCfg.deep_convert_dict(self._cfg_tree)
else:
# Revert it back
self._cfg_tree = CGenYamlCfg.deep_convert_list(self._cfg_tree)
def generate_yml_file(self, in_file, out_file):
cfg_yaml = CFG_YAML()
text = cfg_yaml.expand_yaml(in_file)
yml_fd = open(out_file, "w")
yml_fd.write(text)
yml_fd.close()
return 0
def write_cfg_header_file(self, hdr_file_name, tag_mode,
tag_dict, struct_list):
lines = []
lines.append('\n\n')
if self.get_mode() == 'FSP':
lines.append('#include <FspUpd.h>\n')
tag_mode = tag_mode & 0x7F
tag_list = sorted(list(tag_dict.items()), key=lambda x: x[1])
for tagname, tagval in tag_list:
if (tag_mode == 0 and tagval >= 0x100) or \
(tag_mode == 1 and tagval < 0x100):
continue
lines.append('#define %-30s 0x%03X\n' % (
'CDATA_%s_TAG' % tagname[:-9], tagval))
lines.append('\n\n')
name_dict = {}
new_dict = {}
for each in struct_list:
if (tag_mode == 0 and each['tag'] >= 0x100) or \
(tag_mode == 1 and each['tag'] < 0x100):
continue
new_dict[each['name']] = (each['alias'], each['count'])
if each['alias'] not in name_dict:
name_dict[each['alias']] = 1
lines.extend(self.create_struct(each['alias'],
each['node'], new_dict))
lines.append('#pragma pack()\n\n')
self.write_header_file(lines, hdr_file_name)
def write_header_file(self, txt_body, file_name, type='h'):
file_name_def = os.path.basename(file_name).replace('.', '_')
file_name_def = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', file_name_def)
file_name_def = re.sub('([a-z0-9])([A-Z])', r'\1_\2',
file_name_def).upper()
lines = []
lines.append("%s\n" % get_copyright_header(type))
lines.append("#ifndef __%s__\n" % file_name_def)
lines.append("#define __%s__\n\n" % file_name_def)
if type == 'h':
lines.append("#pragma pack(1)\n\n")
lines.extend(txt_body)
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(file_name):
hdr_file = open(file_name, "r")
org_txt = hdr_file.read()
hdr_file.close()
new_txt = ''.join(lines)
if org_txt == new_txt:
create = False
if create:
hdr_file = open(file_name, "w")
hdr_file.write(''.join(lines))
hdr_file.close()
def generate_data_inc_file(self, dat_inc_file_name, bin_file=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 bin_file:
fin = open(bin_file, 'rb')
bin_dat = prefix + bytearray(fin.read())
fin.close()
else:
bin_dat = prefix + self.generate_binary_array()
file_name = os.path.basename(dat_inc_file_name).upper()
file_name = file_name.replace('.', '_')
txt_lines = []
txt_lines.append("UINT8 mConfigDataBlob[%d] = {\n" % len(bin_dat))
count = 0
line = [' ']
for each in bin_dat:
line.append('0x%02X, ' % each)
count = count + 1
if (count & 0x0F) == 0:
line.append('\n')
txt_lines.append(''.join(line))
line = [' ']
if len(line) > 1:
txt_lines.append(''.join(line) + '\n')
txt_lines.append("};\n\n")
self.write_header_file(txt_lines, dat_inc_file_name, 'inc')
return 0
def get_struct_array_info(self, input):
parts = input.split(':')
if len(parts) > 1:
var = parts[1]
input = parts[0]
else:
var = ''
array_str = input.split('[')
name = array_str[0]
if len(array_str) > 1:
num_str = ''.join(c for c in array_str[-1] if c.isdigit())
num_str = '1000' if len(num_str) == 0 else num_str
array_num = int(num_str)
else:
array_num = 0
return name, array_num, var
def process_multilines(self, string, max_char_length):
multilines = ''
string_length = len(string)
current_string_start = 0
string_offset = 0
break_line_dict = []
if len(string) <= max_char_length:
while (string_offset < string_length):
if string_offset >= 1:
if string[string_offset - 1] == '\\' and string[
string_offset] == 'n':
break_line_dict.append(string_offset + 1)
string_offset += 1
if break_line_dict != []:
for each in break_line_dict:
multilines += " %s\n" % string[
current_string_start:each].lstrip()
current_string_start = each
if string_length - current_string_start > 0:
multilines += " %s\n" % string[
current_string_start:].lstrip()
else:
multilines = " %s\n" % string
else:
new_line_start = 0
new_line_count = 0
found_space_char = False
while (string_offset < string_length):
if string_offset >= 1:
if new_line_count >= max_char_length - 1:
if string[string_offset] == ' ' and \
string_length - string_offset > 10:
break_line_dict.append(new_line_start
+ new_line_count)
new_line_start = new_line_start + new_line_count
new_line_count = 0
found_space_char = True
elif string_offset == string_length - 1 and \
found_space_char is False:
break_line_dict.append(0)
if string[string_offset - 1] == '\\' and string[
string_offset] == 'n':
break_line_dict.append(string_offset + 1)
new_line_start = string_offset + 1
new_line_count = 0
string_offset += 1
new_line_count += 1
if break_line_dict != []:
break_line_dict.sort()
for each in break_line_dict:
if each > 0:
multilines += " %s\n" % string[
current_string_start:each].lstrip()
current_string_start = each
if string_length - current_string_start > 0:
multilines += " %s\n" % \
string[current_string_start:].lstrip()
return multilines
def create_field(self, item, name, length, offset, struct,
bsf_name, help, option, bits_length=None):
pos_name = 28
name_line = ''
# help_line = ''
# option_line = ''
if length == 0 and name == 'dummy':
return '\n'
if bits_length == 0:
return '\n'
is_array = False
if length in [1, 2, 4, 8]:
type = "UINT%d" % (length * 8)
else:
is_array = True
type = "UINT8"
if item and item['value'].startswith('{'):
type = "UINT8"
is_array = True
if struct != '':
struct_base = struct.rstrip('*')
name = '*' * (len(struct) - len(struct_base)) + name
struct = struct_base
type = struct
if struct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
is_array = True
unit = int(type[4:]) // 8
length = length / unit
else:
is_array = False
if is_array:
name = name + '[%d]' % length
if len(type) < pos_name:
space1 = pos_name - len(type)
else:
space1 = 1
if bsf_name != '':
name_line = " %s\n" % bsf_name
else:
name_line = "N/A\n"
# if help != '':
# help_line = self.process_multilines(help, 80)
# if option != '':
# option_line = self.process_multilines(option, 80)
if offset is None:
offset_str = '????'
else:
offset_str = '0x%04X' % offset
if bits_length is None:
bits_length = ''
else:
bits_length = ' : %d' % bits_length
# return "\n/** %s%s%s**/\n %s%s%s%s;\n" % (name_line, help_line,
# option_line, type, ' ' * space1, name, bits_length)
return "\n /* Offset %s: %s */\n %s%s%s%s;\n" % (
offset_str, name_line.strip(), type, ' ' * space1,
name, bits_length)
def create_struct(self, cname, top, struct_dict):
index = 0
last = ''
lines = []
off_base = -1
if cname in struct_dict:
if struct_dict[cname][2]:
return []
lines.append('\ntypedef struct {\n')
for field in top:
if field[0] == '$':
continue
index += 1
t_item = top[field]
if 'indx' not in t_item:
if CGenYamlCfg.STRUCT not in top[field]:
continue
if struct_dict[field][1] == 0:
continue
append = True
struct_info = top[field][CGenYamlCfg.STRUCT]
if 'struct' in struct_info:
struct, array_num, var = self.get_struct_array_info(
struct_info['struct'])
if array_num > 0:
if last == struct:
append = False
last = struct
if var == '':
var = field
field = CGenYamlCfg.format_struct_field_name(
var, struct_dict[field][1])
else:
struct = struct_dict[field][0]
field = CGenYamlCfg.format_struct_field_name(
field, struct_dict[field][1])
if append:
offset = t_item['$STRUCT']['offset'] // 8
if off_base == -1:
off_base = offset
line = self.create_field(None, field, 0, 0, struct,
'', '', '')
lines.append(' %s' % line)
last = struct
continue
item = self.get_item_by_index(t_item['indx'])
if item['cname'] == 'CfgHeader' and index == 1 or \
(item['cname'] == 'CondValue' and index == 2):
continue
bit_length = None
length = (item['length'] + 7) // 8
match = re.match("^(\\d+)([b|B|W|D|Q])([B|W|D|Q]?)",
t_item['length'])
if match and match.group(2) == 'b':
bit_length = int(match.group(1))
if match.group(3) != '':
length = CGenYamlCfg.bits_width[match.group(3)] // 8
else:
length = 4
offset = item['offset'] // 8
if off_base == -1:
off_base = offset
struct = item.get('struct', '')
name = field
prompt = item['name']
help = item['help']
option = item['option']
line = self.create_field(item, name, length, offset, struct,
prompt, help, option, bit_length)
lines.append(' %s' % line)
last = struct
lines.append('\n} %s;\n\n' % cname)
return lines
def write_fsp_sig_header_file(self, hdr_file_name):
hdr_fd = open(hdr_file_name, 'w')
hdr_fd.write("%s\n" % get_copyright_header('h'))
hdr_fd.write("#ifndef __FSPUPD_H__\n"
"#define __FSPUPD_H__\n\n"
"#include <FspEas.h>\n\n"
"#pragma pack(1)\n\n")
lines = []
for fsp_comp in 'TMS':
top = self.locate_cfg_item('FSP%s_UPD' % fsp_comp)
if not top:
raise Exception('Could not find FSP UPD definition !')
bins = self.get_field_value(top)
lines.append("#define FSP%s_UPD_SIGNATURE"
" 0x%016X /* '%s' */\n\n"
% (fsp_comp, bytes_to_value(bins[:8]),
bins[:8].decode()))
hdr_fd.write(''.join(lines))
hdr_fd.write("#pragma pack()\n\n"
"#endif\n")
hdr_fd.close()
def create_header_file(self, hdr_file_name, com_hdr_file_name='', path=''):
def _build_header_struct(name, cfgs, level):
if CGenYamlCfg.STRUCT in cfgs:
if 'CfgHeader' in cfgs:
# collect CFGDATA TAG IDs
cfghdr = self.get_item_by_index(cfgs['CfgHeader']['indx'])
tag_val = array_str_to_value(cfghdr['value']) >> 20
tag_dict[name] = tag_val
if level == 1:
tag_curr[0] = tag_val
struct_dict[name] = (level, tag_curr[0], cfgs)
if path == 'FSP_SIG':
self.write_fsp_sig_header_file(hdr_file_name)
return
tag_curr = [0]
tag_dict = {}
struct_dict = {}
if path == '':
top = None
else:
top = self.locate_cfg_item(path)
if not top:
raise Exception("Invalid configuration path '%s' !" % path)
_build_header_struct(path, top, 0)
self.traverse_cfg_tree(_build_header_struct, top)
if tag_curr[0] == 0:
hdr_mode = 2
else:
hdr_mode = 1
if re.match('FSP[TMS]_UPD', path):
hdr_mode |= 0x80
# filter out the items to be built for tags and structures
struct_list = []
for each in struct_dict:
match = False
for check in CGenYamlCfg.exclude_struct:
if re.match(check, each):
match = True
if each in tag_dict:
if each not in CGenYamlCfg.include_tag:
del tag_dict[each]
break
if not match:
struct_list.append({'name': each, 'alias': '', 'count': 0,
'level': struct_dict[each][0],
'tag': struct_dict[each][1],
'node': struct_dict[each][2]})
# sort by level so that the bottom level struct
# will be build first to satisfy dependencies
struct_list = sorted(struct_list, key=lambda x: x['level'],
reverse=True)
# Convert XXX_[0-9]+ to XXX as an array hint
for each in struct_list:
cfgs = each['node']
if 'struct' in cfgs['$STRUCT']:
each['alias'], array_num, var = self.get_struct_array_info(
cfgs['$STRUCT']['struct'])
else:
match = re.match('(\\w+)(_\\d+)', each['name'])
if match:
each['alias'] = match.group(1)
else:
each['alias'] = each['name']
# count items for array build
for idx, each in enumerate(struct_list):
if idx > 0:
last_struct = struct_list[idx-1]['node']['$STRUCT']
curr_struct = each['node']['$STRUCT']
if struct_list[idx-1]['alias'] == each['alias'] and \
curr_struct['length'] == last_struct['length'] and \
curr_struct['offset'] == last_struct['offset'] + \
last_struct['length']:
for idx2 in range(idx-1, -1, -1):
if struct_list[idx2]['count'] > 0:
struct_list[idx2]['count'] += 1
break
continue
each['count'] = 1
# generate common header
if com_hdr_file_name:
self.write_cfg_header_file(com_hdr_file_name, 0, tag_dict,
struct_list)
# generate platform header
self.write_cfg_header_file(hdr_file_name, hdr_mode, tag_dict,
struct_list)
return 0
def load_yaml(self, cfg_file):
cfg_yaml = CFG_YAML()
self.initialize()
self._cfg_tree = cfg_yaml.load_yaml(cfg_file)
self._def_dict = cfg_yaml.def_dict
self._yaml_path = os.path.dirname(cfg_file)
self.build_cfg_list()
self.build_var_dict()
self.update_def_value()
return 0
def usage():
print('\n'.join([
"GenYamlCfg Version 0.50",
"Usage:",
" GenYamlCfg GENINC BinFile IncOutFile "
" [-D Macros]",
" GenYamlCfg GENPKL YamlFile PklOutFile "
" [-D Macros]",
" GenYamlCfg GENBIN YamlFile[;DltFile] BinOutFile "
" [-D Macros]",
" GenYamlCfg GENDLT YamlFile[;BinFile] DltOutFile "
" [-D Macros]",
" GenYamlCfg GENYML YamlFile YamlOutFile"
" [-D Macros]",
" GenYamlCfg GENHDR YamlFile HdrOutFile "
" [-D Macros]"
]))
def main():
# Parse the options and args
argc = len(sys.argv)
if argc < 4:
usage()
return 1
gen_cfg_data = CGenYamlCfg()
command = sys.argv[1].upper()
out_file = sys.argv[3]
if argc >= 5 and gen_cfg_data.parse_macros(sys.argv[4:]) != 0:
raise Exception("ERROR: Macro parsing failed !")
file_list = sys.argv[2].split(';')
if len(file_list) >= 2:
yml_file = file_list[0]
dlt_file = file_list[1]
elif len(file_list) == 1:
yml_file = file_list[0]
dlt_file = ''
else:
raise Exception("ERROR: Invalid parameter '%s' !" % sys.argv[2])
yml_scope = ''
if '@' in yml_file:
parts = yml_file.split('@')
yml_file = parts[0]
yml_scope = parts[1]
if command == "GENDLT" and yml_file.endswith('.dlt'):
# It needs to expand an existing DLT file
dlt_file = yml_file
lines = gen_cfg_data.expand_include_files(dlt_file)
write_lines(lines, out_file)
return 0
if command == "GENYML":
if not yml_file.lower().endswith('.yaml'):
raise Exception('Only YAML file is supported !')
gen_cfg_data.generate_yml_file(yml_file, out_file)
return 0
bin_file = ''
if (yml_file.lower().endswith('.bin')) and (command == "GENINC"):
# It is binary file
bin_file = yml_file
yml_file = ''
if bin_file:
gen_cfg_data.generate_data_inc_file(out_file, bin_file)
return 0
cfg_bin_file = ''
cfg_bin_file2 = ''
if dlt_file:
if command == "GENDLT":
cfg_bin_file = dlt_file
dlt_file = ''
if len(file_list) >= 3:
cfg_bin_file2 = file_list[2]
if yml_file.lower().endswith('.pkl'):
with open(yml_file, "rb") as pkl_file:
gen_cfg_data.__dict__ = marshal.load(pkl_file)
gen_cfg_data.prepare_marshal(False)
# Override macro definition again for Pickle file
if argc >= 5:
gen_cfg_data.parse_macros(sys.argv[4:])
else:
gen_cfg_data.load_yaml(yml_file)
if command == 'GENPKL':
gen_cfg_data.prepare_marshal(True)
with open(out_file, "wb") as pkl_file:
marshal.dump(gen_cfg_data.__dict__, pkl_file)
json_file = os.path.splitext(out_file)[0] + '.json'
fo = open(json_file, 'w')
path_list = []
cfgs = {'_cfg_page': gen_cfg_data._cfg_page,
'_cfg_list': gen_cfg_data._cfg_list,
'_path_list': path_list}
# optimize to reduce size
path = None
for each in cfgs['_cfg_list']:
new_path = each['path'][:-len(each['cname'])-1]
if path != new_path:
path = new_path
each['path'] = path
path_list.append(path)
else:
del each['path']
if each['order'] == each['offset']:
del each['order']
del each['offset']
# value is just used to indicate display type
value = each['value']
if value.startswith('0x'):
hex_len = ((each['length'] + 7) // 8) * 2
if len(value) == hex_len:
value = 'x%d' % hex_len
else:
value = 'x'
each['value'] = value
elif value and value[0] in ['"', "'", '{']:
each['value'] = value[0]
else:
del each['value']
fo.write(repr(cfgs))
fo.close()
return 0
if dlt_file:
gen_cfg_data.override_default_value(dlt_file)
gen_cfg_data.detect_fsp()
if command == "GENBIN":
if len(file_list) == 3:
old_data = gen_cfg_data.generate_binary_array()
fi = open(file_list[2], 'rb')
new_data = bytearray(fi.read())
fi.close()
if len(new_data) != len(old_data):
raise Exception("Binary file '%s' length does not match, \
ignored !" % file_list[2])
else:
gen_cfg_data.load_default_from_bin(new_data)
gen_cfg_data.override_default_value(dlt_file)
gen_cfg_data.generate_binary(out_file, yml_scope)
elif command == "GENDLT":
full = True if 'FULL' in gen_cfg_data._macro_dict else False
gen_cfg_data.generate_delta_file(out_file, cfg_bin_file,
cfg_bin_file2, full)
elif command == "GENHDR":
out_files = out_file.split(';')
brd_out_file = out_files[0].strip()
if len(out_files) > 1:
com_out_file = out_files[1].strip()
else:
com_out_file = ''
gen_cfg_data.create_header_file(brd_out_file, com_out_file, yml_scope)
elif command == "GENINC":
gen_cfg_data.generate_data_inc_file(out_file)
elif command == "DEBUG":
gen_cfg_data.print_cfgs()
else:
raise Exception("Unsuported command '%s' !" % command)
return 0
if __name__ == '__main__':
sys.exit(main())