mirror of https://github.com/acidanthera/audk.git
2386 lines
86 KiB
Python
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())
|