# @ GenYamlCfg.py # # Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
# 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.
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' 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', '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._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) 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' % '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 \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 \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_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())