| # @ 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 | |
| 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.value | |
| 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' | |
| FORMSET = 'formset' | |
| 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.yaml_type = 'fsp' | |
| 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 = {} | |
| temp_data = [] | |
| while True: | |
| line = self.get_line() | |
| if line is None: | |
| break | |
| curr_line = line.strip() | |
| if curr_line == "## DO NOT REMOVE -- YAML Mode": | |
| self.yaml_type = "vfr" | |
| 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] | |
| start = 1 if marker1 == '-' else 0 | |
| pos = curr_line.find(': ') | |
| if marker1 == '-': | |
| marker2 = curr_line[curr_line.find(":")] | |
| pos = -1 | |
| else: | |
| marker2 = curr_line[-1] | |
| 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 self.yaml_type =='fsp': | |
| 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.yaml_type == 'vfr': | |
| if key in curr.keys(): | |
| if type(curr[key]) == type([]): | |
| temp_data = curr[key] | |
| else: | |
| temp_data.append(curr[key]) | |
| temp_data.append(child) | |
| if level < 5: | |
| curr[key] = temp_data | |
| temp_data = [] | |
| else: | |
| if level < 5: | |
| 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 | |
| if self.tmp_tree and key == CFG_YAML.FORMSET: | |
| 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() | |
| for key in yaml_tree.keys(): | |
| if key.lower() == "configs": | |
| self.yaml_type = 'fsp' | |
| self.tmp_tree = yaml_tree[CFG_YAML.TEMPLATE] | |
| self.cfg_tree = yaml_tree[CFG_YAML.CONFIGS] | |
| break | |
| else: | |
| self.cfg_tree = yaml_tree | |
| break | |
| if self.yaml_type == 'vfr': | |
| formset_found = True | |
| for key in yaml_tree.keys(): | |
| if key == CFG_YAML.FORMSET: | |
| self.cfg_tree = yaml_tree[CFG_YAML.FORMSET] | |
| formset_found = False | |
| break | |
| if formset_found == True: | |
| self.cfg_tree = yaml_tree | |
| elif self.yaml_type == 'fsp': | |
| 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', | |
| 'FSPI_ARCH_UPD', 'FSPT_ARCH2_UPD', | |
| 'FSPM_ARCH2_UPD', 'FSPS_ARCH2_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() | |
| self._tk = self.import_tkinter() | |
| 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._main_page = '' | |
| self._var_dict = {} | |
| self._def_dict = {} | |
| self._yaml_path = '' | |
| self.yaml_type = '' | |
| #Added to overcome duplicate formid | |
| self.form_page_map = {} | |
| self.formset_level = 0 | |
| @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) | |
| # | |
| # FSP[T/M/S]_ARCH2_UPD struct field name is Fsp[t/m/s]Arch2Upd, | |
| # it should change to Fsp[t/m/s]ArchUpd for code compatibility. | |
| # | |
| name = re.sub(r'(Fsp[tms]Arch)2Upd', r'\1Upd', name) | |
| 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): | |
| cfgs = [] | |
| if page_id is None: | |
| # return full list | |
| return self._cfg_list | |
| else: | |
| if self.yaml_type == 'fsp': | |
| # 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)] | |
| #VFR YAML Support Start | |
| elif self.yaml_type =='vfr': | |
| for cfg in self._cfg_list: | |
| for i in cfg: | |
| if (i['page'] == page_id): | |
| cfgs.append(i) | |
| #VFR YAML Support End | |
| 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 | |
| if type(root) == type([]): | |
| for temp_root in root: | |
| return _locate_cfg_item(temp_root, path, level) | |
| 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 != '' and 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 name != '' and 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)) | |
| #EDK2 VFR YAML Support start | |
| def build_formset_list(self, form_name='', top=None, parent_form='',path =[]): | |
| if self.formset_level == 1: | |
| self._cfg_page['root']['title'] = 'Platform' | |
| self._cfg_page['root']['child'].append({form_name: {'title': form_name, | |
| 'child': []}}) | |
| self._main_page = form_name | |
| if top is None: | |
| top = self._cfg_tree | |
| form_name = "Formset" | |
| self._cfg_page['root']['title'] = 'Formset' | |
| is_leaf = True | |
| if form_name == "form" or form_name == "formid": | |
| self._cur_page = top["title"].split('#')[0] | |
| self.form_page_map[top['formid'].split('#')[0]] = self._cur_page | |
| for driver in self._cfg_page['root']['child']: | |
| if list(driver.keys())[0] == self._main_page: | |
| driver[self._main_page]['child'].append({self._cur_page: {'title': self._cur_page, 'child': []}}) | |
| if form_name == "formmap": | |
| self._cur_page = top["formid"].split('#')[0] | |
| self.form_page_map[top['FormId'].split('#')[0]] = self._cur_page | |
| self._cfg_page['root']['child'].append({self._cur_page: {'title': self._cur_page, | |
| 'child': []}}) | |
| form_data = {} | |
| temp_data = [] | |
| for key in top: | |
| if key == 'include': | |
| form_data['type'] = key | |
| form_data["page"] = self._cur_page | |
| continue | |
| if type(top[key]) is list and self.formset_level <= 3: | |
| self.formset_level += 1 | |
| path.append(key) | |
| for data in top[key]: | |
| self.build_formset_list(key, data, key, path) | |
| path.pop() | |
| self.formset_level -= 1 | |
| elif type(top[key]) is OrderedDict and (self.formset_level <= 3): | |
| if parent_form != '': | |
| self.formset_level += 1 | |
| path.append(key) | |
| self.build_formset_list(key, top[key], form_name, path) | |
| path.pop() | |
| self.formset_level -= 1 | |
| else: | |
| self.formset_level += 1 | |
| path.append(key) | |
| self.build_formset_list(key, top[key], key, path) | |
| path.pop() | |
| self.formset_level -= 1 | |
| else: | |
| form_data["page"] = self._cur_page | |
| form_data[key] = top[key] | |
| form_data['path'] = ".".join(path) | |
| if form_name != 'form' or form_name != "formid": | |
| form_data["type"] = form_name | |
| else: | |
| form_data["type"] = " " | |
| count = 0 | |
| if self._cfg_list != []: | |
| for cfg_name in self._cfg_list: | |
| for list_data in cfg_name: | |
| if key == list_data['type']: | |
| count +=1 | |
| if count > 1: | |
| temp_data = cfg_name | |
| if len(temp_data) != 0 or len(form_data)!=0: | |
| temp_data.append(form_data) | |
| self._cfg_list.append(temp_data) | |
| return | |
| #EDK2 VFR YAML Support End | |
| 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' % 'TMSI'[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 import_tkinter(self): | |
| try: | |
| import tkinter | |
| import tkinter.messagebox | |
| return tkinter | |
| except ImportError: | |
| print('tkinter is not supported under current OS') | |
| return None | |
| 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 = ('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) | |
| if self._tk: | |
| self._tk.messagebox.showwarning('Warning: ', string) | |
| else: | |
| print('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.' | |
| if self._tk: | |
| self._tk.messagebox.showinfo('', string) | |
| else: | |
| print('warning: ', 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): | |
| if self._tk is None: | |
| return | |
| # 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 = self._tk.Tk() | |
| window.title("Data Difference") | |
| window.resizable(1, 1) | |
| # Window Size | |
| window.geometry("800x400") | |
| frame = self._tk.Frame(window, height=800, width=700) | |
| frame.pack(side=self._tk.BOTTOM) | |
| # Vertical (y) Scroll Bar | |
| scroll = self._tk.Scrollbar(window) | |
| scroll.pack(side=self._tk.RIGHT, fill=self._tk.Y) | |
| text = self._tk.Text(window, wrap=self._tk.NONE, | |
| yscrollcommand=scroll.set, | |
| width=700, height=400) | |
| text.insert(self._tk.INSERT, pop_up_text) | |
| text.pack() | |
| # Configure the scrollbars | |
| scroll.config(command=text.yview) | |
| exit_button = self._tk.Button( | |
| window, text="Close", command=window.destroy) | |
| exit_button.pack(in_=frame, side=self._tk.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') | |
| 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#pragma pack(1)\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'], each['exclude']) | |
| 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" % file_name_def) | |
| lines.extend(txt_body) | |
| 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) | |
| 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 - off_base, 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 'TMSI': | |
| 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[TMSI]_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 | |
| struct_list.append({'name': each, 'alias': '', 'count': 0, | |
| 'level': struct_dict[each][0], | |
| 'tag': struct_dict[each][1], | |
| 'node': struct_dict[each][2], | |
| 'exclude': True if match else False}) | |
| # 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_type = cfg_yaml.yaml_type | |
| self._yaml_path = os.path.dirname(cfg_file) | |
| if self.yaml_type == 'vfr': | |
| self.build_formset_list() | |
| elif self.yaml_type == 'fsp': | |
| 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) | |
| if gen_cfg_data.yaml_type == 'fsp': | |
| 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()) |