BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3396 This is a GUI interface that can be used by users who would like to change configuration settings directly from the interface without having to modify the source. This tool depends on Python GUI tool kit Tkinter. It runs on both Windows and Linux. The user needs to load the YAML file along with DLT file for a specific board into the ConfigEditor, change the desired configuration values. Finally, generate a new configuration delta file or a config binary blob for the newly changed values to take effect. These will be the inputs to the merge tool or the stitch tool so that new config changes can be merged and stitched into the final configuration blob. This tool also supports binary update directly and display FSP information. It is also backward compatible for BSF file format. Running Configuration Editor: python ConfigEditor.py Co-authored-by: Maurice Ma <maurice.ma@intel.com> Cc: Maurice Ma <maurice.ma@intel.com> Cc: Nate DeSimone <nathaniel.l.desimone@intel.com> Cc: Star Zeng <star.zeng@intel.com> Cc: Chasel Chiu <chasel.chiu@intel.com> Signed-off-by: Loo Tung Lun <tung.lun.loo@intel.com> Reviewed-by: Chasel Chiu <chasel.chiu@intel.com>
2253 lines
81 KiB
Python
2253 lines
81 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
|
|
|
|
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.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:
|
|
opt_list = item['option'].split(',')
|
|
for option in opt_list:
|
|
option = option.strip()
|
|
try:
|
|
(op_val, op_str) = option.split(':')
|
|
except Exception:
|
|
raise SystemExit("Exception: Invalide \
|
|
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
|
|
|
|
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'])
|
|
if force or act_cfg['value'] == '':
|
|
value = get_bits_from_bytes(full_bytes,
|
|
act_cfg['offset'] -
|
|
struct_info['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)
|
|
|
|
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]])
|
|
else:
|
|
raise Exception("Could not find '%s' in binary !"
|
|
% seg[0])
|
|
|
|
return bin_segs
|
|
|
|
def extract_cfg_from_bin(self, bin_data):
|
|
# get cfg bin length
|
|
cfg_bins = bytearray()
|
|
bin_segs = self.get_bin_segment(bin_data)
|
|
for each in bin_segs:
|
|
cfg_bins.extend(bin_data[each[1]:each[1] + 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]
|
|
bin_data[each[1]:each[1] + length] = cfg_bins[cfg_off:
|
|
cfg_off
|
|
+ length]
|
|
cfg_off += length
|
|
print('Patched the loaded binary successfully !')
|
|
|
|
return bin_data
|
|
|
|
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)
|
|
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())
|