diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools')
16 files changed, 6277 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/FspDscBsf2Yaml.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/FspDscBsf2Yaml.py new file mode 100755 index 00000000..b3f2a3e5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/FspDscBsf2Yaml.py @@ -0,0 +1,884 @@ +#!/usr/bin/env python +## @ FspDscBsf2Yaml.py +# This script convert DSC or BSF format file into YAML format +# +# Copyright(c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +import os +import re +import sys +from datetime import date +from collections import OrderedDict +from functools import reduce + +from GenCfgOpt import CGenCfgOpt + +__copyright_tmp__ = """## @file +# +# YAML CFGDATA %s File. +# +# Copyright(c) %4d, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## +""" + +__copyright_dsc__ = """## @file +# +# Copyright (c) %04d, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[PcdsDynamicVpd.Upd] + # + # Global definitions in BSF + # !BSF BLOCK:{NAME:"FSP UPD Configuration", VER:"0.1"} + # + +""" + + +def Bytes2Val(Bytes): + return reduce(lambda x, y: (x << 8) | y, Bytes[::-1]) + + +def Str2Bytes(Value, Blen): + Result = bytearray(Value[1:-1], 'utf-8') # Excluding quotes + if len(Result) < Blen: + Result.extend(b'\x00' * (Blen - len(Result))) + return Result + + +class CFspBsf2Dsc: + + def __init__(self, bsf_file): + self.cfg_list = CFspBsf2Dsc.parse_bsf(bsf_file) + + def get_dsc_lines(self): + return CFspBsf2Dsc.generate_dsc(self.cfg_list) + + def save_dsc(self, dsc_file): + return CFspBsf2Dsc.generate_dsc(self.cfg_list, dsc_file) + + @staticmethod + def parse_bsf(bsf_file): + + fd = open(bsf_file, 'r') + bsf_txt = fd.read() + fd.close() + + find_list = [] + regex = re.compile(r'\s+Find\s+"(.*?)"(.*?)^\s+\$(.*?)\s+', re.S | re.MULTILINE) + for match in regex.finditer(bsf_txt): + find = match.group(1) + name = match.group(3) + if not name.endswith('_Revision'): + raise Exception("Unexpected CFG item following 'Find' !") + find_list.append((name, find)) + + idx = 0 + count = 0 + prefix = '' + chk_dict = {} + cfg_list = [] + cfg_temp = {'find': '', 'cname': '', 'length': 0, 'value': '0', 'type': 'Reserved', + 'embed': '', 'page': '', 'option': '', 'instance': 0} + regex = re.compile(r'^\s+(\$(.*?)|Skip)\s+(\d+)\s+bytes(\s+\$_DEFAULT_\s+=\s+(.+?))?$', + re.S | re.MULTILINE) + + for match in regex.finditer(bsf_txt): + dlen = int(match.group(3)) + if match.group(1) == 'Skip': + key = 'gPlatformFspPkgTokenSpaceGuid_BsfSkip%d' % idx + val = ', '.join(['%02X' % ord(i) for i in '\x00' * dlen]) + idx += 1 + option = '$SKIP' + else: + key = match.group(2) + val = match.group(5) + option = '' + + cfg_item = dict(cfg_temp) + finds = [i for i in find_list if i[0] == key] + if len(finds) > 0: + if count >= 1: + # Append a dummy one + cfg_item['cname'] = 'Dummy' + cfg_list.append(dict(cfg_item)) + cfg_list[-1]['embed'] = '%s:TAG_%03X:END' % (prefix, ord(prefix[-1])) + prefix = finds[0][1] + cfg_item['embed'] = '%s:TAG_%03X:START' % (prefix, ord(prefix[-1])) + cfg_item['find'] = prefix + cfg_item['cname'] = 'Signature' + cfg_item['length'] = len(finds[0][1]) + str2byte = Str2Bytes("'" + finds[0][1] + "'", len(finds[0][1])) + cfg_item['value'] = '0x%X' % Bytes2Val(str2byte) + cfg_list.append(dict(cfg_item)) + cfg_item = dict(cfg_temp) + find_list.pop(0) + count = 0 + + cfg_item['cname'] = key + cfg_item['length'] = dlen + cfg_item['value'] = val + cfg_item['option'] = option + + if key not in chk_dict.keys(): + chk_dict[key] = 0 + else: + chk_dict[key] += 1 + cfg_item['instance'] = chk_dict[key] + + cfg_list.append(cfg_item) + count += 1 + + if prefix: + cfg_item = dict(cfg_temp) + cfg_item['cname'] = 'Dummy' + cfg_item['embed'] = '%s:%03X:END' % (prefix, ord(prefix[-1])) + cfg_list.append(cfg_item) + + option_dict = {} + selreg = re.compile(r'\s+Selection\s*(.+?)\s*,\s*"(.*?)"$', re.S | re.MULTILINE) + regex = re.compile(r'^List\s&(.+?)$(.+?)^EndList$', re.S | re.MULTILINE) + for match in regex.finditer(bsf_txt): + key = match.group(1) + option_dict[key] = [] + for select in selreg.finditer(match.group(2)): + option_dict[key].append((int(select.group(1), 0), select.group(2))) + + chk_dict = {} + pagereg = re.compile(r'^Page\s"(.*?)"$(.+?)^EndPage$', re.S | re.MULTILINE) + for match in pagereg.finditer(bsf_txt): + page = match.group(1) + for line in match.group(2).splitlines(): + match = re.match(r'\s+(Combo|EditNum)\s\$(.+?),\s"(.*?)",\s(.+?),$', line) + if match: + cname = match.group(2) + if cname not in chk_dict.keys(): + chk_dict[cname] = 0 + else: + chk_dict[cname] += 1 + instance = chk_dict[cname] + cfg_idxs = [i for i, j in enumerate(cfg_list) if j['cname'] == cname and j['instance'] == instance] + if len(cfg_idxs) != 1: + raise Exception("Multiple CFG item '%s' found !" % cname) + cfg_item = cfg_list[cfg_idxs[0]] + cfg_item['page'] = page + cfg_item['type'] = match.group(1) + cfg_item['prompt'] = match.group(3) + cfg_item['range'] = None + if cfg_item['type'] == 'Combo': + cfg_item['option'] = option_dict[match.group(4)[1:]] + elif cfg_item['type'] == 'EditNum': + cfg_item['option'] = match.group(4) + match = re.match(r'\s+ Help\s"(.*?)"$', line) + if match: + cfg_item['help'] = match.group(1) + + match = re.match(r'\s+"Valid\srange:\s(.*)"$', line) + if match: + parts = match.group(1).split() + cfg_item['option'] = ( + (int(parts[0], 0), int(parts[2], 0), cfg_item['option'])) + + return cfg_list + + @staticmethod + def generate_dsc(option_list, dsc_file=None): + dsc_lines = [] + header = '%s' % (__copyright_dsc__ % date.today().year) + dsc_lines.extend(header.splitlines()) + + pages = [] + for cfg_item in option_list: + if cfg_item['page'] and (cfg_item['page'] not in pages): + pages.append(cfg_item['page']) + + page_id = 0 + for page in pages: + dsc_lines.append(' # !BSF PAGES:{PG%02X::"%s"}' % (page_id, page)) + page_id += 1 + dsc_lines.append('') + + last_page = '' + for option in option_list: + dsc_lines.append('') + default = option['value'] + pos = option['cname'].find('_') + name = option['cname'][pos + 1:] + + if option['find']: + dsc_lines.append(' # !BSF FIND:{%s}' % option['find']) + dsc_lines.append('') + + if option['instance'] > 0: + name = name + '_%s' % option['instance'] + + if option['embed']: + dsc_lines.append(' # !HDR EMBED:{%s}' % option['embed']) + + if option['type'] == 'Reserved': + dsc_lines.append(' # !BSF NAME:{Reserved} TYPE:{Reserved}') + if option['option'] == '$SKIP': + dsc_lines.append(' # !BSF OPTION:{$SKIP}') + else: + prompt = option['prompt'] + + if last_page != option['page']: + last_page = option['page'] + dsc_lines.append(' # !BSF PAGE:{PG%02X}' % (pages.index(option['page']))) + + if option['type'] == 'Combo': + dsc_lines.append(' # !BSF NAME:{%s} TYPE:{%s}' % + (prompt, option['type'])) + ops = [] + for val, text in option['option']: + ops.append('0x%x:%s' % (val, text)) + dsc_lines.append(' # !BSF OPTION:{%s}' % (', '.join(ops))) + elif option['type'] == 'EditNum': + cfg_len = option['length'] + if ',' in default and cfg_len > 8: + dsc_lines.append(' # !BSF NAME:{%s} TYPE:{Table}' % (prompt)) + if cfg_len > 16: + cfg_len = 16 + ops = [] + for i in range(cfg_len): + ops.append('%X:1:HEX' % i) + dsc_lines.append(' # !BSF OPTION:{%s}' % (', '.join(ops))) + else: + dsc_lines.append( + ' # !BSF NAME:{%s} TYPE:{%s, %s,(0x%X, 0x%X)}' % + (prompt, option['type'], option['option'][2], + option['option'][0], option['option'][1])) + dsc_lines.append(' # !BSF HELP:{%s}' % option['help']) + + if ',' in default: + default = '{%s}' % default + dsc_lines.append(' gCfgData.%-30s | * | 0x%04X | %s' % + (name, option['length'], default)) + + if dsc_file: + fd = open(dsc_file, 'w') + fd.write('\n'.join(dsc_lines)) + fd.close() + + return dsc_lines + + +class CFspDsc2Yaml(): + + def __init__(self): + self._Hdr_key_list = ['EMBED', 'STRUCT'] + self._Bsf_key_list = ['NAME', 'HELP', 'TYPE', 'PAGE', 'PAGES', 'OPTION', + 'CONDITION', 'ORDER', 'MARKER', 'SUBT', 'FIELD', 'FIND'] + self.gen_cfg_data = None + self.cfg_reg_exp = re.compile(r"^([_a-zA-Z0-9$\(\)]+)\s*\|\s*(0x[0-9A-F]+|\*)\s*\|" + + r"\s*(\d+|0x[0-9a-fA-F]+)\s*\|\s*(.+)") + self.bsf_reg_exp = re.compile(r"(%s):{(.+?)}(?:$|\s+)" % '|'.join(self._Bsf_key_list)) + self.hdr_reg_exp = re.compile(r"(%s):{(.+?)}" % '|'.join(self._Hdr_key_list)) + self.prefix = '' + self.unused_idx = 0 + self.offset = 0 + self.base_offset = 0 + + def load_config_data_from_dsc(self, file_name): + """ + Load and parse a DSC CFGDATA file. + """ + gen_cfg_data = CGenCfgOpt('FSP') + if file_name.endswith('.dsc'): + # if gen_cfg_data.ParseDscFileYaml(file_name, '') != 0: + if gen_cfg_data.ParseDscFile(file_name, '') != 0: + raise Exception('DSC file parsing error !') + if gen_cfg_data.CreateVarDict() != 0: + raise Exception('DSC variable creation error !') + else: + raise Exception('Unsupported file "%s" !' % file_name) + self.gen_cfg_data = gen_cfg_data + + def print_dsc_line(self): + """ + Debug function to print all DSC lines. + """ + for line in self.gen_cfg_data._DscLines: + print(line) + + def format_value(self, field, text, indent=''): + """ + Format a CFGDATA item into YAML format. + """ + if(not text.startswith('!expand')) and (': ' in text): + tgt = ':' if field == 'option' else '- ' + text = text.replace(': ', tgt) + lines = text.splitlines() + if len(lines) == 1 and field != 'help': + return text + else: + return '>\n ' + '\n '.join([indent + i.lstrip() for i in lines]) + + def reformat_pages(self, val): + # Convert XXX:YYY into XXX::YYY format for page definition + parts = val.split(',') + if len(parts) <= 1: + return val + + new_val = [] + for each in parts: + nodes = each.split(':') + if len(nodes) == 2: + each = '%s::%s' % (nodes[0], nodes[1]) + new_val.append(each) + ret = ','.join(new_val) + return ret + + def reformat_struct_value(self, utype, val): + # Convert DSC UINT16/32/64 array into new format by + # adding prefix 0:0[WDQ] to provide hint to the array format + if utype in ['UINT16', 'UINT32', 'UINT64']: + if val and val[0] == '{' and val[-1] == '}': + if utype == 'UINT16': + unit = 'W' + elif utype == 'UINT32': + unit = 'D' + else: + unit = 'Q' + val = '{ 0:0%s, %s }' % (unit, val[1:-1]) + return val + + def process_config(self, cfg): + if 'page' in cfg: + cfg['page'] = self.reformat_pages(cfg['page']) + + if 'struct' in cfg: + cfg['value'] = self.reformat_struct_value(cfg['struct'], cfg['value']) + + def parse_dsc_line(self, dsc_line, config_dict, init_dict, include): + """ + Parse a line in DSC and update the config dictionary accordingly. + """ + init_dict.clear() + match = re.match(r'g(CfgData|\w+FspPkgTokenSpaceGuid)\.(.+)', dsc_line) + if match: + match = self.cfg_reg_exp.match(match.group(2)) + if not match: + return False + config_dict['cname'] = self.prefix + match.group(1) + value = match.group(4).strip() + length = match.group(3).strip() + config_dict['length'] = length + config_dict['value'] = value + if match.group(2) == '*': + self.offset += int(length, 0) + else: + org_offset = int(match.group(2), 0) + if org_offset == 0: + self.base_offset = self.offset + offset = org_offset + self.base_offset + if self.offset != offset: + if offset > self.offset: + init_dict['padding'] = offset - self.offset + self.offset = offset + int(length, 0) + return True + + match = re.match(r"^\s*#\s+!([<>])\s+include\s+(.+)", dsc_line) + if match and len(config_dict) == 0: + # !include should not be inside a config field + # if so, do not convert include into YAML + init_dict = dict(config_dict) + config_dict.clear() + config_dict['cname'] = '$ACTION' + if match.group(1) == '<': + config_dict['include'] = match.group(2) + else: + config_dict['include'] = '' + return True + + match = re.match(r"^\s*#\s+(!BSF|!HDR)\s+(.+)", dsc_line) + if not match: + return False + + remaining = match.group(2) + if match.group(1) == '!BSF': + result = self.bsf_reg_exp.findall(remaining) + if not result: + return False + + for each in result: + key = each[0].lower() + val = each[1] + if key == 'field': + name = each[1] + if ':' not in name: + raise Exception('Incorrect bit field format !') + parts = name.split(':') + config_dict['length'] = parts[1] + config_dict['cname'] = '@' + parts[0] + return True + elif key in ['pages', 'page', 'find']: + init_dict = dict(config_dict) + config_dict.clear() + config_dict['cname'] = '$ACTION' + if key == 'find': + config_dict['find'] = val + else: + config_dict['page'] = val + return True + elif key == 'subt': + config_dict.clear() + parts = each[1].split(':') + tmp_name = parts[0][:-5] + if tmp_name == 'CFGHDR': + cfg_tag = '_$FFF_' + sval = '!expand { %s_TMPL : [ ' % tmp_name + '%s, %s, ' % (parts[1], cfg_tag) \ + + ', '.join(parts[2:]) + ' ] }' + else: + sval = '!expand { %s_TMPL : [ ' % tmp_name + ', '.join(parts[1:]) + ' ] }' + config_dict.clear() + config_dict['cname'] = tmp_name + config_dict['expand'] = sval + return True + else: + if key in ['name', 'help', 'option'] and val.startswith('+'): + val = config_dict[key] + '\n' + val[1:] + if val.strip() == '': + val = "''" + config_dict[key] = val + + else: + match = self.hdr_reg_exp.match(remaining) + if not match: + return False + key = match.group(1) + remaining = match.group(2) + if key == 'EMBED': + parts = remaining.split(':') + names = parts[0].split(',') + if parts[-1] == 'END': + prefix = '>' + else: + prefix = '<' + skip = False + if parts[1].startswith('TAG_'): + tag_txt = '%s:%s' % (names[0], parts[1]) + else: + tag_txt = names[0] + if parts[2] in ['START', 'END']: + if names[0] == 'PCIE_RP_PIN_CTRL[]': + skip = True + else: + tag_txt = '%s:%s' % (names[0], parts[1]) + if not skip: + config_dict.clear() + config_dict['cname'] = prefix + tag_txt + return True + + if key == 'STRUCT': + text = remaining.strip() + config_dict[key.lower()] = text + + return False + + def process_template_lines(self, lines): + """ + Process a line in DSC template section. + """ + template_name = '' + bsf_temp_dict = OrderedDict() + temp_file_dict = OrderedDict() + include_file = ['.'] + + for line in lines: + match = re.match(r"^\s*#\s+!([<>])\s+include\s+(.+)", line) + if match: + if match.group(1) == '<': + include_file.append(match.group(2)) + else: + include_file.pop() + + match = re.match(r"^\s*#\s+(!BSF)\s+DEFT:{(.+?):(START|END)}", line) + if match: + if match.group(3) == 'START' and not template_name: + template_name = match.group(2).strip() + temp_file_dict[template_name] = list(include_file) + bsf_temp_dict[template_name] = [] + if match.group(3) == 'END' and (template_name == match.group(2).strip()) \ + and template_name: + template_name = '' + else: + if template_name: + bsf_temp_dict[template_name].append(line) + return bsf_temp_dict, temp_file_dict + + def process_option_lines(self, lines): + """ + Process a line in DSC config section. + """ + cfgs = [] + struct_end = False + config_dict = dict() + init_dict = dict() + include = [''] + for line in lines: + ret = self.parse_dsc_line(line, config_dict, init_dict, include) + if ret: + if 'padding' in init_dict: + num = init_dict['padding'] + init_dict.clear() + padding_dict = {} + cfgs.append(padding_dict) + padding_dict['cname'] = 'UnusedUpdSpace%d' % self.unused_idx + padding_dict['length'] = '0x%x' % num + padding_dict['value'] = '{ 0 }' + self.unused_idx += 1 + + if cfgs and cfgs[-1]['cname'][0] != '@' and config_dict['cname'][0] == '@': + # it is a bit field, mark the previous one as virtual + cname = cfgs[-1]['cname'] + new_cfg = dict(cfgs[-1]) + new_cfg['cname'] = '@$STRUCT' + cfgs[-1].clear() + cfgs[-1]['cname'] = cname + cfgs.append(new_cfg) + + if cfgs and cfgs[-1]['cname'] == 'CFGHDR' and config_dict['cname'][0] == '<': + # swap CfgHeader and the CFG_DATA order + if ':' in config_dict['cname']: + # replace the real TAG for CFG_DATA + cfgs[-1]['expand'] = cfgs[-1]['expand'].replace( + '_$FFF_', '0x%s' % + config_dict['cname'].split(':')[1][4:]) + cfgs.insert(-1, config_dict) + else: + self.process_config(config_dict) + if struct_end: + struct_end = False + cfgs.insert(-1, config_dict) + else: + cfgs.append(config_dict) + if config_dict['cname'][0] == '>': + struct_end = True + + config_dict = dict(init_dict) + return cfgs + + def variable_fixup(self, each): + """ + Fix up some variable definitions for SBL. + """ + key = each + val = self.gen_cfg_data._MacroDict[each] + return key, val + + def template_fixup(self, tmp_name, tmp_list): + """ + Fix up some special config templates for SBL + """ + return + + def config_fixup(self, cfg_list): + """ + Fix up some special config items for SBL. + """ + + # Insert FSPT_UPD/FSPM_UPD/FSPS_UPD tag so as to create C strcture + idxs = [] + for idx, cfg in enumerate(cfg_list): + if cfg['cname'].startswith('<FSP_UPD_HEADER'): + idxs.append(idx) + + if len(idxs) != 3: + return + + # Handle insert backwards so that the index does not change in the loop + fsp_comp = 'SMT' + idx_comp = 0 + for idx in idxs[::-1]: + # Add current FSP?_UPD start tag + cfgfig_dict = {} + cfgfig_dict['cname'] = '<FSP%s_UPD' % fsp_comp[idx_comp] + cfg_list.insert(idx, cfgfig_dict) + if idx_comp < 2: + # Add previous FSP?_UPD end tag + cfgfig_dict = {} + cfgfig_dict['cname'] = '>FSP%s_UPD' % fsp_comp[idx_comp + 1] + cfg_list.insert(idx, cfgfig_dict) + idx_comp += 1 + + # Add final FSPS_UPD end tag + cfgfig_dict = {} + cfgfig_dict['cname'] = '>FSP%s_UPD' % fsp_comp[0] + cfg_list.append(cfgfig_dict) + + return + + def get_section_range(self, section_name): + """ + Extract line number range from config file for a given section name. + """ + start = -1 + end = -1 + for idx, line in enumerate(self.gen_cfg_data._DscLines): + if start < 0 and line.startswith('[%s]' % section_name): + start = idx + elif start >= 0 and line.startswith('['): + end = idx + break + if start == -1: + start = 0 + if end == -1: + end = len(self.gen_cfg_data._DscLines) + return start, end + + def normalize_file_name(self, file, is_temp=False): + """ + Normalize file name convention so that it is consistent. + """ + if file.endswith('.dsc'): + file = file[:-4] + '.yaml' + dir_name = os.path.dirname(file) + base_name = os.path.basename(file) + if is_temp: + if 'Template_' not in file: + base_name = base_name.replace('Template', 'Template_') + else: + if 'CfgData_' not in file: + base_name = base_name.replace('CfgData', 'CfgData_') + if dir_name: + path = dir_name + '/' + base_name + else: + path = base_name + return path + + def output_variable(self): + """ + Output variable block into a line list. + """ + lines = [] + for each in self.gen_cfg_data._MacroDict: + key, value = self.variable_fixup(each) + lines.append('%-30s : %s' % (key, value)) + return lines + + def output_template(self): + """ + Output template block into a line list. + """ + self.offset = 0 + self.base_offset = 0 + start, end = self.get_section_range('PcdsDynamicVpd.Tmp') + bsf_temp_dict, temp_file_dict = self.process_template_lines(self.gen_cfg_data._DscLines[start:end]) + template_dict = dict() + lines = [] + file_lines = {} + last_file = '.' + file_lines[last_file] = [] + + for tmp_name in temp_file_dict: + temp_file_dict[tmp_name][-1] = self.normalize_file_name(temp_file_dict[tmp_name][-1], True) + if len(temp_file_dict[tmp_name]) > 1: + temp_file_dict[tmp_name][-2] = self.normalize_file_name(temp_file_dict[tmp_name][-2], True) + + for tmp_name in bsf_temp_dict: + file = temp_file_dict[tmp_name][-1] + if last_file != file and len(temp_file_dict[tmp_name]) > 1: + inc_file = temp_file_dict[tmp_name][-2] + file_lines[inc_file].extend(['', '- !include %s' % temp_file_dict[tmp_name][-1], '']) + last_file = file + if file not in file_lines: + file_lines[file] = [] + lines = file_lines[file] + text = bsf_temp_dict[tmp_name] + tmp_list = self.process_option_lines(text) + self.template_fixup(tmp_name, tmp_list) + template_dict[tmp_name] = tmp_list + lines.append('%s: >' % tmp_name) + lines.extend(self.output_dict(tmp_list, False)['.']) + lines.append('\n') + return file_lines + + def output_config(self): + """ + Output config block into a line list. + """ + self.offset = 0 + self.base_offset = 0 + start, end = self.get_section_range('PcdsDynamicVpd.Upd') + cfgs = self.process_option_lines(self.gen_cfg_data._DscLines[start:end]) + self.config_fixup(cfgs) + file_lines = self.output_dict(cfgs, True) + return file_lines + + def output_dict(self, cfgs, is_configs): + """ + Output one config item into a line list. + """ + file_lines = {} + level = 0 + file = '.' + for each in cfgs: + if 'length' in each and int(each['length'], 0) == 0: + continue + + if 'include' in each: + if each['include']: + each['include'] = self.normalize_file_name(each['include']) + file_lines[file].extend(['', '- !include %s' % each['include'], '']) + file = each['include'] + else: + file = '.' + continue + + if file not in file_lines: + file_lines[file] = [] + + lines = file_lines[file] + name = each['cname'] + + prefix = name[0] + if prefix == '<': + level += 1 + + padding = ' ' * level + if prefix not in '<>@': + padding += ' ' + else: + name = name[1:] + if prefix == '@': + padding += ' ' + + if ':' in name: + parts = name.split(':') + name = parts[0] + + padding = padding[2:] if is_configs else padding + + if prefix != '>': + if 'expand' in each: + lines.append('%s- %s' % (padding, each['expand'])) + else: + lines.append('%s- %-12s :' % (padding, name)) + + for field in each: + if field in ['cname', 'expand', 'include']: + continue + value_str = self.format_value(field, each[field], padding + ' ' * 16) + full_line = ' %s %-12s : %s' % (padding, field, value_str) + lines.extend(full_line.splitlines()) + + if prefix == '>': + level -= 1 + if level == 0: + lines.append('') + + return file_lines + + +def bsf_to_dsc(bsf_file, dsc_file): + fsp_dsc = CFspBsf2Dsc(bsf_file) + dsc_lines = fsp_dsc.get_dsc_lines() + fd = open(dsc_file, 'w') + fd.write('\n'.join(dsc_lines)) + fd.close() + return + + +def dsc_to_yaml(dsc_file, yaml_file): + dsc2yaml = CFspDsc2Yaml() + dsc2yaml.load_config_data_from_dsc(dsc_file) + + cfgs = {} + for cfg in ['Template', 'Option']: + if cfg == 'Template': + file_lines = dsc2yaml.output_template() + else: + file_lines = dsc2yaml.output_config() + for file in file_lines: + lines = file_lines[file] + if file == '.': + cfgs[cfg] = lines + else: + if('/' in file or '\\' in file): + continue + file = os.path.basename(file) + fo = open(os.path.join(file), 'w') + fo.write(__copyright_tmp__ % (cfg, date.today().year) + '\n\n') + for line in lines: + fo.write(line + '\n') + fo.close() + + variables = dsc2yaml.output_variable() + fo = open(yaml_file, 'w') + fo.write(__copyright_tmp__ % ('Default', date.today().year)) + if len(variables) > 0: + fo.write('\n\nvariable:\n') + for line in variables: + fo.write(' ' + line + '\n') + + fo.write('\n\ntemplate:\n') + for line in cfgs['Template']: + if line != '': + fo.write(' ' + line + '\n') + + fo.write('\n\nconfigs:\n') + for line in cfgs['Option']: + if line != '': + fo.write(' ' + line + '\n') + + fo.close() + + +def get_fsp_name_from_path(bsf_file): + name = '' + parts = bsf_file.split(os.sep) + for part in parts: + if part.endswith('FspBinPkg'): + name = part[:-9] + break + if not name: + raise Exception('Could not get FSP name from file path!') + return name + + +def usage(): + print('\n'.join([ + "FspDscBsf2Yaml Version 0.10", + "Usage:", + " FspDscBsf2Yaml BsfFile|DscFile YamlFile" + ])) + + +def main(): + # + # Parse the options and args + # + argc = len(sys.argv) + if argc < 3: + usage() + return 1 + + bsf_file = sys.argv[1] + yaml_file = sys.argv[2] + if os.path.isdir(yaml_file): + yaml_file = os.path.join(yaml_file, get_fsp_name_from_path(bsf_file) + '.yaml') + + if bsf_file.endswith('.dsc'): + dsc_file = bsf_file + bsf_file = '' + else: + dsc_file = os.path.splitext(yaml_file)[0] + '.dsc' + bsf_to_dsc(bsf_file, dsc_file) + + dsc_to_yaml(dsc_file, yaml_file) + + print("'%s' was created successfully!" % yaml_file) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py new file mode 100755 index 00000000..34484c16 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/GenCfgOpt.py @@ -0,0 +1,1791 @@ +## @ GenCfgOpt.py +# +# Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +import os +import re +import sys +import struct +from datetime import date +from functools import reduce + +# Generated file copyright header + +__copyright_txt__ = """## @file +# +# THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION. +# +# This file lists all VPD informations for a platform collected by build.exe. +# +# Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +""" + +__copyright_bsf__ = """/** @file + + Boot Setting File for Platform Configuration. + + Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + This file is automatically generated. Please do NOT modify !!! + +**/ + +""" + +__copyright_h__ = """/** @file + +Copyright (c) %4d, Intel Corporation. All rights reserved.<BR> + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of Intel Corporation nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + This file is automatically generated. Please do NOT modify !!! + +**/ +""" + +BuildOptionPcd = [] + +class CLogicalExpression: + def __init__(self): + self.index = 0 + self.string = '' + + def errExit(self, err = ''): + print ("ERROR: Express parsing for:") + print (" %s" % self.string) + print (" %s^" % (' ' * self.index)) + if err: + print ("INFO : %s" % err) + raise SystemExit + + def getNonNumber (self, n1, n2): + if not n1.isdigit(): + return n1 + if not n2.isdigit(): + return n2 + return None + + def getCurr(self, lens = 1): + try: + if lens == -1: + return self.string[self.index :] + else: + if self.index + lens > len(self.string): + lens = len(self.string) - self.index + return self.string[self.index : self.index + lens] + except Exception: + return '' + + def isLast(self): + return self.index == len(self.string) + + def moveNext(self, len = 1): + self.index += len + + def skipSpace(self): + while not self.isLast(): + if self.getCurr() in ' \t': + self.moveNext() + else: + return + + def normNumber (self, val): + return True if val else False + + def getNumber(self, var): + var = var.strip() + if re.match('^0x[a-fA-F0-9]+$', var): + value = int(var, 16) + elif re.match('^[+-]?\d+$', var): + value = int(var, 10) + else: + value = None + return value + + def parseValue(self): + self.skipSpace() + var = '' + while not self.isLast(): + char = self.getCurr() + if re.match('^[\w.]', char): + var += char + self.moveNext() + else: + break + val = self.getNumber(var) + if val is None: + value = var + else: + value = "%d" % val + return value + + def parseSingleOp(self): + self.skipSpace() + if re.match('^NOT\W', self.getCurr(-1)): + self.moveNext(3) + op = self.parseBrace() + val = self.getNumber (op) + if val is None: + self.errExit ("'%s' is not a number" % op) + return "%d" % (not self.normNumber(int(op))) + else: + return self.parseValue() + + def parseBrace(self): + self.skipSpace() + char = self.getCurr() + if char == '(': + self.moveNext() + value = self.parseExpr() + self.skipSpace() + if self.getCurr() != ')': + self.errExit ("Expecting closing brace or operator") + self.moveNext() + return value + else: + value = self.parseSingleOp() + return value + + def parseCompare(self): + value = self.parseBrace() + while True: + self.skipSpace() + char = self.getCurr() + if char in ['<', '>']: + self.moveNext() + next = self.getCurr() + if next == '=': + op = char + next + self.moveNext() + else: + op = char + result = self.parseBrace() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(eval (value + op + result)) + else: + self.errExit ("'%s' is not a valid number for comparision" % test) + elif char in ['=', '!']: + op = self.getCurr(2) + if op in ['==', '!=']: + self.moveNext(2) + result = self.parseBrace() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber((eval (value + op + result))) + else: + value = "%d" % self.normNumber(eval ("'" + value + "'" + op + "'" + result + "'")) + else: + break + else: + break + return value + + def parseAnd(self): + value = self.parseCompare() + while True: + self.skipSpace() + if re.match('^AND\W', self.getCurr(-1)): + self.moveNext(3) + result = self.parseCompare() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(int(value) & int(result)) + else: + self.errExit ("'%s' is not a valid op number for AND" % test) + else: + break + return value + + def parseOrXor(self): + value = self.parseAnd() + op = None + while True: + self.skipSpace() + op = None + if re.match('^XOR\W', self.getCurr(-1)): + self.moveNext(3) + op = '^' + elif re.match('^OR\W', self.getCurr(-1)): + self.moveNext(2) + op = '|' + else: + break + if op: + result = self.parseAnd() + test = self.getNonNumber(result, value) + if test is None: + value = "%d" % self.normNumber(eval (value + op + result)) + else: + self.errExit ("'%s' is not a valid op number for XOR/OR" % test) + return value + + def parseExpr(self): + return self.parseOrXor() + + def getResult(self): + value = self.parseExpr() + self.skipSpace() + if not self.isLast(): + self.errExit ("Unexpected character found '%s'" % self.getCurr()) + test = self.getNumber(value) + if test is None: + self.errExit ("Result '%s' is not a number" % value) + return int(value) + + def evaluateExpress (self, Expr): + self.index = 0 + self.string = Expr + if self.getResult(): + Result = True + else: + Result = False + return Result + +class CGenCfgOpt: + def __init__(self, Mode = ''): + self.Debug = False + self.Error = '' + self.Mode = Mode + self._GlobalDataDef = """ +GlobalDataDef + SKUID = 0, "DEFAULT" +EndGlobalData + +""" + self._BuidinOptionTxt = """ +List &EN_DIS + Selection 0x1 , "Enabled" + Selection 0x0 , "Disabled" +EndList + +""" + self._BsfKeyList = ['FIND','NAME','HELP','TYPE','PAGE', 'PAGES', 'BLOCK', 'OPTION','CONDITION','ORDER', 'MARKER', 'SUBT'] + self._HdrKeyList = ['HEADER','STRUCT', 'EMBED', 'COMMENT'] + self._BuidinOption = {'$EN_DIS' : 'EN_DIS'} + + self._MacroDict = {} + self._VarDict = {} + self._PcdsDict = {} + self._CfgBlkDict = {} + self._CfgPageDict = {} + self._BsfTempDict = {} + self._CfgItemList = [] + self._DscLines = [] + self._DscFile = '' + + self._MapVer = 0 + self._DscTime = 0 + + def ParseMacros (self, MacroDefStr): + # ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build'] + self._MacroDict = {} + IsExpression = False + for Macro in MacroDefStr: + if Macro.startswith('-D'): + IsExpression = True + if len(Macro) > 2: + Macro = Macro[2:] + else : + continue + if IsExpression: + IsExpression = False + Match = re.match("(\w+)=(.+)", Macro) + if Match: + self._MacroDict[Match.group(1)] = Match.group(2) + else: + Match = re.match("(\w+)", Macro) + if Match: + self._MacroDict[Match.group(1)] = '' + if len(self._MacroDict) == 0: + Error = 1 + else: + Error = 0 + if self.Debug: + print ("INFO : Macro dictionary:") + for Each in self._MacroDict: + print (" $(%s) = [ %s ]" % (Each , self._MacroDict[Each])) + return Error + + def EvaulateIfdef (self, Macro): + Result = Macro in self._MacroDict + if self.Debug: + print ("INFO : Eval Ifdef [%s] : %s" % (Macro, Result)) + return Result + + def ExpandMacros (self, Input, Preserve = False): + Line = Input + Match = re.findall("\$\(\w+\)", Input) + if Match: + for Each in Match: + Variable = Each[2:-1] + if Variable in self._MacroDict: + Line = Line.replace(Each, self._MacroDict[Variable]) + else: + if self.Debug: + print ("WARN : %s is not defined" % Each) + if not Preserve: + Line = Line.replace(Each, Each[2:-1]) + return Line + + def ExpandPcds (self, Input): + Line = Input + Match = re.findall("(\w+\.\w+)", Input) + if Match: + for PcdName in Match: + if PcdName in self._PcdsDict: + Line = Line.replace(PcdName, self._PcdsDict[PcdName]) + else: + if self.Debug: + print ("WARN : %s is not defined" % PcdName) + return Line + + def EvaluateExpress (self, Expr): + ExpExpr = self.ExpandPcds(Expr) + ExpExpr = self.ExpandMacros(ExpExpr) + LogExpr = CLogicalExpression() + Result = LogExpr.evaluateExpress (ExpExpr) + if self.Debug: + print ("INFO : Eval Express [%s] : %s" % (Expr, Result)) + return Result + + def ValueToByteArray (self, ValueStr, Length): + Match = re.match("\{\s*FILE:(.+)\}", ValueStr) + if Match: + FileList = Match.group(1).split(',') + Result = bytearray() + for File in FileList: + File = File.strip() + BinPath = os.path.join(os.path.dirname(self._DscFile), File) + Result.extend(bytearray(open(BinPath, 'rb').read())) + else: + try: + Result = bytearray(self.ValueToList(ValueStr, Length)) + except ValueError as e: + raise Exception ("Bytes in '%s' must be in range 0~255 !" % ValueStr) + if len(Result) < Length: + Result.extend(b'\x00' * (Length - len(Result))) + elif len(Result) > Length: + raise Exception ("Value '%s' is too big to fit into %d bytes !" % (ValueStr, Length)) + + return Result[:Length] + + def ValueToList (self, ValueStr, Length): + if ValueStr[0] == '{': + Result = [] + BinList = ValueStr[1:-1].split(',') + InBitField = False + LastInBitField = False + Value = 0 + BitLen = 0 + for Element in BinList: + InBitField = False + Each = Element.strip() + if len(Each) == 0: + pass + else: + if Each[0] in ['"', "'"]: + Result.extend(list(bytearray(Each[1:-1], 'utf-8'))) + elif ':' in Each: + Match = re.match("(.+):(\d+)b", Each) + if Match is None: + raise Exception("Invald value list format '%s' !" % Each) + InBitField = True + CurrentBitLen = int(Match.group(2)) + CurrentValue = ((self.EvaluateExpress(Match.group(1)) & (1<<CurrentBitLen) - 1)) << BitLen + else: + Result.append(self.EvaluateExpress(Each.strip())) + if InBitField: + Value += CurrentValue + BitLen += CurrentBitLen + if LastInBitField and ((not InBitField) or (Element == BinList[-1])): + if BitLen % 8 != 0: + raise Exception("Invald bit field length!") + Result.extend(Val2Bytes(Value, BitLen // 8)) + Value = 0 + BitLen = 0 + LastInBitField = InBitField + elif ValueStr.startswith("'") and ValueStr.endswith("'"): + Result = Str2Bytes (ValueStr, Length) + elif ValueStr.startswith('"') and ValueStr.endswith('"'): + Result = Str2Bytes (ValueStr, Length) + else: + Result = Val2Bytes (self.EvaluateExpress(ValueStr), Length) + return Result + + def FormatListValue(self, ConfigDict): + Struct = ConfigDict['struct'] + if Struct not in ['UINT8','UINT16','UINT32','UINT64']: + return + + dataarray = [] + binlist = ConfigDict['value'][1:-1].split(',') + for each in binlist: + each = each.strip() + if each.startswith('0x'): + value = int(each, 16) + else: + value = int(each) + dataarray.append(value) + + unit = int(Struct[4:]) / 8 + if int(ConfigDict['length']) != unit * len(dataarray): + raise Exception("Array size is not proper for '%s' !" % ConfigDict['cname']) + + bytearray = [] + for each in dataarray: + value = each + for loop in range(int(unit)): + bytearray.append("0x%02X" % (value & 0xFF)) + value = value >> 8 + newvalue = '{' + ','.join(bytearray) + '}' + ConfigDict['value'] = newvalue + return "" + + def ParseDscFile (self, DscFile, FvDir): + Hardcode = False + AutoAlign = False + self._CfgItemList = [] + self._CfgPageDict = {} + self._CfgBlkDict = {} + self._DscFile = DscFile + self._FvDir = FvDir + + self._DscLines = [] + self._BsfTempDict = {} + + # Initial DSC time is parent DSC time. + self._DscTime = os.path.getmtime(DscFile) + + CfgDict = {} + + IsDefSect = False + IsPcdSect = False + IsUpdSect = False + IsVpdSect = False + IsTmpSect = False + + TemplateName = '' + + IfStack = [] + ElifStack = [] + Error = 0 + ConfigDict = {} + + + if type(DscFile) is list: + # it is DSC lines already + DscLines = DscFile + self._DscFile = '.' + else: + DscFd = open(DscFile, "r") + DscLines = DscFd.readlines() + DscFd.close() + self._DscFile = DscFile + + SkipLines = 0 + + MaxAlign = 32 #Default align to 32, but if there are 64 bit unit, align to 64 + SizeAlign = 0 #record the struct max align + Base = 0 #Starting offset of sub-structure. + + while len(DscLines): + DscLine = DscLines.pop(0).strip() + if SkipLines == 0: + self._DscLines.append (DscLine) + else: + SkipLines = SkipLines - 1 + if len(DscLine) == 0: + continue + + Handle = False + Match = re.match("^\[(.+)\]", DscLine) + if Match is not None: + IsDefSect = False + IsPcdSect = False + IsVpdSect = False + IsUpdSect = False + IsTmpSect = False + SectionName = Match.group(1).lower() + if SectionName == "Defines".lower(): + IsDefSect = True + if (SectionName == "PcdsFeatureFlag".lower() or SectionName == "PcdsFixedAtBuild".lower()): + IsPcdSect = True + elif SectionName == "PcdsDynamicVpd.Tmp".lower(): + IsTmpSect = True + elif SectionName == "PcdsDynamicVpd.Upd".lower(): + ConfigDict = {} + ConfigDict['header'] = 'ON' + ConfigDict['region'] = 'UPD' + ConfigDict['order'] = -1 + ConfigDict['page'] = '' + ConfigDict['name'] = '' + ConfigDict['find'] = '' + ConfigDict['marker'] = '' + ConfigDict['struct'] = '' + ConfigDict['embed'] = '' + ConfigDict['comment'] = '' + ConfigDict['subreg'] = [] + ConfigDict['condition'] = '' + ConfigDict['option'] = '' + IsUpdSect = True + Offset = 0 + else: + if IsDefSect or IsPcdSect or IsUpdSect or IsVpdSect or IsTmpSect: + + Match = False if DscLine[0] != '!' else True + if Match: + Match = re.match("^!(else|endif|ifdef|ifndef|if|elseif|include)\s*(.+)?$", DscLine.split("#")[0]) + Keyword = Match.group(1) if Match else '' + Remaining = Match.group(2) if Match else '' + Remaining = '' if Remaining is None else Remaining.strip() + + if Keyword in ['if', 'elseif', 'ifdef', 'ifndef', 'include'] and not Remaining: + raise Exception ("ERROR: Expression is expected after '!if' or !elseif' for line '%s'" % DscLine) + + if Keyword == 'else': + if IfStack: + IfStack[-1] = not IfStack[-1] + else: + raise Exception ("ERROR: No paired '!if' found for '!else' for line '%s'" % DscLine) + elif Keyword == 'endif': + if IfStack: + IfStack.pop() + Level = ElifStack.pop() + if Level > 0: + del IfStack[-Level:] + else: + raise Exception ("ERROR: No paired '!if' found for '!endif' for line '%s'" % DscLine) + elif Keyword == 'ifdef' or Keyword == 'ifndef': + Result = self.EvaulateIfdef (Remaining) + if Keyword == 'ifndef': + Result = not Result + IfStack.append(Result) + ElifStack.append(0) + elif Keyword == 'if' or Keyword == 'elseif': + Result = self.EvaluateExpress(Remaining) + if Keyword == "if": + ElifStack.append(0) + IfStack.append(Result) + else: #elseif + if IfStack: + IfStack[-1] = not IfStack[-1] + IfStack.append(Result) + ElifStack[-1] = ElifStack[-1] + 1 + else: + raise Exception ("ERROR: No paired '!if' found for '!elif' for line '%s'" % DscLine) + else: + if IfStack: + Handle = reduce(lambda x,y: x and y, IfStack) + else: + Handle = True + if Handle: + Match = re.match("!include\s+(.+)", DscLine) + if Match: + IncludeFilePath = Match.group(1) + IncludeFilePath = self.ExpandMacros(IncludeFilePath) + PackagesPath = os.getenv("PACKAGES_PATH") + if PackagesPath: + for PackagePath in PackagesPath.split(os.pathsep): + IncludeFilePathAbs = os.path.join(os.path.normpath(PackagePath), os.path.normpath(IncludeFilePath)) + if os.path.exists(IncludeFilePathAbs): + IncludeDsc = open(IncludeFilePathAbs, "r") + break + else: + IncludeDsc = open(IncludeFilePath, "r") + if IncludeDsc == None: + print("ERROR: Cannot open file '%s'" % IncludeFilePath) + raise SystemExit + + # Update DscTime when newer DSC time found. + CurrentDscTime = os.path.getmtime(os.path.realpath(IncludeDsc.name)) + if CurrentDscTime > self._DscTime: + self._DscTime = CurrentDscTime + + NewDscLines = IncludeDsc.readlines() + IncludeDsc.close() + DscLines = NewDscLines + DscLines + del self._DscLines[-1] + Offset = 0 + else: + if DscLine.startswith('!'): + print("ERROR: Unrecognized directive for line '%s'" % DscLine) + raise SystemExit + if not Handle: + del self._DscLines[-1] + continue + + if IsDefSect: + #DEFINE UPD_TOOL_GUID = 8C3D856A-9BE6-468E-850A-24F7A8D38E09 + #DEFINE FSP_T_UPD_TOOL_GUID = 34686CA3-34F9-4901-B82A-BA630F0714C6 + #DEFINE FSP_M_UPD_TOOL_GUID = 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385 + #DEFINE FSP_S_UPD_TOOL_GUID = CAE3605B-5B34-4C85-B3D7-27D54273C40F + Match = re.match("^\s*(?:DEFINE\s+)*(\w+)\s*=\s*(.+)", DscLine) + if Match: + self._MacroDict[Match.group(1)] = self.ExpandMacros(Match.group(2)) + if self.Debug: + print ("INFO : DEFINE %s = [ %s ]" % (Match.group(1), self.ExpandMacros(Match.group(2)))) + elif IsPcdSect: + #gSiPkgTokenSpaceGuid.PcdTxtEnable|FALSE + #gSiPkgTokenSpaceGuid.PcdOverclockEnable|TRUE + Match = re.match("^\s*([\w\.]+)\s*\|\s*(\w+)", DscLine) + if Match: + self._PcdsDict[Match.group(1)] = Match.group(2) + if self.Debug: + print ("INFO : PCD %s = [ %s ]" % (Match.group(1), Match.group(2))) + i = 0 + while i < len(BuildOptionPcd): + Match = re.match("\s*([\w\.]+)\s*\=\s*(\w+)", BuildOptionPcd[i]) + if Match: + self._PcdsDict[Match.group(1)] = Match.group(2) + i += 1 + + elif IsTmpSect: + # !BSF DEFT:{GPIO_TMPL:START} + Match = re.match("^\s*#\s+(!BSF)\s+DEFT:{(.+?):(START|END)}", DscLine) + if Match: + if Match.group(3) == 'START' and not TemplateName: + TemplateName = Match.group(2).strip() + self._BsfTempDict[TemplateName] = [] + if Match.group(3) == 'END' and (TemplateName == Match.group(2).strip()) and TemplateName: + TemplateName = '' + else: + if TemplateName: + Match = re.match("^!include\s*(.+)?$", DscLine) + if Match: + continue + self._BsfTempDict[TemplateName].append(DscLine) + + else: + Match = re.match("^\s*#\s+(!BSF|@Bsf|!HDR)\s+(.+)", DscLine) + if Match: + Remaining = Match.group(2) + if Match.group(1) == '!BSF' or Match.group(1) == '@Bsf': + Match = re.match("(?:^|.+\s+)PAGES:{(.+?)}", Remaining) + if Match: + # !BSF PAGES:{HSW:"Haswell System Agent", LPT:"Lynx Point PCH"} + PageList = Match.group(1).split(',') + for Page in PageList: + Page = Page.strip() + Match = re.match("(\w+):\"(.+)\"", Page) + if Match != None: + self._CfgPageDict[Match.group(1)] = Match.group(2) + + Match = re.match("(?:^|.+\s+)BLOCK:{NAME:\"(.+)\"\s*,\s*VER:\"(.+)\"\s*}", Remaining) + if Match: + self._CfgBlkDict['name'] = Match.group(1) + self._CfgBlkDict['ver'] = Match.group(2) + + for Key in self._BsfKeyList: + Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining) + if Match: + if Key in ['NAME', 'HELP', 'OPTION'] and Match.group(1).startswith('+'): + ConfigDict[Key.lower()] += Match.group(1)[1:] + else: + ConfigDict[Key.lower()] = Match.group(1) + else: + for Key in self._HdrKeyList: + Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining) + if Match: + ConfigDict[Key.lower()] = Match.group(1) + + Match = re.match("^\s*#\s+@Prompt\s+(.+)", DscLine) + if Match: + ConfigDict['name'] = Match.group(1) + + Match = re.match("^\s*#\s*@ValidList\s*(.+)\s*\|\s*(.+)\s*\|\s*(.+)\s*", DscLine) + if Match: + if Match.group(2).strip() in self._BuidinOption: + ConfigDict['option'] = Match.group(2).strip() + else: + OptionValueList = Match.group(2).split(',') + OptionStringList = Match.group(3).split(',') + Index = 0 + for Option in OptionValueList: + Option = Option.strip() + ConfigDict['option'] = ConfigDict['option'] + str(Option) + ':' + OptionStringList[Index].strip() + Index += 1 + if Index in range(len(OptionValueList)): + ConfigDict['option'] += ', ' + ConfigDict['type'] = "Combo" + + Match = re.match("^\s*#\s*@ValidRange\s*(.+)\s*\|\s*(.+)\s*-\s*(.+)\s*", DscLine) + if Match: + if "0x" in Match.group(2) or "0x" in Match.group(3): + ConfigDict['type'] = "EditNum, HEX, (%s,%s)" % (Match.group(2), Match.group(3)) + else: + ConfigDict['type'] = "EditNum, DEC, (%s,%s)" % (Match.group(2), Match.group(3)) + + Match = re.match("^\s*##\s+(.+)", DscLine) + if Match: + ConfigDict['help'] = Match.group(1) + + # Check VPD/UPD + if IsUpdSect: + Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+|\*)\s*\|\s*(\d+|0x[0-9a-fA-F]+)\s*\|\s*(.+)",DscLine) + else: + Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+)(?:\s*\|\s*(.+))?", DscLine) + if Match: + ConfigDict['space'] = Match.group(1) + ConfigDict['cname'] = Match.group(2) + if Match.group(3) != '*': + Hardcode = True + Offset = int (Match.group(3), 16) + else: + AutoAlign = True + + if Hardcode and AutoAlign: + print("Hardcode and auto-align mixed mode is not supported by GenCfgOpt") + raise SystemExit + ConfigDict['offset'] = Offset + if ConfigDict['order'] == -1: + ConfigDict['order'] = ConfigDict['offset'] << 8 + else: + (Major, Minor) = ConfigDict['order'].split('.') + ConfigDict['order'] = (int (Major, 16) << 8 ) + int (Minor, 16) + if IsUpdSect: + Value = Match.group(5).strip() + if Match.group(4).startswith("0x"): + Length = int (Match.group(4), 16) + else : + Length = int (Match.group(4)) + Offset += Length + else: + Value = Match.group(4) + if Value is None: + Value = '' + Value = Value.strip() + if '|' in Value: + Match = re.match("^.+\s*\|\s*(.+)", Value) + if Match: + Value = Match.group(1) + Length = -1 + + ConfigDict['length'] = Length + Match = re.match("\$\((\w+)\)", Value) + if Match: + if Match.group(1) in self._MacroDict: + Value = self._MacroDict[Match.group(1)] + + ConfigDict['value'] = Value + if (len(Value) > 0) and (Value[0] == '{'): + Value = self.FormatListValue(ConfigDict) + + if ConfigDict['name'] == '': + # Clear BSF specific items + ConfigDict['bsfname'] = '' + ConfigDict['help'] = '' + ConfigDict['type'] = '' + ConfigDict['option'] = '' + if IsUpdSect and AutoAlign: + ItemLength = int(ConfigDict['length']) + ItemOffset = int(ConfigDict['offset']) + ItemStruct = ConfigDict['struct'] + Unit = 1 + if ItemLength in [1, 2, 4, 8] and not ConfigDict['value'].startswith('{'): + Unit = ItemLength + # If there are 64 bit unit, align to 64 + if Unit == 8: + MaxAlign = 64 + SizeAlign = 8 + if ItemStruct != '': + UnitDict = {'UINT8':1, 'UINT16':2, 'UINT32':4, 'UINT64':8} + if ItemStruct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']: + Unit = UnitDict[ItemStruct] + # If there are 64 bit unit, align to 64 + if Unit == 8: + MaxAlign = 64 + SizeAlign = max(SizeAlign, Unit) + if (ConfigDict['embed'].find(':START') != -1): + Base = ItemOffset + SubOffset = ItemOffset - Base + SubRemainder = SubOffset % Unit + if SubRemainder: + Diff = Unit - SubRemainder + Offset = Offset + Diff + ItemOffset = ItemOffset + Diff + + if (ConfigDict['embed'].find(':END') != -1): + Remainder = Offset % (MaxAlign/8) # MaxAlign is either 32 or 64 + if Remainder: + Diff = int((MaxAlign/8) - Remainder) + Offset = Offset + Diff + ItemOffset = ItemOffset + Diff + MaxAlign = 32 # Reset to default 32 align when struct end + if (ConfigDict['cname'] == 'UpdTerminator'): + # ItemLength is the size of UpdTerminator + # Itemlength might be 16, 32, or 64 + # Struct align to 64 if UpdTerminator + # or struct size is 64 bit, else align to 32 + Remainder = Offset % max(ItemLength/8, 4, SizeAlign) + Offset = Offset + ItemLength + if Remainder: + Diff = int(max(ItemLength/8, 4, SizeAlign) - Remainder) + ItemOffset = ItemOffset + Diff + ConfigDict['offset'] = ItemOffset + + self._CfgItemList.append(ConfigDict.copy()) + ConfigDict['name'] = '' + ConfigDict['find'] = '' + ConfigDict['struct'] = '' + ConfigDict['embed'] = '' + ConfigDict['comment'] = '' + ConfigDict['marker'] = '' + ConfigDict['order'] = -1 + ConfigDict['subreg'] = [] + ConfigDict['option'] = '' + else: + # It could be a virtual item as below + # !BSF FIELD:{SerialDebugPortAddress0:1} + # or + # @Bsf FIELD:{SerialDebugPortAddress0:1b} + Match = re.match("^\s*#\s+(!BSF|@Bsf)\s+FIELD:{(.+):(\d+)([Bb])?}", DscLine) + if Match: + SubCfgDict = ConfigDict.copy() + if (Match.group(4) == None) or (Match.group(4) == 'B'): + UnitBitLen = 8 + elif Match.group(4) == 'b': + UnitBitLen = 1 + else: + print("ERROR: Invalide BSF FIELD length for line '%s'" % DscLine) + raise SystemExit + SubCfgDict['cname'] = Match.group(2) + SubCfgDict['bitlength'] = int (Match.group(3)) * UnitBitLen + if SubCfgDict['bitlength'] > 0: + LastItem = self._CfgItemList[-1] + if len(LastItem['subreg']) == 0: + SubOffset = 0 + else: + SubOffset = LastItem['subreg'][-1]['bitoffset'] + LastItem['subreg'][-1]['bitlength'] + SubCfgDict['bitoffset'] = SubOffset + LastItem['subreg'].append (SubCfgDict.copy()) + ConfigDict['name'] = '' + return Error + + def GetBsfBitFields (self, subitem, bytes): + start = subitem['bitoffset'] + end = start + subitem['bitlength'] + bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1]) + bitsvalue = bitsvalue[::-1] + bitslen = len(bitsvalue) + if start > bitslen or end > bitslen: + raise Exception ("Invalid bits offset [%d,%d] %d for %s" % (start, end, bitslen, subitem['name'])) + return '0x%X' % (int(bitsvalue[start:end][::-1], 2)) + + def UpdateSubRegionDefaultValue (self): + Error = 0 + for Item in self._CfgItemList: + if len(Item['subreg']) == 0: + continue + bytearray = [] + if Item['value'][0] == '{': + binlist = Item['value'][1:-1].split(',') + for each in binlist: + each = each.strip() + if each.startswith('0x'): + value = int(each, 16) + else: + value = int(each) + bytearray.append(value) + else: + if Item['value'].startswith('0x'): + value = int(Item['value'], 16) + else: + value = int(Item['value']) + idx = 0 + while idx < Item['length']: + bytearray.append(value & 0xFF) + value = value >> 8 + idx = idx + 1 + for SubItem in Item['subreg']: + valuestr = self.GetBsfBitFields(SubItem, bytearray) + SubItem['value'] = valuestr + return Error + + def NoDscFileChange (self, OutPutFile): + NoFileChange = True + if not os.path.exists(OutPutFile): + NoFileChange = False + else: + OutputTime = os.path.getmtime(OutPutFile) + if self._DscTime > OutputTime: + NoFileChange = False + return NoFileChange + + def CreateSplitUpdTxt (self, UpdTxtFile): + GuidList = ['FSP_T_UPD_TOOL_GUID','FSP_M_UPD_TOOL_GUID','FSP_S_UPD_TOOL_GUID'] + SignatureList = ['0x545F', '0x4D5F','0x535F'] # _T, _M, and _S signature for FSPT, FSPM, FSPS + for Index in range(len(GuidList)): + UpdTxtFile = '' + FvDir = self._FvDir + if GuidList[Index] not in self._MacroDict: + self.Error = "%s definition is missing in DSC file" % (GuidList[Index]) + return 1 + + if UpdTxtFile == '': + UpdTxtFile = os.path.join(FvDir, self._MacroDict[GuidList[Index]] + '.txt') + + if (self.NoDscFileChange (UpdTxtFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD TXT file' + return 256 + + TxtFd = open(UpdTxtFile, "w") + TxtFd.write("%s\n" % (__copyright_txt__ % date.today().year)) + + NextOffset = 0 + SpaceIdx = 0 + StartAddr = 0 + EndAddr = 0 + Default = 'DEFAULT|' + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]: + StartAddr = Item['offset'] + NextOffset = StartAddr + InRange = True + if Item['cname'] == 'UpdTerminator' and InRange == True: + EndAddr = Item['offset'] + InRange = False + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]: + InRange = True + if InRange != True: + continue + if Item['cname'] == 'UpdTerminator': + InRange = False + if Item['region'] != 'UPD': + continue + Offset = Item['offset'] + if StartAddr > Offset or EndAddr < Offset: + continue + if NextOffset < Offset: + # insert one line + TxtFd.write("%s.UnusedUpdSpace%d|%s0x%04X|0x%04X|{0}\n" % (Item['space'], SpaceIdx, Default, NextOffset - StartAddr, Offset - NextOffset)) + SpaceIdx = SpaceIdx + 1 + NextOffset = Offset + Item['length'] + TxtFd.write("%s.%s|%s0x%04X|%s|%s\n" % (Item['space'],Item['cname'],Default,Item['offset'] - StartAddr,Item['length'],Item['value'])) + TxtFd.close() + return 0 + + def CreateVarDict (self): + Error = 0 + self._VarDict = {} + if len(self._CfgItemList) > 0: + Item = self._CfgItemList[-1] + self._VarDict['_LENGTH_'] = '%d' % (Item['offset'] + Item['length']) + for Item in self._CfgItemList: + Embed = Item['embed'] + Match = re.match("^(\w+):(\w+):(START|END)", Embed) + if Match: + StructName = Match.group(1) + VarName = '_%s_%s_' % (Match.group(3), StructName) + if Match.group(3) == 'END': + self._VarDict[VarName] = Item['offset'] + Item['length'] + self._VarDict['_LENGTH_%s_' % StructName] = \ + self._VarDict['_END_%s_' % StructName] - self._VarDict['_START_%s_' % StructName] + if Match.group(2).startswith('TAG_'): + if (self.Mode != 'FSP') and (self._VarDict['_LENGTH_%s_' % StructName] % 4): + raise Exception("Size of structure '%s' is %d, not DWORD aligned !" % (StructName, self._VarDict['_LENGTH_%s_' % StructName])) + self._VarDict['_TAG_%s_' % StructName] = int (Match.group(2)[4:], 16) & 0xFFF + else: + self._VarDict[VarName] = Item['offset'] + if Item['marker']: + self._VarDict['_OFFSET_%s_' % Item['marker'].strip()] = Item['offset'] + return Error + + def UpdateBsfBitUnit (self, Item): + BitTotal = 0 + BitOffset = 0 + StartIdx = 0 + Unit = None + UnitDec = {1:'BYTE', 2:'WORD', 4:'DWORD', 8:'QWORD'} + for Idx, SubItem in enumerate(Item['subreg']): + if Unit is None: + Unit = SubItem['bitunit'] + BitLength = SubItem['bitlength'] + BitTotal += BitLength + BitOffset += BitLength + + if BitOffset > 64 or BitOffset > Unit * 8: + break + + if BitOffset == Unit * 8: + for SubIdx in range (StartIdx, Idx + 1): + Item['subreg'][SubIdx]['bitunit'] = Unit + BitOffset = 0 + StartIdx = Idx + 1 + Unit = None + + if BitOffset > 0: + raise Exception ("Bit fields cannot fit into %s for '%s.%s' !" % (UnitDec[Unit], Item['cname'], SubItem['cname'])) + + ExpectedTotal = Item['length'] * 8 + if Item['length'] * 8 != BitTotal: + raise Exception ("Bit fields total length (%d) does not match length (%d) of '%s' !" % (BitTotal, ExpectedTotal, Item['cname'])) + + def UpdateDefaultValue (self): + Error = 0 + for Idx, Item in enumerate(self._CfgItemList): + if len(Item['subreg']) == 0: + Value = Item['value'] + if (len(Value) > 0) and (Value[0] == '{' or Value[0] == "'" or Value[0] == '"'): + # {XXX} or 'XXX' strings + self.FormatListValue(self._CfgItemList[Idx]) + else: + Match = re.match("(0x[0-9a-fA-F]+|[0-9]+)", Value) + if not Match: + NumValue = self.EvaluateExpress (Value) + Item['value'] = '0x%X' % NumValue + else: + ValArray = self.ValueToByteArray (Item['value'], Item['length']) + for SubItem in Item['subreg']: + SubItem['value'] = self.GetBsfBitFields(SubItem, ValArray) + self.UpdateBsfBitUnit (Item) + return Error + + def ProcessMultilines (self, String, MaxCharLength): + Multilines = '' + StringLength = len(String) + CurrentStringStart = 0 + StringOffset = 0 + BreakLineDict = [] + if len(String) <= MaxCharLength: + while (StringOffset < StringLength): + if StringOffset >= 1: + if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n': + BreakLineDict.append (StringOffset + 1) + StringOffset += 1 + if BreakLineDict != []: + for Each in BreakLineDict: + Multilines += " %s\n" % String[CurrentStringStart:Each].lstrip() + CurrentStringStart = Each + if StringLength - CurrentStringStart > 0: + Multilines += " %s\n" % String[CurrentStringStart:].lstrip() + else: + Multilines = " %s\n" % String + else: + NewLineStart = 0 + NewLineCount = 0 + FoundSpaceChar = False + while (StringOffset < StringLength): + if StringOffset >= 1: + if NewLineCount >= MaxCharLength - 1: + if String[StringOffset] == ' ' and StringLength - StringOffset > 10: + BreakLineDict.append (NewLineStart + NewLineCount) + NewLineStart = NewLineStart + NewLineCount + NewLineCount = 0 + FoundSpaceChar = True + elif StringOffset == StringLength - 1 and FoundSpaceChar == False: + BreakLineDict.append (0) + if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n': + BreakLineDict.append (StringOffset + 1) + NewLineStart = StringOffset + 1 + NewLineCount = 0 + StringOffset += 1 + NewLineCount += 1 + if BreakLineDict != []: + BreakLineDict.sort () + for Each in BreakLineDict: + if Each > 0: + Multilines += " %s\n" % String[CurrentStringStart:Each].lstrip() + CurrentStringStart = Each + if StringLength - CurrentStringStart > 0: + Multilines += " %s\n" % String[CurrentStringStart:].lstrip() + return Multilines + + def CreateField (self, Item, Name, Length, Offset, Struct, BsfName, Help, Option, BitsLength = None): + PosName = 28 + PosComment = 30 + NameLine='' + HelpLine='' + OptionLine='' + + if Length == 0 and Name == 'Dummy': + return '\n' + + IsArray = False + if Length in [1,2,4,8]: + Type = "UINT%d" % (Length * 8) + if Name.startswith("UnusedUpdSpace") and Length != 1: + IsArray = True + Type = "UINT8" + else: + IsArray = True + Type = "UINT8" + + if Item and Item['value'].startswith('{'): + Type = "UINT8" + IsArray = True + + if Struct != '': + Type = Struct + if Struct in ['UINT8','UINT16','UINT32','UINT64']: + IsArray = True + Unit = int(Type[4:]) / 8 + Length = Length / Unit + else: + IsArray = False + + if IsArray: + Name = Name + '[%d]' % Length + + if len(Type) < PosName: + Space1 = PosName - len(Type) + else: + Space1 = 1 + + if BsfName != '': + NameLine=" - %s\n" % BsfName + else: + NameLine="\n" + + if Help != '': + HelpLine = self.ProcessMultilines (Help, 80) + + if Option != '': + OptionLine = self.ProcessMultilines (Option, 80) + + if Offset is None: + OffsetStr = '????' + else: + OffsetStr = '0x%04X' % Offset + + if BitsLength is None: + BitsLength = '' + else: + BitsLength = ' : %d' % BitsLength + + return "\n/** Offset %s%s%s%s**/\n %s%s%s%s;\n" % (OffsetStr, NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name, BitsLength) + + def PostProcessBody (self, TextBody): + NewTextBody = [] + OldTextBody = [] + IncludeLine = False + StructName = '' + VariableName = '' + IsUpdHdrDefined = False + IsUpdHeader = False + for Line in TextBody: + SplitToLines = Line.splitlines() + MatchComment = re.match("^/\*\sCOMMENT:(\w+):([\w|\W|\s]+)\s\*/\s([\s\S]*)", SplitToLines[0]) + if MatchComment: + if MatchComment.group(1) == 'FSP_UPD_HEADER': + IsUpdHeader = True + else: + IsUpdHeader = False + if IsUpdHdrDefined != True or IsUpdHeader != True: + CommentLine = " " + MatchComment.group(2) + "\n" + NewTextBody.append("/**" + CommentLine + "**/\n") + Line = Line[(len(SplitToLines[0]) + 1):] + + Match = re.match("^/\*\sEMBED_STRUCT:(\w+):(\w+):(START|END)\s\*/\s([\s\S]*)", Line) + if Match: + Line = Match.group(4) + if Match.group(1) == 'FSP_UPD_HEADER': + IsUpdHeader = True + else: + IsUpdHeader = False + + if Match and Match.group(3) == 'START': + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append ('typedef struct {\n') + StructName = Match.group(1) + VariableName = Match.group(2) + MatchOffset = re.search('/\*\*\sOffset\s0x([a-fA-F0-9]+)', Line) + if MatchOffset: + Offset = int(MatchOffset.group(1), 16) + else: + Offset = None + Line + IncludeLine = True + OldTextBody.append (self.CreateField (None, VariableName, 0, Offset, StructName, '', '', '')) + if IncludeLine: + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append (Line) + else: + OldTextBody.append (Line) + + if Match and Match.group(3) == 'END': + if (StructName != Match.group(1)) or (VariableName != Match.group(2)): + print ("Unmatched struct name '%s' and '%s' !" % (StructName, Match.group(1))) + else: + if IsUpdHdrDefined != True or IsUpdHeader != True: + NewTextBody.append ('} %s;\n\n' % StructName) + IsUpdHdrDefined = True + IncludeLine = False + NewTextBody.extend(OldTextBody) + return NewTextBody + + def WriteLinesWithoutTailingSpace (self, HeaderFd, Line): + TxtBody2 = Line.splitlines(True) + for Line2 in TxtBody2: + Line2 = Line2.rstrip() + Line2 += '\n' + HeaderFd.write (Line2) + return 0 + def CreateHeaderFile (self, InputHeaderFile): + FvDir = self._FvDir + + HeaderFileName = 'FspUpd.h' + HeaderFile = os.path.join(FvDir, HeaderFileName) + + # Check if header needs to be recreated + if (self.NoDscFileChange (HeaderFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD header file' + return 256 + + TxtBody = [] + for Item in self._CfgItemList: + if str(Item['cname']) == 'Signature' and Item['length'] == 8: + Value = int(Item['value'], 16) + Chars = [] + while Value != 0x0: + Chars.append(chr(Value & 0xFF)) + Value = Value >> 8 + SignatureStr = ''.join(Chars) + # Signature will be _T / _M / _S for FSPT / FSPM / FSPS accordingly + if '_T' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPT_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + elif '_M' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPM_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + elif '_S' in SignatureStr[6:6+2]: + TxtBody.append("#define FSPS_UPD_SIGNATURE %s /* '%s' */\n\n" % (Item['value'], SignatureStr)) + TxtBody.append("\n") + + for Region in ['UPD']: + UpdOffsetTable = [] + UpdSignature = ['0x545F', '0x4D5F', '0x535F'] #['_T', '_M', '_S'] signature for FSPT, FSPM, FSPS + UpdStructure = ['FSPT_UPD', 'FSPM_UPD', 'FSPS_UPD'] + for Item in self._CfgItemList: + if Item["cname"] == 'Signature' and Item["value"][0:6] in UpdSignature: + Item["offset"] = 0 # re-initialize offset to 0 when new UPD structure starting + UpdOffsetTable.append (Item["offset"]) + + for UpdIdx in range(len(UpdOffsetTable)): + CommentLine = "" + for Item in self._CfgItemList: + if Item["comment"] != '' and Item["offset"] >= UpdOffsetTable[UpdIdx]: + MatchComment = re.match("^(U|V)PD_DATA_REGION:([\w|\W|\s]+)", Item["comment"]) + if MatchComment and MatchComment.group(1) == Region[0]: + CommentLine = " " + MatchComment.group(2) + "\n" + TxtBody.append("/**" + CommentLine + "**/\n") + elif Item["offset"] >= UpdOffsetTable[UpdIdx] and Item["comment"] == '': + Match = re.match("^FSP([\w|\W|\s])_UPD", UpdStructure[UpdIdx]) + if Match: + TxtBody.append("/** Fsp " + Match.group(1) + " UPD Configuration\n**/\n") + TxtBody.append("typedef struct {\n") + NextOffset = 0 + SpaceIdx = 0 + Offset = 0 + + LastVisible = True + ResvOffset = 0 + ResvIdx = 0 + LineBuffer = [] + InRange = False + for Item in self._CfgItemList: + if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == UpdSignature[UpdIdx] or Region[0] == 'V': + InRange = True + if InRange != True: + continue + if Item['cname'] == 'UpdTerminator': + InRange = False + + if Item['region'] != Region: + continue + + if Item["offset"] < UpdOffsetTable[UpdIdx]: + continue + + NextVisible = LastVisible + + if LastVisible and (Item['header'] == 'OFF'): + NextVisible = False + ResvOffset = Item['offset'] + elif (not LastVisible) and Item['header'] == 'ON': + NextVisible = True + Name = "Reserved" + Region[0] + "pdSpace%d" % ResvIdx + ResvIdx = ResvIdx + 1 + TxtBody.append(self.CreateField (Item, Name, Item["offset"] - ResvOffset, ResvOffset, '', '', '', '')) + + if Offset < Item["offset"]: + if LastVisible: + Name = "Unused" + Region[0] + "pdSpace%d" % SpaceIdx + LineBuffer.append(self.CreateField (Item, Name, Item["offset"] - Offset, Offset, '', '', '', '')) + SpaceIdx = SpaceIdx + 1 + Offset = Item["offset"] + + LastVisible = NextVisible + + Offset = Offset + Item["length"] + if LastVisible: + for Each in LineBuffer: + TxtBody.append (Each) + LineBuffer = [] + Comment = Item["comment"] + Embed = Item["embed"].upper() + if Embed.endswith(':START') or Embed.endswith(':END'): + if not Comment == '' and Embed.endswith(':START'): + Marker = '/* COMMENT:%s */ \n' % Item["comment"] + Marker = Marker + '/* EMBED_STRUCT:%s */ ' % Item["embed"] + else: + Marker = '/* EMBED_STRUCT:%s */ ' % Item["embed"] + else: + if Embed == '': + Marker = '' + else: + self.Error = "Invalid embedded structure format '%s'!\n" % Item["embed"] + return 4 + Line = Marker + self.CreateField (Item, Item["cname"], Item["length"], Item["offset"], Item['struct'], Item['name'], Item['help'], Item['option']) + TxtBody.append(Line) + if Item['cname'] == 'UpdTerminator': + break + TxtBody.append("} " + UpdStructure[UpdIdx] + ";\n\n") + + # Handle the embedded data structure + TxtBody = self.PostProcessBody (TxtBody) + + HeaderTFileName = 'FsptUpd.h' + HeaderMFileName = 'FspmUpd.h' + HeaderSFileName = 'FspsUpd.h' + + UpdRegionCheck = ['FSPT', 'FSPM', 'FSPS'] # FSPX_UPD_REGION + UpdConfigCheck = ['FSP_T', 'FSP_M', 'FSP_S'] # FSP_X_CONFIG, FSP_X_TEST_CONFIG, FSP_X_RESTRICTED_CONFIG + UpdSignatureCheck = ['FSPT_UPD_SIGNATURE', 'FSPM_UPD_SIGNATURE', 'FSPS_UPD_SIGNATURE'] + ExcludedSpecificUpd = ['FSPT_ARCH_UPD', 'FSPM_ARCH_UPD', 'FSPS_ARCH_UPD'] + + IncLines = [] + if InputHeaderFile != '': + if not os.path.exists(InputHeaderFile): + self.Error = "Input header file '%s' does not exist" % InputHeaderFile + return 6 + + InFd = open(InputHeaderFile, "r") + IncLines = InFd.readlines() + InFd.close() + + for item in range(len(UpdRegionCheck)): + if UpdRegionCheck[item] == 'FSPT': + HeaderFd = open(os.path.join(FvDir, HeaderTFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderTFileName)) + elif UpdRegionCheck[item] == 'FSPM': + HeaderFd = open(os.path.join(FvDir, HeaderMFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderMFileName)) + elif UpdRegionCheck[item] == 'FSPS': + HeaderFd = open(os.path.join(FvDir, HeaderSFileName), "w") + FileBase = os.path.basename(os.path.join(FvDir, HeaderSFileName)) + FileName = FileBase.replace(".", "_").upper() + HeaderFd.write("%s\n" % (__copyright_h__ % date.today().year)) + HeaderFd.write("#ifndef __%s__\n" % FileName) + HeaderFd.write("#define __%s__\n\n" % FileName) + HeaderFd.write("#include <%s>\n\n" % HeaderFileName) + HeaderFd.write("#pragma pack(1)\n\n") + + Export = False + for Line in IncLines: + Match = re.search ("!EXPORT\s+([A-Z]+)\s+EXTERNAL_BOOTLOADER_STRUCT_(BEGIN|END)\s+", Line) + if Match: + if Match.group(2) == "BEGIN" and Match.group(1) == UpdRegionCheck[item]: + Export = True + continue + else: + Export = False + continue + if Export: + HeaderFd.write(Line) + HeaderFd.write("\n") + + Index = 0 + StartIndex = 0 + EndIndex = 0 + StructStart = [] + StructStartWithComment = [] + StructEnd = [] + for Line in TxtBody: + Index += 1 + Match = re.match("(typedef struct {)", Line) + if Match: + StartIndex = Index - 1 + Match = re.match("}\s([_A-Z0-9]+);", Line) + if Match and (UpdRegionCheck[item] in Match.group(1) or UpdConfigCheck[item] in Match.group(1)) and (ExcludedSpecificUpd[item] not in Match.group(1)): + EndIndex = Index + StructStart.append(StartIndex) + StructEnd.append(EndIndex) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index == StructStart[Item]: + Match = re.match("^(/\*\*\s*)", Line) + if Match: + StructStartWithComment.append(StructStart[Item]) + else: + StructStartWithComment.append(StructStart[Item] + 1) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]: + self.WriteLinesWithoutTailingSpace(HeaderFd, Line) + HeaderFd.write("#pragma pack()\n\n") + HeaderFd.write("#endif\n") + HeaderFd.close() + + HeaderFd = open(HeaderFile, "w") + FileBase = os.path.basename(HeaderFile) + FileName = FileBase.replace(".", "_").upper() + HeaderFd.write("%s\n" % (__copyright_h__ % date.today().year)) + HeaderFd.write("#ifndef __%s__\n" % FileName) + HeaderFd.write("#define __%s__\n\n" % FileName) + HeaderFd.write("#include <FspEas.h>\n\n") + HeaderFd.write("#pragma pack(1)\n\n") + + for item in range(len(UpdRegionCheck)): + Index = 0 + StartIndex = 0 + EndIndex = 0 + StructStart = [] + StructStartWithComment = [] + StructEnd = [] + for Line in TxtBody: + Index += 1 + Match = re.match("(typedef struct {)", Line) + if Match: + StartIndex = Index - 1 + Match = re.match("#define\s([_A-Z0-9]+)\s*", Line) + if Match and (UpdSignatureCheck[item] in Match.group(1) or UpdSignatureCheck[item] in Match.group(1)): + StructStart.append(Index - 1) + StructEnd.append(Index) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index == StructStart[Item]: + Match = re.match("^(/\*\*\s*)", Line) + if Match: + StructStartWithComment.append(StructStart[Item]) + else: + StructStartWithComment.append(StructStart[Item] + 1) + Index = 0 + for Line in TxtBody: + Index += 1 + for Item in range(len(StructStart)): + if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]: + self.WriteLinesWithoutTailingSpace(HeaderFd, Line) + HeaderFd.write("#pragma pack()\n\n") + HeaderFd.write("#endif\n") + HeaderFd.close() + + return 0 + + def WriteBsfStruct (self, BsfFd, Item): + LogExpr = CLogicalExpression() + if Item['type'] == "None": + Space = "gPlatformFspPkgTokenSpaceGuid" + else: + Space = Item['space'] + Line = " $%s_%s" % (Space, Item['cname']) + Match = re.match("\s*\{([x0-9a-fA-F,\s]+)\}\s*", Item['value']) + if Match: + DefaultValue = Match.group(1).strip() + else: + DefaultValue = Item['value'].strip() + if 'bitlength' in Item: + BsfFd.write(" %s%s%4d bits $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['bitlength'], DefaultValue)) + else: + BsfFd.write(" %s%s%4d bytes $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['length'], DefaultValue)) + TmpList = [] + if Item['type'] == "Combo": + if not Item['option'] in self._BuidinOption: + OptList = Item['option'].split(',') + for Option in OptList: + Option = Option.strip() + (OpVal, OpStr) = Option.split(':') + test = LogExpr.getNumber (OpVal) + if test is None: + raise Exception("Selection Index '%s' is not a number" % OpVal) + TmpList.append((OpVal, OpStr)) + return TmpList + + def WriteBsfOption (self, BsfFd, Item): + PcdName = Item['space'] + '_' + Item['cname'] + WriteHelp = 0 + if Item['type'] == "Combo": + if Item['option'] in self._BuidinOption: + Options = self._BuidinOption[Item['option']] + else: + Options = PcdName + BsfFd.write(' %s $%s, "%s", &%s,\n' % (Item['type'], PcdName, Item['name'], Options)) + WriteHelp = 1 + elif Item['type'].startswith("EditNum"): + Match = re.match("EditNum\s*,\s*(HEX|DEC)\s*,\s*\((\d+|0x[0-9A-Fa-f]+)\s*,\s*(\d+|0x[0-9A-Fa-f]+)\)", Item['type']) + if Match: + BsfFd.write(' EditNum $%s, "%s", %s,\n' % (PcdName, Item['name'], Match.group(1))) + WriteHelp = 2 + elif Item['type'].startswith("EditText"): + BsfFd.write(' %s $%s, "%s",\n' % (Item['type'], PcdName, Item['name'])) + WriteHelp = 1 + elif Item['type'] == "Table": + Columns = Item['option'].split(',') + if len(Columns) != 0: + BsfFd.write(' %s $%s "%s",' % (Item['type'], PcdName, Item['name'])) + for Col in Columns: + Fmt = Col.split(':') + if len(Fmt) != 3: + raise Exception("Column format '%s' is invalid !" % Fmt) + try: + Dtype = int(Fmt[1].strip()) + except: + raise Exception("Column size '%s' is invalid !" % Fmt[1]) + BsfFd.write('\n Column "%s", %d bytes, %s' % (Fmt[0].strip(), Dtype, Fmt[2].strip())) + BsfFd.write(',\n') + WriteHelp = 1 + + if WriteHelp > 0: + HelpLines = Item['help'].split('\\n\\r') + FirstLine = True + for HelpLine in HelpLines: + if FirstLine: + FirstLine = False + BsfFd.write(' Help "%s"\n' % (HelpLine)) + else: + BsfFd.write(' "%s"\n' % (HelpLine)) + if WriteHelp == 2: + BsfFd.write(' "Valid range: %s ~ %s"\n' % (Match.group(2), Match.group(3))) + + def GenerateBsfFile (self, BsfFile): + + if BsfFile == '': + self.Error = "BSF output file '%s' is invalid" % BsfFile + return 1 + + if (self.NoDscFileChange (BsfFile)): + # DSC has not been modified yet + # So don't have to re-generate other files + self.Error = 'No DSC file change, skip to create UPD BSF file' + return 256 + + Error = 0 + OptionDict = {} + BsfFd = open(BsfFile, "w") + BsfFd.write("%s\n" % (__copyright_bsf__ % date.today().year)) + BsfFd.write("%s\n" % self._GlobalDataDef) + BsfFd.write("StructDef\n") + NextOffset = -1 + for Item in self._CfgItemList: + if Item['find'] != '': + BsfFd.write('\n Find "%s"\n' % Item['find']) + NextOffset = Item['offset'] + Item['length'] + if Item['name'] != '': + if NextOffset != Item['offset']: + BsfFd.write(" Skip %d bytes\n" % (Item['offset'] - NextOffset)) + if len(Item['subreg']) > 0: + NextOffset = Item['offset'] + BitsOffset = NextOffset * 8 + for SubItem in Item['subreg']: + BitsOffset += SubItem['bitlength'] + if SubItem['name'] == '': + if 'bitlength' in SubItem: + BsfFd.write(" Skip %d bits\n" % (SubItem['bitlength'])) + else: + BsfFd.write(" Skip %d bytes\n" % (SubItem['length'])) + else: + Options = self.WriteBsfStruct(BsfFd, SubItem) + if len(Options) > 0: + OptionDict[SubItem['space']+'_'+SubItem['cname']] = Options + + NextBitsOffset = (Item['offset'] + Item['length']) * 8 + if NextBitsOffset > BitsOffset: + BitsGap = NextBitsOffset - BitsOffset + BitsRemain = BitsGap % 8 + if BitsRemain: + BsfFd.write(" Skip %d bits\n" % BitsRemain) + BitsGap -= BitsRemain + BytesRemain = int(BitsGap / 8) + if BytesRemain: + BsfFd.write(" Skip %d bytes\n" % BytesRemain) + NextOffset = Item['offset'] + Item['length'] + else: + NextOffset = Item['offset'] + Item['length'] + Options = self.WriteBsfStruct(BsfFd, Item) + if len(Options) > 0: + OptionDict[Item['space']+'_'+Item['cname']] = Options + BsfFd.write("\nEndStruct\n\n") + + BsfFd.write("%s" % self._BuidinOptionTxt) + + for Each in OptionDict: + BsfFd.write("List &%s\n" % Each) + for Item in OptionDict[Each]: + BsfFd.write(' Selection %s , "%s"\n' % (Item[0], Item[1])) + BsfFd.write("EndList\n\n") + + BsfFd.write("BeginInfoBlock\n") + BsfFd.write(' PPVer "%s"\n' % (self._CfgBlkDict['ver'])) + BsfFd.write(' Description "%s"\n' % (self._CfgBlkDict['name'])) + BsfFd.write("EndInfoBlock\n\n") + + for Each in self._CfgPageDict: + BsfFd.write('Page "%s"\n' % self._CfgPageDict[Each]) + BsfItems = [] + for Item in self._CfgItemList: + if Item['name'] != '': + if Item['page'] != Each: + continue + if len(Item['subreg']) > 0: + for SubItem in Item['subreg']: + if SubItem['name'] != '': + BsfItems.append(SubItem) + else: + BsfItems.append(Item) + + BsfItems.sort(key=lambda x: x['order']) + + for Item in BsfItems: + self.WriteBsfOption (BsfFd, Item) + BsfFd.write("EndPage\n\n") + + BsfFd.close() + return Error + + +def Usage(): + print ("GenCfgOpt Version 0.56") + print ("Usage:") + print (" GenCfgOpt UPDTXT PlatformDscFile BuildFvDir [-D Macros]") + print (" GenCfgOpt HEADER PlatformDscFile BuildFvDir InputHFile [-D Macros]") + print (" GenCfgOpt GENBSF PlatformDscFile BuildFvDir BsfOutFile [-D Macros]") + +def Main(): + # + # Parse the options and args + # + i = 1 + + GenCfgOpt = CGenCfgOpt() + while i < len(sys.argv): + if sys.argv[i].strip().lower() == "--pcd": + BuildOptionPcd.append(sys.argv[i+1]) + i += 1 + i += 1 + argc = len(sys.argv) + if argc < 4: + Usage() + return 1 + else: + DscFile = sys.argv[2] + if not os.path.exists(DscFile): + print ("ERROR: Cannot open DSC file '%s' !" % DscFile) + return 2 + + OutFile = '' + if argc > 4: + if sys.argv[4][0] == '-': + Start = 4 + else: + OutFile = sys.argv[4] + Start = 5 + if argc > Start: + if GenCfgOpt.ParseMacros(sys.argv[Start:]) != 0: + print ("ERROR: Macro parsing failed !") + return 3 + + FvDir = sys.argv[3] + if not os.path.exists(FvDir): + os.makedirs(FvDir) + + if GenCfgOpt.ParseDscFile(DscFile, FvDir) != 0: + print ("ERROR: %s !" % GenCfgOpt.Error) + return 5 + + if GenCfgOpt.UpdateSubRegionDefaultValue() != 0: + print ("ERROR: %s !" % GenCfgOpt.Error) + return 7 + + if sys.argv[1] == "UPDTXT": + Ret = GenCfgOpt.CreateSplitUpdTxt(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return Ret + elif sys.argv[1] == "HEADER": + Ret = GenCfgOpt.CreateHeaderFile(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return 8 + return Ret + elif sys.argv[1] == "GENBSF": + Ret = GenCfgOpt.GenerateBsfFile(OutFile) + if Ret != 0: + # No change is detected + if Ret == 256: + print ("INFO: %s !" % (GenCfgOpt.Error)) + else : + print ("ERROR: %s !" % (GenCfgOpt.Error)) + return 9 + return Ret + else: + if argc < 5: + Usage() + return 1 + print ("ERROR: Unknown command '%s' !" % sys.argv[1]) + Usage() + return 1 + return 0 + return 0 + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/PatchFv.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/PatchFv.py new file mode 100755 index 00000000..adec12ea --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/PatchFv.py @@ -0,0 +1,954 @@ +## @ PatchFv.py +# +# Copyright (c) 2014 - 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +import os +import re +import sys + +# +# Read data from file +# +# param [in] binfile Binary file +# param [in] offset Offset +# param [in] len Length +# +# retval value Value +# +def readDataFromFile (binfile, offset, len=1): + fd = open(binfile, "r+b") + fsize = os.path.getsize(binfile) + offval = offset & 0xFFFFFFFF + if (offval & 0x80000000): + offval = fsize - (0xFFFFFFFF - offval + 1) + fd.seek(offval) + if sys.version_info[0] < 3: + bytearray = [ord(b) for b in fd.read(len)] + else: + bytearray = [b for b in fd.read(len)] + value = 0 + idx = len - 1 + while idx >= 0: + value = value << 8 | bytearray[idx] + idx = idx - 1 + fd.close() + return value + +# +# Check FSP header is valid or not +# +# param [in] binfile Binary file +# +# retval boolean True: valid; False: invalid +# +def IsFspHeaderValid (binfile): + fd = open (binfile, "rb") + bindat = fd.read(0x200) # only read first 0x200 bytes + fd.close() + HeaderList = [b'FSPH' , b'FSPP' , b'FSPE'] # Check 'FSPH', 'FSPP', and 'FSPE' in the FSP header + OffsetList = [] + for each in HeaderList: + if each in bindat: + idx = bindat.index(each) + else: + idx = 0 + OffsetList.append(idx) + if not OffsetList[0] or not OffsetList[1]: # If 'FSPH' or 'FSPP' is missing, it will return false + return False + if sys.version_info[0] < 3: + Revision = ord(bindat[OffsetList[0] + 0x0B]) + else: + Revision = bindat[OffsetList[0] + 0x0B] + # + # if revision is bigger than 1, it means it is FSP v1.1 or greater revision, which must contain 'FSPE'. + # + if Revision > 1 and not OffsetList[2]: + return False # If FSP v1.1 or greater without 'FSPE', then return false + return True + +# +# Patch data in file +# +# param [in] binfile Binary file +# param [in] offset Offset +# param [in] value Patch value +# param [in] len Length +# +# retval len Length +# +def patchDataInFile (binfile, offset, value, len=1): + fd = open(binfile, "r+b") + fsize = os.path.getsize(binfile) + offval = offset & 0xFFFFFFFF + if (offval & 0x80000000): + offval = fsize - (0xFFFFFFFF - offval + 1) + bytearray = [] + idx = 0 + while idx < len: + bytearray.append(value & 0xFF) + value = value >> 8 + idx = idx + 1 + fd.seek(offval) + if sys.version_info[0] < 3: + fd.write("".join(chr(b) for b in bytearray)) + else: + fd.write(bytes(bytearray)) + fd.close() + return len + + +class Symbols: + def __init__(self): + self.dictSymbolAddress = {} + self.dictGuidNameXref = {} + self.dictFfsOffset = {} + self.dictVariable = {} + self.dictModBase = {} + self.fdFile = None + self.string = "" + self.fdBase = 0xFFFFFFFF + self.fdSize = 0 + self.index = 0 + self.fvList = [] + self.parenthesisOpenSet = '([{<' + self.parenthesisCloseSet = ')]}>' + + # + # Get FD file + # + # retval self.fdFile Retrieve FD file + # + def getFdFile (self): + return self.fdFile + + # + # Get FD size + # + # retval self.fdSize Retrieve the size of FD file + # + def getFdSize (self): + return self.fdSize + + def parseFvInfFile (self, infFile): + fvInfo = {} + fvFile = infFile[0:-4] + ".Fv" + fvInfo['Name'] = os.path.splitext(os.path.basename(infFile))[0] + fvInfo['Offset'] = self.getFvOffsetInFd(fvFile) + fvInfo['Size'] = readDataFromFile (fvFile, 0x20, 4) + fdIn = open(infFile, "r") + rptLines = fdIn.readlines() + fdIn.close() + fvInfo['Base'] = 0 + for rptLine in rptLines: + match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine) + if match: + fvInfo['Base'] = int(match.group(1), 16) + break + self.fvList.append(dict(fvInfo)) + return 0 + + # + # Create dictionaries + # + # param [in] fvDir FV's directory + # param [in] fvNames All FV's names + # + # retval 0 Created dictionaries successfully + # + def createDicts (self, fvDir, fvNames): + # + # If the fvDir is not a directory, then raise an exception + # + if not os.path.isdir(fvDir): + raise Exception ("'%s' is not a valid directory!" % fvDir) + + # + # If the Guid.xref is not existing in fvDir, then raise an exception + # + xrefFile = os.path.join(fvDir, "Guid.xref") + if not os.path.exists(xrefFile): + raise Exception("Cannot open GUID Xref file '%s'!" % xrefFile) + + # + # Add GUID reference to dictionary + # + self.dictGuidNameXref = {} + self.parseGuidXrefFile(xrefFile) + + # + # Split up each FV from fvNames and get the fdBase + # + fvList = fvNames.split(":") + fdBase = fvList.pop() + if len(fvList) == 0: + fvList.append(fdBase) + + # + # If the FD file is not existing, then raise an exception + # + fdFile = os.path.join(fvDir, fdBase.strip() + ".fd") + if not os.path.exists(fdFile): + raise Exception("Cannot open FD file '%s'!" % fdFile) + + # + # Get the size of the FD file + # + self.fdFile = fdFile + self.fdSize = os.path.getsize(fdFile) + + # + # If the INF file, which is the first element of fvList, is not existing, then raise an exception + # + infFile = os.path.join(fvDir, fvList[0].strip()) + ".inf" + if not os.path.exists(infFile): + raise Exception("Cannot open INF file '%s'!" % infFile) + + # + # Parse INF file in order to get fdBase and then assign those values to dictVariable + # + self.parseInfFile(infFile) + self.dictVariable = {} + self.dictVariable["FDSIZE"] = self.fdSize + self.dictVariable["FDBASE"] = self.fdBase + + # + # Collect information from FV MAP file and FV TXT file then + # put them into dictionaries + # + self.fvList = [] + self.dictSymbolAddress = {} + self.dictFfsOffset = {} + for file in fvList: + + # + # If the .Fv.map file is not existing, then raise an exception. + # Otherwise, parse FV MAP file + # + fvFile = os.path.join(fvDir, file.strip()) + ".Fv" + mapFile = fvFile + ".map" + if not os.path.exists(mapFile): + raise Exception("Cannot open MAP file '%s'!" % mapFile) + + infFile = fvFile[0:-3] + ".inf" + self.parseFvInfFile(infFile) + self.parseFvMapFile(mapFile) + + # + # If the .Fv.txt file is not existing, then raise an exception. + # Otherwise, parse FV TXT file + # + fvTxtFile = fvFile + ".txt" + if not os.path.exists(fvTxtFile): + raise Exception("Cannot open FV TXT file '%s'!" % fvTxtFile) + + self.parseFvTxtFile(fvTxtFile) + + for fv in self.fvList: + self.dictVariable['_BASE_%s_' % fv['Name']] = fv['Base'] + # + # Search all MAP files in FFS directory if it exists then parse MOD MAP file + # + ffsDir = os.path.join(fvDir, "Ffs") + if (os.path.isdir(ffsDir)): + for item in os.listdir(ffsDir): + if len(item) <= 0x24: + continue + mapFile =os.path.join(ffsDir, item, "%s.map" % item[0:0x24]) + if not os.path.exists(mapFile): + continue + self.parseModMapFile(item[0x24:], mapFile) + + return 0 + + # + # Get FV offset in FD file + # + # param [in] fvFile FV file + # + # retval offset Got FV offset successfully + # + def getFvOffsetInFd(self, fvFile): + # + # Check if the first 0x70 bytes of fvFile can be found in fdFile + # + fvHandle = open(fvFile, "r+b") + fdHandle = open(self.fdFile, "r+b") + offset = fdHandle.read().find(fvHandle.read(0x70)) + fvHandle.close() + fdHandle.close() + if offset == -1: + raise Exception("Could not locate FV file %s in FD!" % fvFile) + return offset + + # + # Parse INF file + # + # param [in] infFile INF file + # + # retval 0 Parsed INF file successfully + # + def parseInfFile(self, infFile): + # + # Get FV offset and search EFI_BASE_ADDRESS in the FD file + # then assign the value of EFI_BASE_ADDRESS to fdBase + # + fvOffset = self.getFvOffsetInFd(infFile[0:-4] + ".Fv") + fdIn = open(infFile, "r") + rptLine = fdIn.readline() + self.fdBase = 0xFFFFFFFF + while (rptLine != "" ): + #EFI_BASE_ADDRESS = 0xFFFDF400 + match = re.match("^EFI_BASE_ADDRESS\s*=\s*(0x[a-fA-F0-9]+)", rptLine) + if match is not None: + self.fdBase = int(match.group(1), 16) - fvOffset + rptLine = fdIn.readline() + fdIn.close() + if self.fdBase == 0xFFFFFFFF: + raise Exception("Could not find EFI_BASE_ADDRESS in INF file!" % fvFile) + return 0 + + # + # Parse FV TXT file + # + # param [in] fvTxtFile .Fv.txt file + # + # retval 0 Parsed FV TXT file successfully + # + def parseFvTxtFile(self, fvTxtFile): + fvName = os.path.basename(fvTxtFile)[0:-7].upper() + # + # Get information from .Fv.txt in order to create a dictionary + # For example, + # self.dictFfsOffset[912740BE-2284-4734-B971-84B027353F0C] = 0x000D4078 + # + fvOffset = self.getFvOffsetInFd(fvTxtFile[0:-4]) + fdIn = open(fvTxtFile, "r") + rptLine = fdIn.readline() + while (rptLine != "" ): + match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", rptLine) + if match is not None: + if match.group(2) in self.dictFfsOffset: + self.dictFfsOffset[fvName + ':' + match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset) + else: + self.dictFfsOffset[match.group(2)] = "0x%08X" % (int(match.group(1), 16) + fvOffset) + rptLine = fdIn.readline() + fdIn.close() + return 0 + + # + # Parse FV MAP file + # + # param [in] mapFile .Fv.map file + # + # retval 0 Parsed FV MAP file successfully + # + def parseFvMapFile(self, mapFile): + # + # Get information from .Fv.map in order to create dictionaries + # For example, + # self.dictModBase[FspSecCore:BASE] = 4294592776 (0xfffa4908) + # self.dictModBase[FspSecCore:ENTRY] = 4294606552 (0xfffa7ed8) + # self.dictModBase[FspSecCore:TEXT] = 4294593080 (0xfffa4a38) + # self.dictModBase[FspSecCore:DATA] = 4294612280 (0xfffa9538) + # self.dictSymbolAddress[FspSecCore:_SecStartup] = 0x00fffa4a38 + # + fdIn = open(mapFile, "r") + rptLine = fdIn.readline() + modName = "" + foundModHdr = False + while (rptLine != "" ): + if rptLine[0] != ' ': + #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958,Type=PE) + match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+),\s*Type=\w+\)", rptLine) + if match is None: + #DxeIpl (Fixed Flash Address, BaseAddress=0x00fffb4310, EntryPoint=0x00fffb4958) + match = re.match("([_a-zA-Z0-9\-]+)\s\(.+BaseAddress=(0x[0-9a-fA-F]+),\s+EntryPoint=(0x[0-9a-fA-F]+)\)", rptLine) + if match is not None: + foundModHdr = True + modName = match.group(1) + if len(modName) == 36: + modName = self.dictGuidNameXref[modName.upper()] + self.dictModBase['%s:BASE' % modName] = int (match.group(2), 16) + self.dictModBase['%s:ENTRY' % modName] = int (match.group(3), 16) + #(GUID=86D70125-BAA3-4296-A62F-602BEBBB9081 .textbaseaddress=0x00fffb4398 .databaseaddress=0x00fffb4178) + match = re.match("\(GUID=([A-Z0-9\-]+)\s+\.textbaseaddress=(0x[0-9a-fA-F]+)\s+\.databaseaddress=(0x[0-9a-fA-F]+)\)", rptLine) + if match is not None: + if foundModHdr: + foundModHdr = False + else: + modName = match.group(1) + if len(modName) == 36: + modName = self.dictGuidNameXref[modName.upper()] + self.dictModBase['%s:TEXT' % modName] = int (match.group(2), 16) + self.dictModBase['%s:DATA' % modName] = int (match.group(3), 16) + else: + # 0x00fff8016c __ModuleEntryPoint + foundModHdr = False + match = re.match("^\s+(0x[a-z0-9]+)\s+([_a-zA-Z0-9]+)", rptLine) + if match is not None: + self.dictSymbolAddress["%s:%s"%(modName, match.group(2))] = match.group(1) + rptLine = fdIn.readline() + fdIn.close() + return 0 + + # + # Parse MOD MAP file + # + # param [in] moduleName Module name + # param [in] mapFile .Fv.map file + # + # retval 0 Parsed MOD MAP file successfully + # retval 1 There is no moduleEntryPoint in modSymbols + # + def parseModMapFile(self, moduleName, mapFile): + # + # Get information from mapFile by moduleName in order to create a dictionary + # For example, + # self.dictSymbolAddress[FspSecCore:___guard_fids_count] = 0x00fffa4778 + # + modSymbols = {} + fdIn = open(mapFile, "r") + reportLines = fdIn.readlines() + fdIn.close() + + moduleEntryPoint = "__ModuleEntryPoint" + reportLine = reportLines[0] + if reportLine.strip().find("Archive member included") != -1: + #GCC + # 0x0000000000001d55 IoRead8 + patchMapFileMatchString = "\s+(0x[0-9a-fA-F]{16})\s+([^\s][^0x][_a-zA-Z0-9\-]+)\s" + matchKeyGroupIndex = 2 + matchSymbolGroupIndex = 1 + prefix = '_' + else: + #MSFT + #0003:00000190 _gComBase 00007a50 SerialPo + patchMapFileMatchString = "^\s[0-9a-fA-F]{4}:[0-9a-fA-F]{8}\s+(\w+)\s+([0-9a-fA-F]{8}\s+)" + matchKeyGroupIndex = 1 + matchSymbolGroupIndex = 2 + prefix = '' + + for reportLine in reportLines: + match = re.match(patchMapFileMatchString, reportLine) + if match is not None: + modSymbols[prefix + match.group(matchKeyGroupIndex)] = match.group(matchSymbolGroupIndex) + + # Handle extra module patchable PCD variable in Linux map since it might have different format + # .data._gPcd_BinaryPatch_PcdVpdBaseAddress + # 0x0000000000003714 0x4 /tmp/ccmytayk.ltrans1.ltrans.o + handleNext = False + if matchSymbolGroupIndex == 1: + for reportLine in reportLines: + if handleNext: + handleNext = False + pcdName = match.group(1) + match = re.match("\s+(0x[0-9a-fA-F]{16})\s+", reportLine) + if match is not None: + modSymbols[prefix + pcdName] = match.group(1) + else: + match = re.match("^\s\.data\.(_gPcd_BinaryPatch[_a-zA-Z0-9\-]+)", reportLine) + if match is not None: + handleNext = True + continue + + if not moduleEntryPoint in modSymbols: + return 1 + + modEntry = '%s:%s' % (moduleName,moduleEntryPoint) + if not modEntry in self.dictSymbolAddress: + modKey = '%s:ENTRY' % moduleName + if modKey in self.dictModBase: + baseOffset = self.dictModBase['%s:ENTRY' % moduleName] - int(modSymbols[moduleEntryPoint], 16) + else: + return 2 + else: + baseOffset = int(self.dictSymbolAddress[modEntry], 16) - int(modSymbols[moduleEntryPoint], 16) + for symbol in modSymbols: + fullSym = "%s:%s" % (moduleName, symbol) + if not fullSym in self.dictSymbolAddress: + self.dictSymbolAddress[fullSym] = "0x00%08x" % (baseOffset+ int(modSymbols[symbol], 16)) + return 0 + + # + # Parse Guid.xref file + # + # param [in] xrefFile the full directory of Guid.xref file + # + # retval 0 Parsed Guid.xref file successfully + # + def parseGuidXrefFile(self, xrefFile): + # + # Get information from Guid.xref in order to create a GuidNameXref dictionary + # The dictGuidNameXref, for example, will be like + # dictGuidNameXref [1BA0062E-C779-4582-8566-336AE8F78F09] = FspSecCore + # + fdIn = open(xrefFile, "r") + rptLine = fdIn.readline() + while (rptLine != "" ): + match = re.match("([0-9a-fA-F\-]+)\s([_a-zA-Z0-9]+)", rptLine) + if match is not None: + self.dictGuidNameXref[match.group(1).upper()] = match.group(2) + rptLine = fdIn.readline() + fdIn.close() + return 0 + + # + # Get current character + # + # retval elf.string[self.index] + # retval '' Exception + # + def getCurr(self): + try: + return self.string[self.index] + except Exception: + return '' + + # + # Check to see if it is last index + # + # retval self.index + # + def isLast(self): + return self.index == len(self.string) + + # + # Move to next index + # + def moveNext(self): + self.index += 1 + + # + # Skip space + # + def skipSpace(self): + while not self.isLast(): + if self.getCurr() in ' \t': + self.moveNext() + else: + return + + # + # Parse value + # + # retval value + # + def parseValue(self): + self.skipSpace() + var = '' + while not self.isLast(): + char = self.getCurr() + if char.lower() in '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:-': + var += char + self.moveNext() + else: + break + + if ':' in var: + partList = var.split(':') + lenList = len(partList) + if lenList != 2 and lenList != 3: + raise Exception("Unrecognized expression %s" % var) + modName = partList[lenList-2] + modOff = partList[lenList-1] + if ('-' not in modName) and (modOff[0] in '0123456789'): + # MOD: OFFSET + var = self.getModGuid(modName) + ":" + modOff + if '-' in var: # GUID:OFFSET + value = self.getGuidOff(var) + else: + value = self.getSymbols(var) + self.synUsed = True + else: + if var[0] in '0123456789': + value = self.getNumber(var) + else: + value = self.getVariable(var) + return int(value) + + # + # Parse single operation + # + # retval ~self.parseBrace() or self.parseValue() + # + def parseSingleOp(self): + self.skipSpace() + char = self.getCurr() + if char == '~': + self.moveNext() + return ~self.parseBrace() + else: + return self.parseValue() + + # + # Parse symbol of Brace([, {, <) + # + # retval value or self.parseSingleOp() + # + def parseBrace(self): + self.skipSpace() + char = self.getCurr() + parenthesisType = self.parenthesisOpenSet.find(char) + if parenthesisType >= 0: + self.moveNext() + value = self.parseExpr() + self.skipSpace() + if self.getCurr() != self.parenthesisCloseSet[parenthesisType]: + raise Exception("No closing brace") + self.moveNext() + if parenthesisType == 1: # [ : Get content + value = self.getContent(value) + elif parenthesisType == 2: # { : To address + value = self.toAddress(value) + elif parenthesisType == 3: # < : To offset + value = self.toOffset(value) + return value + else: + return self.parseSingleOp() + + # + # Parse symbol of Multiplier(*) + # + # retval value or self.parseSingleOp() + # + def parseMul(self): + values = [self.parseBrace()] + while True: + self.skipSpace() + char = self.getCurr() + if char == '*': + self.moveNext() + values.append(self.parseBrace()) + else: + break + value = 1 + for each in values: + value *= each + return value + + # + # Parse symbol of And(&) and Or(|) + # + # retval value + # + def parseAndOr(self): + value = self.parseMul() + op = None + while True: + self.skipSpace() + char = self.getCurr() + if char == '&': + self.moveNext() + value &= self.parseMul() + elif char == '|': + div_index = self.index + self.moveNext() + value |= self.parseMul() + else: + break + + return value + + # + # Parse symbol of Add(+) and Minus(-) + # + # retval sum(values) + # + def parseAddMinus(self): + values = [self.parseAndOr()] + while True: + self.skipSpace() + char = self.getCurr() + if char == '+': + self.moveNext() + values.append(self.parseAndOr()) + elif char == '-': + self.moveNext() + values.append(-1 * self.parseAndOr()) + else: + break + return sum(values) + + # + # Parse expression + # + # retval self.parseAddMinus() + # + def parseExpr(self): + return self.parseAddMinus() + + # + # Get result + # + # retval value + # + def getResult(self): + value = self.parseExpr() + self.skipSpace() + if not self.isLast(): + raise Exception("Unexpected character found '%s'" % self.getCurr()) + return value + + # + # Get module GUID + # + # retval value + # + def getModGuid(self, var): + guid = (guid for guid,name in self.dictGuidNameXref.items() if name==var) + try: + value = guid.next() + except Exception: + raise Exception("Unknown module name %s !" % var) + return value + + # + # Get variable + # + # retval value + # + def getVariable(self, var): + value = self.dictVariable.get(var, None) + if value == None: + raise Exception("Unrecognized variable '%s'" % var) + return value + + # + # Get number + # + # retval value + # + def getNumber(self, var): + var = var.strip() + if var.startswith('0x'): # HEX + value = int(var, 16) + else: + value = int(var, 10) + return value + + # + # Get content + # + # param [in] value + # + # retval value + # + def getContent(self, value): + return readDataFromFile (self.fdFile, self.toOffset(value), 4) + + # + # Change value to address + # + # param [in] value + # + # retval value + # + def toAddress(self, value): + if value < self.fdSize: + value = value + self.fdBase + return value + + # + # Change value to offset + # + # param [in] value + # + # retval value + # + def toOffset(self, value): + offset = None + for fvInfo in self.fvList: + if (value >= fvInfo['Base']) and (value < fvInfo['Base'] + fvInfo['Size']): + offset = value - fvInfo['Base'] + fvInfo['Offset'] + if not offset: + if (value >= self.fdBase) and (value < self.fdBase + self.fdSize): + offset = value - self.fdBase + else: + offset = value + if offset >= self.fdSize: + raise Exception("Invalid file offset 0x%08x !" % value) + return offset + + # + # Get GUID offset + # + # param [in] value + # + # retval value + # + def getGuidOff(self, value): + # GUID:Offset + symbolName = value.split(':') + if len(symbolName) == 3: + fvName = symbolName[0].upper() + keyName = '%s:%s' % (fvName, symbolName[1]) + offStr = symbolName[2] + elif len(symbolName) == 2: + keyName = symbolName[0] + offStr = symbolName[1] + if keyName in self.dictFfsOffset: + value = (int(self.dictFfsOffset[keyName], 16) + int(offStr, 16)) & 0xFFFFFFFF + else: + raise Exception("Unknown GUID %s !" % value) + return value + + # + # Get symbols + # + # param [in] value + # + # retval ret + # + def getSymbols(self, value): + if value in self.dictSymbolAddress: + # Module:Function + ret = int (self.dictSymbolAddress[value], 16) + else: + raise Exception("Unknown symbol %s !" % value) + return ret + + # + # Evaluate symbols + # + # param [in] expression + # param [in] isOffset + # + # retval value & 0xFFFFFFFF + # + def evaluate(self, expression, isOffset): + self.index = 0 + self.synUsed = False + self.string = expression + value = self.getResult() + if isOffset: + if self.synUsed: + # Consider it as an address first + value = self.toOffset(value) + if value & 0x80000000: + # Consider it as a negative offset next + offset = (~value & 0xFFFFFFFF) + 1 + if offset < self.fdSize: + value = self.fdSize - offset + if value >= self.fdSize: + raise Exception("Invalid offset expression !") + return value & 0xFFFFFFFF + +# +# Print out the usage +# +def Usage(): + print ("PatchFv Version 0.50") + print ("Usage: \n\tPatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch \"Offset, Value\"") + +def main(): + # + # Parse the options and args + # + symTables = Symbols() + + # + # If the arguments are less than 4, then return an error. + # + if len(sys.argv) < 4: + Usage() + return 1 + + # + # If it fails to create dictionaries, then return an error. + # + if symTables.createDicts(sys.argv[1], sys.argv[2]) != 0: + print ("ERROR: Failed to create symbol dictionary!!") + return 2 + + # + # Get FD file and size + # + fdFile = symTables.getFdFile() + fdSize = symTables.getFdSize() + + try: + # + # Check to see if FSP header is valid + # + ret = IsFspHeaderValid(fdFile) + if ret == False: + raise Exception ("The FSP header is not valid. Stop patching FD.") + comment = "" + for fvFile in sys.argv[3:]: + # + # Check to see if it has enough arguments + # + items = fvFile.split(",") + if len (items) < 2: + raise Exception("Expect more arguments for '%s'!" % fvFile) + + comment = "" + command = "" + params = [] + for item in items: + item = item.strip() + if item.startswith("@"): + comment = item[1:] + elif item.startswith("$"): + command = item[1:] + else: + if len(params) == 0: + isOffset = True + else : + isOffset = False + # + # Parse symbols then append it to params + # + params.append (symTables.evaluate(item, isOffset)) + + # + # Patch a new value into FD file if it is not a command + # + if command == "": + # Patch a DWORD + if len (params) == 2: + offset = params[0] + value = params[1] + oldvalue = readDataFromFile(fdFile, offset, 4) + ret = patchDataInFile (fdFile, offset, value, 4) - 4 + else: + raise Exception ("Patch command needs 2 parameters !") + + if ret: + raise Exception ("Patch failed for offset 0x%08X" % offset) + else: + print ("Patched offset 0x%08X:[%08X] with value 0x%08X # %s" % (offset, oldvalue, value, comment)) + + elif command == "COPY": + # + # Copy binary block from source to destination + # + if len (params) == 3: + src = symTables.toOffset(params[0]) + dest = symTables.toOffset(params[1]) + clen = symTables.toOffset(params[2]) + if (dest + clen <= fdSize) and (src + clen <= fdSize): + oldvalue = readDataFromFile(fdFile, src, clen) + ret = patchDataInFile (fdFile, dest, oldvalue, clen) - clen + else: + raise Exception ("Copy command OFFSET or LENGTH parameter is invalid !") + else: + raise Exception ("Copy command needs 3 parameters !") + + if ret: + raise Exception ("Copy failed from offset 0x%08X to offset 0x%08X!" % (src, dest)) + else : + print ("Copied %d bytes from offset 0x%08X ~ offset 0x%08X # %s" % (clen, src, dest, comment)) + else: + raise Exception ("Unknown command %s!" % command) + return 0 + + except Exception as ex: + print ("ERROR: %s" % ex) + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/SplitFspBin.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/SplitFspBin.py new file mode 100755 index 00000000..fe90f90b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/SplitFspBin.py @@ -0,0 +1,892 @@ +## @ FspTool.py +# +# Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +import os +import sys +import uuid +import copy +import struct +import argparse +from ctypes import * +from functools import reduce + +""" +This utility supports some operations for Intel FSP 1.x/2.x image. +It supports: + - Display FSP 1.x/2.x information header + - Split FSP 2.x image into individual FSP-T/M/S/O component + - Rebase FSP 1.x/2.x components to a different base address + - Generate FSP 1.x/2.x mapping C header file +""" + +CopyRightHeaderFile = """/* + * + * Automatically generated file; DO NOT EDIT. + * FSP mapping file + * + */ +""" + +class c_uint24(Structure): + """Little-Endian 24-bit Unsigned Integer""" + _pack_ = 1 + _fields_ = [('Data', (c_uint8 * 3))] + + def __init__(self, val=0): + self.set_value(val) + + def __str__(self, indent=0): + return '0x%.6x' % self.value + + def __int__(self): + return self.get_value() + + def set_value(self, val): + self.Data[0:3] = Val2Bytes(val, 3) + + def get_value(self): + return Bytes2Val(self.Data[0:3]) + + value = property(get_value, set_value) + +class EFI_FIRMWARE_VOLUME_HEADER(Structure): + _fields_ = [ + ('ZeroVector', ARRAY(c_uint8, 16)), + ('FileSystemGuid', ARRAY(c_uint8, 16)), + ('FvLength', c_uint64), + ('Signature', ARRAY(c_char, 4)), + ('Attributes', c_uint32), + ('HeaderLength', c_uint16), + ('Checksum', c_uint16), + ('ExtHeaderOffset', c_uint16), + ('Reserved', c_uint8), + ('Revision', c_uint8) + ] + +class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure): + _fields_ = [ + ('FvName', ARRAY(c_uint8, 16)), + ('ExtHeaderSize', c_uint32) + ] + +class EFI_FFS_INTEGRITY_CHECK(Structure): + _fields_ = [ + ('Header', c_uint8), + ('File', c_uint8) + ] + +class EFI_FFS_FILE_HEADER(Structure): + _fields_ = [ + ('Name', ARRAY(c_uint8, 16)), + ('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK), + ('Type', c_uint8), + ('Attributes', c_uint8), + ('Size', c_uint24), + ('State', c_uint8) + ] + +class EFI_COMMON_SECTION_HEADER(Structure): + _fields_ = [ + ('Size', c_uint24), + ('Type', c_uint8) + ] + +class FSP_COMMON_HEADER(Structure): + _fields_ = [ + ('Signature', ARRAY(c_char, 4)), + ('HeaderLength', c_uint32) + ] + +class FSP_INFORMATION_HEADER(Structure): + _fields_ = [ + ('Signature', ARRAY(c_char, 4)), + ('HeaderLength', c_uint32), + ('Reserved1', c_uint16), + ('SpecVersion', c_uint8), + ('HeaderRevision', c_uint8), + ('ImageRevision', c_uint32), + ('ImageId', ARRAY(c_char, 8)), + ('ImageSize', c_uint32), + ('ImageBase', c_uint32), + ('ImageAttribute', c_uint16), + ('ComponentAttribute', c_uint16), + ('CfgRegionOffset', c_uint32), + ('CfgRegionSize', c_uint32), + ('Reserved2', c_uint32), + ('TempRamInitEntryOffset', c_uint32), + ('Reserved3', c_uint32), + ('NotifyPhaseEntryOffset', c_uint32), + ('FspMemoryInitEntryOffset', c_uint32), + ('TempRamExitEntryOffset', c_uint32), + ('FspSiliconInitEntryOffset', c_uint32) + ] + +class FSP_PATCH_TABLE(Structure): + _fields_ = [ + ('Signature', ARRAY(c_char, 4)), + ('HeaderLength', c_uint16), + ('HeaderRevision', c_uint8), + ('Reserved', c_uint8), + ('PatchEntryNum', c_uint32) + ] + +class EFI_IMAGE_DATA_DIRECTORY(Structure): + _fields_ = [ + ('VirtualAddress', c_uint32), + ('Size', c_uint32) + ] + +class EFI_TE_IMAGE_HEADER(Structure): + _fields_ = [ + ('Signature', ARRAY(c_char, 2)), + ('Machine', c_uint16), + ('NumberOfSections', c_uint8), + ('Subsystem', c_uint8), + ('StrippedSize', c_uint16), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('ImageBase', c_uint64), + ('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY), + ('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY) + ] + +class EFI_IMAGE_DOS_HEADER(Structure): + _fields_ = [ + ('e_magic', c_uint16), + ('e_cblp', c_uint16), + ('e_cp', c_uint16), + ('e_crlc', c_uint16), + ('e_cparhdr', c_uint16), + ('e_minalloc', c_uint16), + ('e_maxalloc', c_uint16), + ('e_ss', c_uint16), + ('e_sp', c_uint16), + ('e_csum', c_uint16), + ('e_ip', c_uint16), + ('e_cs', c_uint16), + ('e_lfarlc', c_uint16), + ('e_ovno', c_uint16), + ('e_res', ARRAY(c_uint16, 4)), + ('e_oemid', c_uint16), + ('e_oeminfo', c_uint16), + ('e_res2', ARRAY(c_uint16, 10)), + ('e_lfanew', c_uint16) + ] + +class EFI_IMAGE_FILE_HEADER(Structure): + _fields_ = [ + ('Machine', c_uint16), + ('NumberOfSections', c_uint16), + ('TimeDateStamp', c_uint32), + ('PointerToSymbolTable', c_uint32), + ('NumberOfSymbols', c_uint32), + ('SizeOfOptionalHeader', c_uint16), + ('Characteristics', c_uint16) + ] + +class PE_RELOC_BLOCK_HEADER(Structure): + _fields_ = [ + ('PageRVA', c_uint32), + ('BlockSize', c_uint32) + ] + +class EFI_IMAGE_OPTIONAL_HEADER32(Structure): + _fields_ = [ + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('BaseOfData', c_uint32), + ('ImageBase', c_uint32), + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum' , c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ('SizeOfStackReserve', c_uint32), + ('SizeOfStackCommit' , c_uint32), + ('SizeOfHeapReserve', c_uint32), + ('SizeOfHeapCommit' , c_uint32), + ('LoaderFlags' , c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16)) + ] + +class EFI_IMAGE_OPTIONAL_HEADER32_PLUS(Structure): + _fields_ = [ + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ('ImageBase', c_uint64), + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum' , c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ('SizeOfStackReserve', c_uint64), + ('SizeOfStackCommit' , c_uint64), + ('SizeOfHeapReserve', c_uint64), + ('SizeOfHeapCommit' , c_uint64), + ('LoaderFlags' , c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY, 16)) + ] + +class EFI_IMAGE_OPTIONAL_HEADER(Union): + _fields_ = [ + ('PeOptHdr', EFI_IMAGE_OPTIONAL_HEADER32), + ('PePlusOptHdr', EFI_IMAGE_OPTIONAL_HEADER32_PLUS) + ] + +class EFI_IMAGE_NT_HEADERS32(Structure): + _fields_ = [ + ('Signature', c_uint32), + ('FileHeader', EFI_IMAGE_FILE_HEADER), + ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER) + ] + + +class EFI_IMAGE_DIRECTORY_ENTRY: + EXPORT = 0 + IMPORT = 1 + RESOURCE = 2 + EXCEPTION = 3 + SECURITY = 4 + BASERELOC = 5 + DEBUG = 6 + COPYRIGHT = 7 + GLOBALPTR = 8 + TLS = 9 + LOAD_CONFIG = 10 + +class EFI_FV_FILETYPE: + ALL = 0x00 + RAW = 0x01 + FREEFORM = 0x02 + SECURITY_CORE = 0x03 + PEI_CORE = 0x04 + DXE_CORE = 0x05 + PEIM = 0x06 + DRIVER = 0x07 + COMBINED_PEIM_DRIVER = 0x08 + APPLICATION = 0x09 + SMM = 0x0a + FIRMWARE_VOLUME_IMAGE = 0x0b + COMBINED_SMM_DXE = 0x0c + SMM_CORE = 0x0d + OEM_MIN = 0xc0 + OEM_MAX = 0xdf + DEBUG_MIN = 0xe0 + DEBUG_MAX = 0xef + FFS_MIN = 0xf0 + FFS_MAX = 0xff + FFS_PAD = 0xf0 + +class EFI_SECTION_TYPE: + """Enumeration of all valid firmware file section types.""" + ALL = 0x00 + COMPRESSION = 0x01 + GUID_DEFINED = 0x02 + DISPOSABLE = 0x03 + PE32 = 0x10 + PIC = 0x11 + TE = 0x12 + DXE_DEPEX = 0x13 + VERSION = 0x14 + USER_INTERFACE = 0x15 + COMPATIBILITY16 = 0x16 + FIRMWARE_VOLUME_IMAGE = 0x17 + FREEFORM_SUBTYPE_GUID = 0x18 + RAW = 0x19 + PEI_DEPEX = 0x1b + SMM_DEPEX = 0x1c + +def AlignPtr (offset, alignment = 8): + return (offset + alignment - 1) & ~(alignment - 1) + +def Bytes2Val (bytes): + return reduce(lambda x,y: (x<<8)|y, bytes[::-1] ) + +def Val2Bytes (value, blen): + return [(value>>(i*8) & 0xff) for i in range(blen)] + +def IsIntegerType (val): + if sys.version_info[0] < 3: + if type(val) in (int, long): + return True + else: + if type(val) is int: + return True + return False + +def IsStrType (val): + if sys.version_info[0] < 3: + if type(val) is str: + return True + else: + if type(val) is bytes: + return True + return False + +def HandleNameStr (val): + if sys.version_info[0] < 3: + rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), val) + else: + rep = "0x%X ('%s')" % (Bytes2Val (bytearray (val)), str (val, 'utf-8')) + return rep + +def OutputStruct (obj, indent = 0, plen = 0): + if indent: + body = '' + else: + body = (' ' * indent + '<%s>:\n') % obj.__class__.__name__ + + if plen == 0: + plen = sizeof(obj) + + max_key_len = 26 + pstr = (' ' * (indent + 1) + '{0:<%d} = {1}\n') % max_key_len + + for field in obj._fields_: + key = field[0] + val = getattr(obj, key) + rep = '' + if not isinstance(val, c_uint24) and isinstance(val, Structure): + body += pstr.format(key, val.__class__.__name__) + body += OutputStruct (val, indent + 1) + plen -= sizeof(val) + else: + if IsStrType (val): + rep = HandleNameStr (val) + elif IsIntegerType (val): + rep = '0x%X' % val + elif isinstance(val, c_uint24): + rep = '0x%X' % val.get_value() + elif 'c_ubyte_Array' in str(type(val)): + if sizeof(val) == 16: + if sys.version_info[0] < 3: + rep = str(bytearray(val)) + else: + rep = bytes(val) + rep = str(uuid.UUID(bytes_le = rep)).upper() + else: + res = ['0x%02X'%i for i in bytearray(val)] + rep = '[%s]' % (','.join(res)) + else: + rep = str(val) + plen -= sizeof(field[1]) + body += pstr.format(key, rep) + if plen <= 0: + break + return body + +class Section: + def __init__(self, offset, secdata): + self.SecHdr = EFI_COMMON_SECTION_HEADER.from_buffer (secdata, 0) + self.SecData = secdata[0:int(self.SecHdr.Size)] + self.Offset = offset + +class FirmwareFile: + def __init__(self, offset, filedata): + self.FfsHdr = EFI_FFS_FILE_HEADER.from_buffer (filedata, 0) + self.FfsData = filedata[0:int(self.FfsHdr.Size)] + self.Offset = offset + self.SecList = [] + + def ParseFfs(self): + ffssize = len(self.FfsData) + offset = sizeof(self.FfsHdr) + if self.FfsHdr.Name != '\xff' * 16: + while offset < (ffssize - sizeof (EFI_COMMON_SECTION_HEADER)): + sechdr = EFI_COMMON_SECTION_HEADER.from_buffer (self.FfsData, offset) + sec = Section (offset, self.FfsData[offset:offset + int(sechdr.Size)]) + self.SecList.append(sec) + offset += int(sechdr.Size) + offset = AlignPtr(offset, 4) + +class FirmwareVolume: + def __init__(self, offset, fvdata): + self.FvHdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (fvdata, 0) + self.FvData = fvdata[0 : self.FvHdr.FvLength] + self.Offset = offset + if self.FvHdr.ExtHeaderOffset > 0: + self.FvExtHdr = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer (self.FvData, self.FvHdr.ExtHeaderOffset) + else: + self.FvExtHdr = None + self.FfsList = [] + + def ParseFv(self): + fvsize = len(self.FvData) + if self.FvExtHdr: + offset = self.FvHdr.ExtHeaderOffset + self.FvExtHdr.ExtHeaderSize + else: + offset = self.FvHdr.HeaderLength + offset = AlignPtr(offset) + while offset < (fvsize - sizeof (EFI_FFS_FILE_HEADER)): + ffshdr = EFI_FFS_FILE_HEADER.from_buffer (self.FvData, offset) + if (ffshdr.Name == '\xff' * 16) and (int(ffshdr.Size) == 0xFFFFFF): + offset = fvsize + else: + ffs = FirmwareFile (offset, self.FvData[offset:offset + int(ffshdr.Size)]) + ffs.ParseFfs() + self.FfsList.append(ffs) + offset += int(ffshdr.Size) + offset = AlignPtr(offset) + +class FspImage: + def __init__(self, offset, fih, fihoff, patch): + self.Fih = fih + self.FihOffset = fihoff + self.Offset = offset + self.FvIdxList = [] + self.Type = "XTMSXXXXOXXXXXXX"[(fih.ComponentAttribute >> 12) & 0x0F] + self.PatchList = patch + self.PatchList.append(fihoff + 0x1C) + + def AppendFv(self, FvIdx): + self.FvIdxList.append(FvIdx) + + def Patch(self, delta, fdbin): + count = 0 + applied = 0 + for idx, patch in enumerate(self.PatchList): + ptype = (patch>>24) & 0x0F + if ptype not in [0x00, 0x0F]: + raise Exception('ERROR: Invalid patch type %d !' % ptype) + if patch & 0x80000000: + patch = self.Fih.ImageSize - (0x1000000 - (patch & 0xFFFFFF)) + else: + patch = patch & 0xFFFFFF + if (patch < self.Fih.ImageSize) and (patch + sizeof(c_uint32) <= self.Fih.ImageSize): + offset = patch + self.Offset + value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)]) + value += delta + fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32)) + applied += 1 + count += 1 + # Don't count the FSP base address patch entry appended at the end + if count != 0: + count -= 1 + applied -= 1 + return (count, applied) + +class FirmwareDevice: + def __init__(self, offset, fdfile): + self.FvList = [] + self.FspList = [] + self.FdFile = fdfile + self.Offset = 0 + hfsp = open (self.FdFile, 'rb') + self.FdData = bytearray(hfsp.read()) + hfsp.close() + + def ParseFd(self): + offset = 0 + fdsize = len(self.FdData) + self.FvList = [] + while offset < (fdsize - sizeof (EFI_FIRMWARE_VOLUME_HEADER)): + fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FdData, offset) + if b'_FVH' != fvh.Signature: + raise Exception("ERROR: Invalid FV header !") + fv = FirmwareVolume (offset, self.FdData[offset:offset + fvh.FvLength]) + fv.ParseFv () + self.FvList.append(fv) + offset += fv.FvHdr.FvLength + + def CheckFsp (self): + if len(self.FspList) == 0: + return + + fih = None + for fsp in self.FspList: + if not fih: + fih = fsp.Fih + else: + newfih = fsp.Fih + if (newfih.ImageId != fih.ImageId) or (newfih.ImageRevision != fih.ImageRevision): + raise Exception("ERROR: Inconsistent FSP ImageId or ImageRevision detected !") + + def ParseFsp(self): + flen = 0 + for idx, fv in enumerate(self.FvList): + # Check if this FV contains FSP header + if flen == 0: + if len(fv.FfsList) == 0: + continue + ffs = fv.FfsList[0] + if len(ffs.SecList) == 0: + continue + sec = ffs.SecList[0] + if sec.SecHdr.Type != EFI_SECTION_TYPE.RAW: + continue + fihoffset = ffs.Offset + sec.Offset + sizeof(sec.SecHdr) + fspoffset = fv.Offset + offset = fspoffset + fihoffset + fih = FSP_INFORMATION_HEADER.from_buffer (self.FdData, offset) + if b'FSPH' != fih.Signature: + continue + + offset += fih.HeaderLength + offset = AlignPtr(offset, 4) + plist = [] + while True: + fch = FSP_COMMON_HEADER.from_buffer (self.FdData, offset) + if b'FSPP' != fch.Signature: + offset += fch.HeaderLength + offset = AlignPtr(offset, 4) + else: + fspp = FSP_PATCH_TABLE.from_buffer (self.FdData, offset) + offset += sizeof(fspp) + pdata = (c_uint32 * fspp.PatchEntryNum).from_buffer(self.FdData, offset) + plist = list(pdata) + break + + fsp = FspImage (fspoffset, fih, fihoffset, plist) + fsp.AppendFv (idx) + self.FspList.append(fsp) + flen = fsp.Fih.ImageSize - fv.FvHdr.FvLength + else: + fsp.AppendFv (idx) + flen -= fv.FvHdr.FvLength + if flen < 0: + raise Exception("ERROR: Incorrect FV size in image !") + self.CheckFsp () + +class PeTeImage: + def __init__(self, offset, data): + self.Offset = offset + tehdr = EFI_TE_IMAGE_HEADER.from_buffer (data, 0) + if tehdr.Signature == b'VZ': # TE image + self.TeHdr = tehdr + elif tehdr.Signature == b'MZ': # PE image + self.TeHdr = None + self.DosHdr = EFI_IMAGE_DOS_HEADER.from_buffer (data, 0) + self.PeHdr = EFI_IMAGE_NT_HEADERS32.from_buffer (data, self.DosHdr.e_lfanew) + if self.PeHdr.Signature != 0x4550: + raise Exception("ERROR: Invalid PE32 header !") + if self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b: # PE32 image + if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32.DataDirectory.offset: + raise Exception("ERROR: Unsupported PE32 image !") + if self.PeHdr.OptionalHeader.PeOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC: + raise Exception("ERROR: No relocation information available !") + elif self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x20b: # PE32+ image + if self.PeHdr.FileHeader.SizeOfOptionalHeader < EFI_IMAGE_OPTIONAL_HEADER32_PLUS.DataDirectory.offset: + raise Exception("ERROR: Unsupported PE32+ image !") + if self.PeHdr.OptionalHeader.PePlusOptHdr.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC: + raise Exception("ERROR: No relocation information available !") + else: + raise Exception("ERROR: Invalid PE32 optional header !") + self.Offset = offset + self.Data = data + self.RelocList = [] + + def IsTeImage(self): + return self.TeHdr is not None + + def ParseReloc(self): + if self.IsTeImage(): + rsize = self.TeHdr.DataDirectoryBaseReloc.Size + roffset = sizeof(self.TeHdr) - self.TeHdr.StrippedSize + self.TeHdr.DataDirectoryBaseReloc.VirtualAddress + else: + # Assuming PE32 image type (self.PeHdr.OptionalHeader.PeOptHdr.Magic == 0x10b) + rsize = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size + roffset = self.PeHdr.OptionalHeader.PeOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress + if self.PeHdr.OptionalHeader.PePlusOptHdr.Magic == 0x20b: # PE32+ image + rsize = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].Size + roffset = self.PeHdr.OptionalHeader.PePlusOptHdr.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY.BASERELOC].VirtualAddress + + alignment = 4 + offset = roffset + while offset < roffset + rsize: + offset = AlignPtr(offset, 4) + blkhdr = PE_RELOC_BLOCK_HEADER.from_buffer(self.Data, offset) + offset += sizeof(blkhdr) + # Read relocation type,offset pairs + rlen = blkhdr.BlockSize - sizeof(PE_RELOC_BLOCK_HEADER) + rnum = int (rlen/sizeof(c_uint16)) + rdata = (c_uint16 * rnum).from_buffer(self.Data, offset) + for each in rdata: + roff = each & 0xfff + rtype = each >> 12 + if rtype == 0: # IMAGE_REL_BASED_ABSOLUTE: + continue + if ((rtype != 3) and (rtype != 10)): # IMAGE_REL_BASED_HIGHLOW and IMAGE_REL_BASED_DIR64 + raise Exception("ERROR: Unsupported relocation type %d!" % rtype) + # Calculate the offset of the relocation + aoff = blkhdr.PageRVA + roff + if self.IsTeImage(): + aoff += sizeof(self.TeHdr) - self.TeHdr.StrippedSize + self.RelocList.append((rtype, aoff)) + offset += sizeof(rdata) + + def Rebase(self, delta, fdbin): + count = 0 + if delta == 0: + return count + + for (rtype, roff) in self.RelocList: + if rtype == 3: # IMAGE_REL_BASED_HIGHLOW + offset = roff + self.Offset + value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint32)]) + value += delta + fdbin[offset:offset+sizeof(c_uint32)] = Val2Bytes(value, sizeof(c_uint32)) + count += 1 + elif rtype == 10: # IMAGE_REL_BASED_DIR64 + offset = roff + self.Offset + value = Bytes2Val(fdbin[offset:offset+sizeof(c_uint64)]) + value += delta + fdbin[offset:offset+sizeof(c_uint64)] = Val2Bytes(value, sizeof(c_uint64)) + count += 1 + else: + raise Exception('ERROR: Unknown relocation type %d !' % rtype) + + if self.IsTeImage(): + offset = self.Offset + EFI_TE_IMAGE_HEADER.ImageBase.offset + size = EFI_TE_IMAGE_HEADER.ImageBase.size + else: + offset = self.Offset + self.DosHdr.e_lfanew + offset += EFI_IMAGE_NT_HEADERS32.OptionalHeader.offset + if self.PeHdr.OptionalHeader.PePlusOptHdr.Magic == 0x20b: # PE32+ image + offset += EFI_IMAGE_OPTIONAL_HEADER32_PLUS.ImageBase.offset + size = EFI_IMAGE_OPTIONAL_HEADER32_PLUS.ImageBase.size + else: + offset += EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.offset + size = EFI_IMAGE_OPTIONAL_HEADER32.ImageBase.size + + value = Bytes2Val(fdbin[offset:offset+size]) + delta + fdbin[offset:offset+size] = Val2Bytes(value, size) + + return count + +def ShowFspInfo (fspfile): + fd = FirmwareDevice(0, fspfile) + fd.ParseFd () + fd.ParseFsp () + + print ("\nFound the following %d Firmware Volumes in FSP binary:" % (len(fd.FvList))) + for idx, fv in enumerate(fd.FvList): + name = fv.FvExtHdr.FvName + if not name: + name = '\xff' * 16 + else: + if sys.version_info[0] < 3: + name = str(bytearray(name)) + else: + name = bytes(name) + guid = uuid.UUID(bytes_le = name) + print ("FV%d:" % idx) + print (" GUID : %s" % str(guid).upper()) + print (" Offset : 0x%08X" % fv.Offset) + print (" Length : 0x%08X" % fv.FvHdr.FvLength) + print ("\n") + + for fsp in fd.FspList: + fvlist = map(lambda x : 'FV%d' % x, fsp.FvIdxList) + print ("FSP_%s contains %s" % (fsp.Type, ','.join(fvlist))) + print ("%s" % (OutputStruct(fsp.Fih, 0, fsp.Fih.HeaderLength))) + +def GenFspHdr (fspfile, outdir, hfile): + fd = FirmwareDevice(0, fspfile) + fd.ParseFd () + fd.ParseFsp () + + if not hfile: + hfile = os.path.splitext(os.path.basename(fspfile))[0] + '.h' + fspname, ext = os.path.splitext(os.path.basename(hfile)) + filename = os.path.join(outdir, fspname + ext) + hfsp = open(filename, 'w') + hfsp.write ('%s\n\n' % CopyRightHeaderFile) + + firstfv = True + for fsp in fd.FspList: + fih = fsp.Fih + if firstfv: + if sys.version_info[0] < 3: + hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), fih.ImageId)) + else: + hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (Bytes2Val(bytearray(fih.ImageId)), str (fih.ImageId, 'utf-8'))) + hfsp.write("#define FSP_IMAGE_REV 0x%08X \n\n" % fih.ImageRevision) + firstfv = False + fv = fd.FvList[fsp.FvIdxList[0]] + hfsp.write ('#define FSP%s_BASE 0x%08X\n' % (fsp.Type, fih.ImageBase)) + hfsp.write ('#define FSP%s_OFFSET 0x%08X\n' % (fsp.Type, fv.Offset)) + hfsp.write ('#define FSP%s_LENGTH 0x%08X\n\n' % (fsp.Type, fih.ImageSize)) + + hfsp.close() + +def SplitFspBin (fspfile, outdir, nametemplate): + fd = FirmwareDevice(0, fspfile) + fd.ParseFd () + fd.ParseFsp () + + for fsp in fd.FspList: + if fsp.Fih.HeaderRevision < 3: + raise Exception("ERROR: FSP 1.x is not supported by the split command !") + ftype = fsp.Type + if not nametemplate: + nametemplate = fspfile + fspname, ext = os.path.splitext(os.path.basename(nametemplate)) + filename = os.path.join(outdir, fspname + '_' + fsp.Type + ext) + hfsp = open(filename, 'wb') + print ("Create FSP component file '%s'" % filename) + for fvidx in fsp.FvIdxList: + fv = fd.FvList[fvidx] + hfsp.write(fv.FvData) + hfsp.close() + +def RebaseFspBin (FspBinary, FspComponent, FspBase, OutputDir, OutputFile): + fd = FirmwareDevice(0, FspBinary) + fd.ParseFd () + fd.ParseFsp () + + numcomp = len(FspComponent) + baselist = FspBase + if numcomp != len(baselist): + print ("ERROR: Required number of base does not match number of FSP component !") + return + + newfspbin = fd.FdData[:] + + for idx, fspcomp in enumerate(FspComponent): + + found = False + for fsp in fd.FspList: + # Is this FSP 1.x single binary? + if fsp.Fih.HeaderRevision < 3: + found = True + ftype = 'X' + break + ftype = fsp.Type.lower() + if ftype == fspcomp: + found = True + break + + if not found: + print ("ERROR: Could not find FSP_%c component to rebase !" % fspcomp.upper()) + return + + fspbase = baselist[idx] + if fspbase.startswith('0x'): + newbase = int(fspbase, 16) + else: + newbase = int(fspbase) + oldbase = fsp.Fih.ImageBase + delta = newbase - oldbase + print ("Rebase FSP-%c from 0x%08X to 0x%08X:" % (ftype.upper(),oldbase,newbase)) + + imglist = [] + for fvidx in fsp.FvIdxList: + fv = fd.FvList[fvidx] + for ffs in fv.FfsList: + for sec in ffs.SecList: + if sec.SecHdr.Type in [EFI_SECTION_TYPE.TE, EFI_SECTION_TYPE.PE32]: # TE or PE32 + offset = fd.Offset + fv.Offset + ffs.Offset + sec.Offset + sizeof(sec.SecHdr) + imglist.append ((offset, len(sec.SecData) - sizeof(sec.SecHdr))) + + fcount = 0 + pcount = 0 + for (offset, length) in imglist: + img = PeTeImage(offset, fd.FdData[offset:offset + length]) + img.ParseReloc() + pcount += img.Rebase(delta, newfspbin) + fcount += 1 + + print (" Patched %d entries in %d TE/PE32 images." % (pcount, fcount)) + + (count, applied) = fsp.Patch(delta, newfspbin) + print (" Patched %d entries using FSP patch table." % applied) + if count != applied: + print (" %d invalid entries are ignored !" % (count - applied)) + + if OutputFile == '': + filename = os.path.basename(FspBinary) + base, ext = os.path.splitext(filename) + OutputFile = base + "_%08X" % newbase + ext + + fspname, ext = os.path.splitext(os.path.basename(OutputFile)) + filename = os.path.join(OutputDir, fspname + ext) + fd = open(filename, "wb") + fd.write(newfspbin) + fd.close() + +def main (): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(title='commands', dest="which") + + parser_rebase = subparsers.add_parser('rebase', help='rebase a FSP into a new base address') + parser_rebase.set_defaults(which='rebase') + parser_rebase.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True) + parser_rebase.add_argument('-c', '--fspcomp', choices=['t','m','s','o'], nargs='+', dest='FspComponent', type=str, help='FSP component to rebase', default = "['t']", required = True) + parser_rebase.add_argument('-b', '--newbase', dest='FspBase', nargs='+', type=str, help='Rebased FSP binary file name', default = '', required = True) + parser_rebase.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.') + parser_rebase.add_argument('-n', '--outfile', dest='OutputFile', type=str, help='Rebased FSP binary file name', default = '') + + parser_split = subparsers.add_parser('split', help='split a FSP into multiple components') + parser_split.set_defaults(which='split') + parser_split.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True) + parser_split.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.') + parser_split.add_argument('-n', '--nametpl', dest='NameTemplate', type=str, help='Output name template', default = '') + + parser_genhdr = subparsers.add_parser('genhdr', help='generate a header file for FSP binary') + parser_genhdr.set_defaults(which='genhdr') + parser_genhdr.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True) + parser_genhdr.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.') + parser_genhdr.add_argument('-n', '--hfile', dest='HFileName', type=str, help='Output header file name', default = '') + + parser_info = subparsers.add_parser('info', help='display FSP information') + parser_info.set_defaults(which='info') + parser_info.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True) + + args = parser.parse_args() + if args.which in ['rebase', 'split', 'genhdr', 'info']: + if not os.path.exists(args.FspBinary): + raise Exception ("ERROR: Could not locate FSP binary file '%s' !" % args.FspBinary) + if hasattr(args, 'OutputDir') and not os.path.exists(args.OutputDir): + raise Exception ("ERROR: Invalid output directory '%s' !" % args.OutputDir) + + if args.which == 'rebase': + RebaseFspBin (args.FspBinary, args.FspComponent, args.FspBase, args.OutputDir, args.OutputFile) + elif args.which == 'split': + SplitFspBin (args.FspBinary, args.OutputDir, args.NameTemplate) + elif args.which == 'genhdr': + GenFspHdr (args.FspBinary, args.OutputDir, args.HFileName) + elif args.which == 'info': + ShowFspInfo (args.FspBinary) + else: + parser.print_help() + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspUpd.h b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspUpd.h new file mode 100644 index 00000000..cfc89e48 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspUpd.h @@ -0,0 +1,16 @@ +#ifndef __FSPUPD_H__ +#define __FSPUPD_H__ + +#include <FspEas.h> + +#pragma pack(1) + +#define FSPT_UPD_SIGNATURE 0x545F4450554D4551 /* 'QEMUPD_T' */ + +#define FSPM_UPD_SIGNATURE 0x4D5F4450554D4551 /* 'QEMUPD_M' */ + +#define FSPS_UPD_SIGNATURE 0x535F4450554D4551 /* 'QEMUPD_S' */ + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspmUpd.h b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspmUpd.h new file mode 100644 index 00000000..b88c9be0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspmUpd.h @@ -0,0 +1,75 @@ +#ifndef __FSPMUPD_H__ +#define __FSPMUPD_H__ + +#include <FspUpd.h> + +#pragma pack(1) + + +/** Fsp M Configuration +**/ +typedef struct { + +/** Offset 0x00C8 - Debug Serial Port Base address + Debug serial port base address. This option will be used only when the 'Serial Port + Debug Device' option is set to 'External Device'. 0x00000000(Default). +**/ + UINT32 SerialDebugPortAddress; + +/** Offset 0x00CC - Debug Serial Port Type + 16550 compatible debug serial port resource type. NONE means no serial port support. + 0x02:MMIO(Default). + 0:NONE, 1:I/O, 2:MMIO +**/ + UINT8 SerialDebugPortType; + +/** Offset 0x00CD - Serial Port Debug Device + Select active serial port device for debug.For SOC UART devices,'Debug Serial Port + Base' options will be ignored. 0x02:SOC UART2(Default). + 0:SOC UART0, 1:SOC UART1, 2:SOC UART2, 3:External Device +**/ + UINT8 SerialDebugPortDevice; + +/** Offset 0x00CE - Debug Serial Port Stride Size + Debug serial port register map stride size in bytes. 0x00:1, 0x02:4(Default). + 0:1, 2:4 +**/ + UINT8 SerialDebugPortStrideSize; + +/** Offset 0x00CF +**/ + UINT8 UnusedUpdSpace2[1]; + +/** Offset 0x00D0 +**/ + UINT8 ReservedFspmUpd[4]; +} FSP_M_CONFIG; + +/** Fsp M UPD Configuration +**/ +typedef struct { + +/** Offset 0x0000 +**/ + FSP_UPD_HEADER FspUpdHeader; + +/** Offset 0x00A8 +**/ + FSPM_ARCH_UPD FspmArchUpd; + +/** Offset 0x00C8 +**/ + FSP_M_CONFIG FspmConfig; + +/** Offset 0x00D4 +**/ + UINT8 UnusedUpdSpace3[2]; + +/** Offset 0x00D6 +**/ + UINT16 UpdTerminator; +} FSPM_UPD; + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspsUpd.h b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspsUpd.h new file mode 100644 index 00000000..e859d827 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFspsUpd.h @@ -0,0 +1,69 @@ +#ifndef __FSPSUPD_H__ +#define __FSPSUPD_H__ + +#include <FspUpd.h> + +#pragma pack(1) + + +/** Fsp S Configuration +**/ +typedef struct { + +/** Offset 0x0118 - BMP Logo Data Size + BMP logo data buffer size. 0x00000000(Default). +**/ + UINT32 LogoSize; + +/** Offset 0x011C - BMP Logo Data Pointer + BMP logo data pointer to a BMP format buffer. 0x00000000(Default). +**/ + UINT32 LogoPtr; + +/** Offset 0x0120 - Graphics Configuration Data Pointer + Graphics configuration data used for initialization. 0x00000000(Default). +**/ + UINT32 GraphicsConfigPtr; + +/** Offset 0x0124 - PCI GFX Temporary MMIO Base + PCI Temporary PCI GFX Base used before full PCI enumeration. 0x80000000(Default). +**/ + UINT32 PciTempResourceBase; + +/** Offset 0x0128 +**/ + UINT8 UnusedUpdSpace1[3]; + +/** Offset 0x012B +**/ + UINT8 ReservedFspsUpd; +} FSP_S_CONFIG; + +/** Fsp S UPD Configuration +**/ +typedef struct { + +/** Offset 0x0000 +**/ + FSP_UPD_HEADER FspUpdHeader; + +/** Offset 0x00F8 +**/ + FSPS_ARCH_UPD FspsArchUpd; + +/** Offset 0x0118 +**/ + FSP_S_CONFIG FspsConfig; + +/** Offset 0x012C +**/ + UINT8 UnusedUpdSpace2[2]; + +/** Offset 0x012E +**/ + UINT16 UpdTerminator; +} FSPS_UPD; + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFsptUpd.h b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFsptUpd.h new file mode 100644 index 00000000..979ac324 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedFsptUpd.h @@ -0,0 +1,87 @@ +#ifndef __FSPTUPD_H__ +#define __FSPTUPD_H__ + +#include <FspUpd.h> + +#pragma pack(1) + + +/** Fsp T Common UPD +**/ +typedef struct { + +/** Offset 0x0040 +**/ + UINT8 Revision; + +/** Offset 0x0041 +**/ + UINT8 Reserved[3]; + +/** Offset 0x0044 +**/ + UINT32 MicrocodeRegionBase; + +/** Offset 0x0048 +**/ + UINT32 MicrocodeRegionLength; + +/** Offset 0x004C +**/ + UINT32 CodeRegionBase; + +/** Offset 0x0050 +**/ + UINT32 CodeRegionLength; + +/** Offset 0x0054 +**/ + UINT8 Reserved1[12]; +} FSPT_COMMON_UPD; + +/** Fsp T Configuration +**/ +typedef struct { + +/** Offset 0x0060 - Chicken bytes to test Hex config + This option shows how to present option for 4 bytes data +**/ + UINT32 ChickenBytes; + +/** Offset 0x0064 +**/ + UINT8 ReservedFsptUpd1[28]; +} FSP_T_CONFIG; + +/** Fsp T UPD Configuration +**/ +typedef struct { + +/** Offset 0x0000 +**/ + FSP_UPD_HEADER FspUpdHeader; + +/** Offset 0x0020 +**/ + FSPT_ARCH_UPD FsptArchUpd; + +/** Offset 0x0040 +**/ + FSPT_COMMON_UPD FsptCommonUpd; + +/** Offset 0x0060 +**/ + FSP_T_CONFIG FsptConfig; + +/** Offset 0x0080 +**/ + UINT8 UnusedUpdSpace0[6]; + +/** Offset 0x0086 +**/ + UINT16 UpdTerminator; +} FSPT_UPD; + +#pragma pack() + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.bsf b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.bsf new file mode 100644 index 00000000..c4a041dc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.bsf @@ -0,0 +1,88 @@ +GlobalDataDef
+ SKUID = 0, "DEFAULT"
+EndGlobalData
+
+
+StructDef
+
+ Find "QEMUPD_T"
+ $gQemuFspPkgTokenSpaceGuid_Revision 1 bytes $_DEFAULT_ = 0x01
+ Skip 87 bytes
+ $gQemuFspPkgTokenSpaceGuid_ChickenBytes 4 bytes $_DEFAULT_ = 0x00000000
+
+ Find "QEMUPD_M"
+ $gQemuFspPkgTokenSpaceGuid_Revision 1 bytes $_DEFAULT_ = 0x01
+ Skip 35 bytes
+ $gQemuFspPkgTokenSpaceGuid_StackBase 4 bytes $_DEFAULT_ = 0x00070000
+ $gQemuFspPkgTokenSpaceGuid_StackSize 4 bytes $_DEFAULT_ = 0x00010000
+ $gQemuFspPkgTokenSpaceGuid_BootLoaderTolumSize 4 bytes $_DEFAULT_ = 0x00000000
+ $gPlatformFspPkgTokenSpaceGuid_Bootmode 4 bytes $_DEFAULT_ = 0x00000000
+ Skip 8 bytes
+ $gQemuFspPkgTokenSpaceGuid_SerialDebugPortAddress 4 bytes $_DEFAULT_ = 0x00000000
+ $gQemuFspPkgTokenSpaceGuid_SerialDebugPortType 1 bytes $_DEFAULT_ = 0x02
+ $gQemuFspPkgTokenSpaceGuid_SerialDebugPortDevice 1 bytes $_DEFAULT_ = 0x02
+ $gQemuFspPkgTokenSpaceGuid_SerialDebugPortStrideSize 1 bytes $_DEFAULT_ = 0x02
+
+ Find "QEMUPD_S"
+ $gQemuFspPkgTokenSpaceGuid_Revision 1 bytes $_DEFAULT_ = 0x01
+ Skip 55 bytes
+ $gQemuFspPkgTokenSpaceGuid_LogoSize 4 bytes $_DEFAULT_ = 0x00000000
+ $gQemuFspPkgTokenSpaceGuid_LogoPtr 4 bytes $_DEFAULT_ = 0x00000000
+ $gQemuFspPkgTokenSpaceGuid_GraphicsConfigPtr 4 bytes $_DEFAULT_ = 0x00000000
+ $gQemuFspPkgTokenSpaceGuid_PciTempResourceBase 4 bytes $_DEFAULT_ = 0x80000000
+
+EndStruct
+
+
+List &EN_DIS
+ Selection 0x1 , "Enabled"
+ Selection 0x0 , "Disabled"
+EndList
+
+List &gQemuFspPkgTokenSpaceGuid_SerialDebugPortType
+ Selection 0 , "NONE"
+ Selection 1 , "I/O"
+ Selection 2 , "MMIO"
+EndList
+
+List &gQemuFspPkgTokenSpaceGuid_SerialDebugPortDevice
+ Selection 0 , "SOC UART0"
+ Selection 1 , "SOC UART1"
+ Selection 2 , "SOC UART2"
+ Selection 3 , "External Device"
+EndList
+
+List &gQemuFspPkgTokenSpaceGuid_SerialDebugPortStrideSize
+ Selection 0 , "1"
+ Selection 2 , "4"
+EndList
+
+BeginInfoBlock
+ PPVer "0.1"
+ Description "QEMU Platform"
+EndInfoBlock
+
+Page "FSP T"
+ EditNum $gQemuFspPkgTokenSpaceGuid_ChickenBytes, "Chicken bytes to test Hex config", HEX,
+ Help "This option shows how to present option for 4 bytes data"
+ "Valid range: 0x00000000 ~ 0xFFFFFFFF"
+EndPage
+
+Page "FSP MemoryInit Settings"
+ EditNum $gQemuFspPkgTokenSpaceGuid_SerialDebugPortAddress, "Debug Serial Port Base address", HEX,
+ Help "Debug serial port base address. This option will be used only when the 'Serial Port Debug Device' option is set to 'External Device'. 0x00000000(Default)."
+ "Valid range: 0x00000000 ~ 0xFFFFFFFF"
+ Combo $gQemuFspPkgTokenSpaceGuid_SerialDebugPortType, "Debug Serial Port Type", &gQemuFspPkgTokenSpaceGuid_SerialDebugPortType,
+ Help "16550 compatible debug serial port resource type. NONE means no serial port support. 0x02:MMIO(Default)."
+ Combo $gQemuFspPkgTokenSpaceGuid_SerialDebugPortDevice, "Serial Port Debug Device", &gQemuFspPkgTokenSpaceGuid_SerialDebugPortDevice,
+ Help "Select active serial port device for debug.For SOC UART devices,'Debug Serial Port Base' options will be ignored. 0x02:SOC UART2(Default)."
+ Combo $gQemuFspPkgTokenSpaceGuid_SerialDebugPortStrideSize, "Debug Serial Port Stride Size", &gQemuFspPkgTokenSpaceGuid_SerialDebugPortStrideSize,
+ Help "Debug serial port register map stride size in bytes. 0x00:1, 0x02:4(Default)."
+EndPage
+
+Page "FSP SiliconInit Settings"
+ EditNum $gQemuFspPkgTokenSpaceGuid_PciTempResourceBase, "PCI GFX Temporary MMIO Base", HEX,
+ Help "PCI Temporary PCI GFX Base used before full PCI enumeration. 0x80000000(Default)."
+ "Valid range: 0x80000000 ~ 0xDFFFFFFF"
+EndPage
+
diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.yaml b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.yaml new file mode 100644 index 00000000..db0513f9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/ExpectedOutput.yaml @@ -0,0 +1,267 @@ +variable: + PLATFORM_NAME : QemuFspPkg + PLATFORM_GUID : 1BEDB57A-7904-406e-8486-C89FC7FB39EE + PLATFORM_VERSION : 0.1 + DSC_SPECIFICATION : 0x00010005 + OUTPUT_DIRECTORY : Build/QemuFspPkg + SUPPORTED_ARCHITECTURES : IA32|X64 + BUILD_TARGETS : DEBUG|RELEASE + SKUID_IDENTIFIER : DEFAULT + FLASH_DEFINITION : QemuFspPkg/QemuFspPkg.fdf + FSP_T_UPD_TOOL_GUID : 34686CA3-34F9-4901-B82A-BA630F0714C6 + FSP_V_UPD_TOOL_GUID : 4E2F4725-734A-4399-BAF5-B4E16348EB2F + FSP_M_UPD_TOOL_GUID : 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385 + FSP_S_UPD_TOOL_GUID : CAE3605B-5B34-4C85-B3D7-27D54273C40F + FSP_T_UPD_FFS_GUID : 70BCF6A5-FFB1-47D8-B1AE-EFE5508E23EA + FSP_V_UPD_FFS_GUID : 0197EF5E-2FFC-4089-8E55-F70400B18146 + FSP_M_UPD_FFS_GUID : D5B86AEA-6AF7-40D4-8014-982301BC3D89 + FSP_S_UPD_FFS_GUID : E3CD9B18-998C-4F76-B65E-98B154E5446F + FSP_PACKAGE : QemuFspPkg + FSP_IMAGE_ID : 0x245053464D455124 # $QEMFSP$ + FSP_IMAGE_REV : 0x00001010 + CAR_BASE_ADDRESS : 0x00000000 + CAR_REGION_SIZE : 0x00080000 + CAR_BLD_REGION_SIZE : 0x00070000 + CAR_FSP_REGION_SIZE : 0x00010000 + FSP_ARCH : X64 + + +template: + + +configs: + - $ACTION : + page : TMP::"FSP T", MEM::"FSP MemoryInit Settings", SIL::"FSP SiliconInit Settings" + - $ACTION : + find : QEMUPD_T + - FSPT_UPD : + - FSP_UPD_HEADER : + - Signature : + length : 0x08 + value : 0x545F4450554D4551 + - Revision : + name : FsptUpdRevision + length : 0x01 + value : 0x01 + - Reserved : + length : 0x17 + value : {0x00} + - FSPT_ARCH_UPD : + - Revision : + length : 0x01 + value : 0x01 + - Reserved : + length : 0x03 + value : {0x00} + - Length : + length : 0x04 + value : 0x00000020 + - FspDebugHandler : + length : 0x04 + value : 0x00000000 + - Reserved1 : + length : 0x14 + value : {0x00} + - FSPT_COMMON_UPD : + - Revision : + length : 0x01 + value : 0x01 + - Reserved : + length : 0x03 + value : {0x00} + - MicrocodeRegionBase : + length : 0x04 + value : 0x00000000 + - MicrocodeRegionLength : + length : 0x04 + value : 0x00000000 + - CodeRegionBase : + length : 0x04 + value : 0x00000000 + - CodeRegionLength : + length : 0x04 + value : 0x00000000 + - Reserved1 : + length : 0x0C + value : {0x00} + - FSP_T_CONFIG : + - $ACTION : + page : TMP + - ChickenBytes : + name : Chicken bytes to test Hex config + type : EditNum, HEX, (0x00000000,0xFFFFFFFF) + help : > + This option shows how to present option for 4 bytes data + length : 0x04 + value : 0x00000000 + - ReservedFsptUpd1 : + length : 0x1C + value : {0x00} + - UpdTerminator : + length : 0x02 + value : 0x55AA + - $ACTION : + find : QEMUPD_M + - FSPM_UPD : + - FSP_UPD_HEADER : + - Signature : + length : 0x08 + value : 0x4D5F4450554D4551 + - Revision : + name : FspmUpdRevision + length : 0x01 + value : 0x01 + - Reserved : + length : 0x17 + value : {0x00} + - FSPM_ARCH_UPD : + - Revision : + length : 0x01 + value : 0x01 + - Reserved : + length : 0x03 + value : {0x00} + - NvsBufferPtr : + struct : VOID* + length : 0x04 + value : 0x00000000 + - StackBase : + struct : VOID* + name : StackBase + help : > + Stack base for FSP use. Default- 0xFEF16000 + length : 0x04 + value : $(CAR_BLD_REGION_SIZE) + - StackSize : + name : StackSize + help : > + To pass the stack size for FSP use. Bootloader can programmatically get the FSP requested StackSize by using the defaults in the FSP-M component. This is the minimum stack size expected by this revision of FSP. Default- 0x2A000 + length : 0x04 + value : $(CAR_FSP_REGION_SIZE) + - BootLoaderTolumSize : + name : BootLoaderTolumSize + help : > + To pass Bootloader Tolum size. + length : 0x04 + value : 0x00000000 + - Bootmode : + name : Bootmode + help : > + To maintain Bootmode details. + length : 0x04 + value : 0x00000000 + - Reserved1 : + length : 0x08 + value : {0x00} + - FSP_M_CONFIG : + - $ACTION : + page : MEM + - SerialDebugPortAddress : + name : Debug Serial Port Base address + type : EditNum, HEX, (0x00000000,0xFFFFFFFF) + help : > + Debug serial port base address. This option will be used only when the 'Serial Port Debug Device' + option is set to 'External Device'. 0x00000000(Default). + length : 0x04 + value : 0x00000000 + - SerialDebugPortType : + name : Debug Serial Port Type + type : Combo + option : 0:NONE, 1:I/O, 2:MMIO + help : > + 16550 compatible debug serial port resource type. NONE means no serial port support. 0x02:MMIO(Default). + length : 0x01 + value : 0x02 + - SerialDebugPortDevice : + name : Serial Port Debug Device + type : Combo + option : 0:SOC UART0, 1:SOC UART1, 2:SOC UART2, 3:External Device + help : > + Select active serial port device for debug. + For SOC UART devices,'Debug Serial Port Base' options will be ignored. 0x02:SOC UART2(Default). + length : 0x01 + value : 0x02 + - SerialDebugPortStrideSize : + name : Debug Serial Port Stride Size + type : Combo + option : 0:1, 2:4 + help : > + Debug serial port register map stride size in bytes. 0x00:1, 0x02:4(Default). + length : 0x01 + value : 0x02 + - ReservedFspmUpd : + length : 0x04 + value : {0x00} + - UpdTerminator : + length : 0x02 + value : 0x55AA + - $ACTION : + find : QEMUPD_S + - FSPS_UPD : + - FSP_UPD_HEADER : + - Signature : + length : 0x08 + value : 0x535F4450554D4551 + - Revision : + name : FspsUpdRevision + length : 0x01 + value : 0x01 + - Reserved : + length : 0x17 + value : {0x00} + - FSPS_ARCH_UPD : + - Revision : + length : 0x01 + value : 0x01 + - Reserved : + length : 0x03 + value : {0x00} + - Length : + length : 0x04 + value : 0x00000020 + - FspEventHandler : + length : 0x04 + value : 0x00000000 + - EnableMultiPhaseSiliconInit : + length : 0x01 + value : 0x00 + - Reserved1 : + length : 0x13 + value : {0x00} + - FSP_S_CONFIG : + - $ACTION : + page : SIL + - LogoSize : + name : BMP Logo Data Size + type : Reserved + help : > + BMP logo data buffer size. 0x00000000(Default). + length : 0x04 + value : 0x00000000 + - LogoPtr : + name : BMP Logo Data Pointer + type : Reserved + help : > + BMP logo data pointer to a BMP format buffer. 0x00000000(Default). + length : 0x04 + value : 0x00000000 + - GraphicsConfigPtr : + name : Graphics Configuration Data Pointer + type : Reserved + help : > + Graphics configuration data used for initialization. 0x00000000(Default). + length : 0x04 + value : 0x00000000 + - PciTempResourceBase : + name : PCI GFX Temporary MMIO Base + type : EditNum, HEX, (0x80000000,0xDFFFFFFF) + help : > + PCI Temporary PCI GFX Base used before full PCI enumeration. 0x80000000(Default). + length : 0x04 + value : 0x80000000 + - ReservedFspsUpd : + length : 0x01 + value : 0x00 + - UpdTerminator : + length : 0x02 + value : 0x55AA diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/QemuFspPkg.dsc b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/QemuFspPkg.dsc new file mode 100644 index 00000000..25d51783 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/QemuFspPkg.dsc @@ -0,0 +1,471 @@ +## @file +# FSP DSC build file for QEMU platform +# +# Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR> +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + PLATFORM_NAME = QemuFspPkg + PLATFORM_GUID = 1BEDB57A-7904-406e-8486-C89FC7FB39EE + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/QemuFspPkg + SUPPORTED_ARCHITECTURES = IA32|X64 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + FLASH_DEFINITION = QemuFspPkg/QemuFspPkg.fdf + + # + # UPD tool definition + # + FSP_T_UPD_TOOL_GUID = 34686CA3-34F9-4901-B82A-BA630F0714C6 + FSP_V_UPD_TOOL_GUID = 4E2F4725-734A-4399-BAF5-B4E16348EB2F + FSP_M_UPD_TOOL_GUID = 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385 + FSP_S_UPD_TOOL_GUID = CAE3605B-5B34-4C85-B3D7-27D54273C40F + FSP_T_UPD_FFS_GUID = 70BCF6A5-FFB1-47D8-B1AE-EFE5508E23EA + FSP_V_UPD_FFS_GUID = 0197EF5E-2FFC-4089-8E55-F70400B18146 + FSP_M_UPD_FFS_GUID = D5B86AEA-6AF7-40D4-8014-982301BC3D89 + FSP_S_UPD_FFS_GUID = E3CD9B18-998C-4F76-B65E-98B154E5446F + + # + # Set platform specific package/folder name, same as passed from PREBUILD script. + # PLATFORM_PACKAGE would be the same as PLATFORM_NAME as well as package build folder + # DEFINE only takes effect at R9 DSC and FDF. + # + DEFINE FSP_PACKAGE = QemuFspPkg + DEFINE FSP_IMAGE_ID = 0x245053464D455124 # $QEMFSP$ + DEFINE FSP_IMAGE_REV = 0x00001010 + + DEFINE CAR_BASE_ADDRESS = 0x00000000 + DEFINE CAR_REGION_SIZE = 0x00080000 + DEFINE CAR_BLD_REGION_SIZE = 0x00070000 + DEFINE CAR_FSP_REGION_SIZE = 0x00010000 + + DEFINE FSP_ARCH = X64 + +################################################################################ +# +# SKU Identification section - list of all SKU IDs supported by this +# Platform. +# +################################################################################ +[SkuIds] + 0|DEFAULT # The entry: 0|DEFAULT is reserved and always required. + +################################################################################ +# +# Library Class section - list of all Library Classes needed by this Platform. +# +################################################################################ + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses] + PeiCoreEntryPoint|MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf + PciLib|MdePkg/Library/BasePciLibPciExpress/BasePciLibPciExpress.inf + PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf + PciExpressLib|MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + ReportStatusCodeLib|MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf + CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf + PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf + PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf + UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf + SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf + CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf + ExtractGuidedSectionLib|MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf + CacheLib|IntelFsp2Pkg/Library/BaseCacheLib/BaseCacheLib.inf + CacheAsRamLib|IntelFsp2Pkg/Library/BaseCacheAsRamLibNull/BaseCacheAsRamLibNull.inf + FspSwitchStackLib|IntelFsp2Pkg/Library/BaseFspSwitchStackLib/BaseFspSwitchStackLib.inf + FspCommonLib|IntelFsp2Pkg/Library/BaseFspCommonLib/BaseFspCommonLib.inf + FspPlatformLib|IntelFsp2Pkg/Library/BaseFspPlatformLib/BaseFspPlatformLib.inf + PlatformHookLib|MdeModulePkg/Library/BasePlatformHookLibNull/BasePlatformHookLibNull.inf + PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf + OemHookStatusCodeLib|MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf + UefiCpuLib|UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf +!if $(TARGET) == DEBUG + DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf + SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf +!else + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf +!endif + + +################################################################################ +# +# Pcd Section - list of all EDK II PCD Entries defined by this Platform +# +################################################################################ +[PcdsFixedAtBuild] + gEfiMdeModulePkgTokenSpaceGuid.PcdShadowPeimOnS3Boot | TRUE + gQemuFspPkgTokenSpaceGuid.PcdFspHeaderRevision | 0x03 + gQemuFspPkgTokenSpaceGuid.PcdFspImageIdString | $(FSP_IMAGE_ID) + gQemuFspPkgTokenSpaceGuid.PcdFspImageRevision | $(FSP_IMAGE_REV) + # + # FSP CAR Usages (BL RAM | FSP RAM | FSP CODE) + # + gIntelFsp2PkgTokenSpaceGuid.PcdTemporaryRamBase | $(CAR_BASE_ADDRESS) + gIntelFsp2PkgTokenSpaceGuid.PcdTemporaryRamSize | $(CAR_REGION_SIZE) + gIntelFsp2PkgTokenSpaceGuid.PcdFspTemporaryRamSize | $(CAR_FSP_REGION_SIZE) + gIntelFsp2PkgTokenSpaceGuid.PcdFspReservedBufferSize | 0x0100 + + # This defines how much space will be used for heap in FSP temporary memory + # x % of FSP temporary memory will be used for heap + # (100 - x) % of FSP temporary memory will be used for stack + gIntelFsp2PkgTokenSpaceGuid.PcdFspHeapSizePercentage | 65 + + # This is a platform specific global pointer used by FSP + gIntelFsp2PkgTokenSpaceGuid.PcdGlobalDataPointerAddress | 0xFED00148 + gIntelFsp2PkgTokenSpaceGuid.PcdFspReservedMemoryLength | 0x00100000 + +!if $(TARGET) == RELEASE + gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel | 0x00000000 + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask | 0 +!else + gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel | 0x80000047 + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask | 0x27 +!endif + +[PcdsPatchableInModule] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress | 0xE0000000 + # + # This entry will be patched during the build process + # + gEfiMdeModulePkgTokenSpaceGuid.PcdVpdBaseAddress | 0x12345678 + +!if $(TARGET) == RELEASE + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel | 0 +!else + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel | 0x80000047 +!endif + +[PcdsDynamicVpd.Upd] + # + # This section is not used by the normal build process + # However, FSP will use dedicated tool to handle it and generate a + # VPD similar binary block (User Configuration Data). This block will + # be accessed through a generated data structure directly rather than + # PCD services. This is for size consideration. + # Format: + # gQemuFspPkgTokenSpaceGuid.Updxxxxxxxxxxxxn | OFFSET | LENGTH | VALUE + # Only simple data type is supported + # + + # + # Comments with !BSF will be used to generate BSF file + # Comments with !HDR will be used to generate H header file + # + + # Global definitions in BSF + # !BSF PAGES:{TMP:"FSP T", MEM:"FSP MemoryInit Settings", SIL:"FSP SiliconInit Settings"} + # !BSF BLOCK:{NAME:"QEMU Platform", VER:"0.1"} + + # !BSF FIND:{QEMUPD_T} + # !HDR COMMENT:{FSP_UPD_HEADER:FSP UPD Header} + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:START} + # FsptUpdSignature: {QEMUPD_T} + gQemuFspPkgTokenSpaceGuid.Signature | * | 0x08 | 0x545F4450554D4551 + # !BSF NAME:{FsptUpdRevision} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:END} + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x17 | {0x00} + + # !HDR COMMENT:{FSPT_ARCH_UPD:FSPT_ARCH_UPD} + # !HDR EMBED:{FSPT_ARCH_UPD:FsptArchUpd:START} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x03 | {0x00} + gQemuFspPkgTokenSpaceGuid.Length | * | 0x04 | 0x00000020 + gQemuFspPkgTokenSpaceGuid.FspDebugHandler | * | 0x04 | 0x00000000 + # !HDR EMBED:{FSPT_ARCH_UPD:FsptArchUpd:END} + gQemuFspPkgTokenSpaceGuid.Reserved1 | * | 0x14 | {0x00} + + # !HDR COMMENT:{FSPT_COMMON_UPD:Fsp T Common UPD} + # !HDR EMBED:{FSPT_COMMON_UPD:FsptCommonUpd:START} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x03 | {0x00} + + # Base address of the microcode region. + gQemuFspPkgTokenSpaceGuid.MicrocodeRegionBase | * | 0x04 | 0x00000000 + + # Length of the microcode region. + gQemuFspPkgTokenSpaceGuid.MicrocodeRegionLength | * | 0x04 | 0x00000000 + + # Base address of the cacheable flash region. + gQemuFspPkgTokenSpaceGuid.CodeRegionBase | * | 0x04 | 0x00000000 + + # Length of the cacheable flash region. + gQemuFspPkgTokenSpaceGuid.CodeRegionLength | * | 0x04 | 0x00000000 + + # !HDR EMBED:{FSPT_COMMON_UPD:FsptCommonUpd:END} + gQemuFspPkgTokenSpaceGuid.Reserved1 | * | 0x0C | {0x00} + + # !HDR COMMENT:{FSP_T_CONFIG:Fsp T Configuration} + # !HDR EMBED:{FSP_T_CONFIG:FsptConfig:START} + # !BSF PAGE:{TMP} + # !BSF NAME:{Chicken bytes to test Hex config} + # !BSF TYPE:{EditNum, HEX, (0x00000000,0xFFFFFFFF)} + # !BSF HELP:{This option shows how to present option for 4 bytes data} + gQemuFspPkgTokenSpaceGuid.ChickenBytes | * | 0x04 | 0x00000000 + + # !HDR EMBED:{FSP_T_CONFIG:FsptConfig:END} + gQemuFspPkgTokenSpaceGuid.ReservedFsptUpd1 | * | 0x1C | {0x00} + + # Note please keep "UpdTerminator" at the end of each UPD region. + # The tool will use this field to determine the actual end of the UPD data + # structure. + gQemuFspPkgTokenSpaceGuid.UpdTerminator | * | 0x02 | 0x55AA + + ################################################################################ + # + # UPDs consumed in FspMemoryInit Api + # + ################################################################################ + # !BSF FIND:{QEMUPD_M} + # !HDR COMMENT:{FSP_UPD_HEADER:FSP UPD Header} + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:START} + # FspmUpdSignature: {QEMUPD_M} + gQemuFspPkgTokenSpaceGuid.Signature | * | 0x08 | 0x4D5F4450554D4551 + # !BSF NAME:{FspmUpdRevision} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:END} + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x17 | {0x00} + + # !HDR COMMENT:{FSPM_ARCH_UPD:Fsp M Architectural UPD} + # !HDR EMBED:{FSPM_ARCH_UPD:FspmArchUpd:START} + + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x03 | {0x00} + + # !HDR STRUCT:{VOID*} + gQemuFspPkgTokenSpaceGuid.NvsBufferPtr | * | 0x04 | 0x00000000 + + # !HDR STRUCT:{VOID*} + # !BSF NAME:{StackBase} + # !BSF HELP:{Stack base for FSP use. Default: 0xFEF16000} + gQemuFspPkgTokenSpaceGuid.StackBase | * | 0x04 | $(CAR_BLD_REGION_SIZE) + + # !BSF NAME:{StackSize} + # !BSF HELP:{To pass the stack size for FSP use. Bootloader can programmatically get the FSP requested StackSize by using the defaults in the FSP-M component. This is the minimum stack size expected by this revision of FSP. Default: 0x2A000} + gQemuFspPkgTokenSpaceGuid.StackSize | * | 0x04 | $(CAR_FSP_REGION_SIZE) + + # !BSF NAME:{BootLoaderTolumSize} + # !BSF HELP:{To pass Bootloader Tolum size.} + gQemuFspPkgTokenSpaceGuid.BootLoaderTolumSize | * | 0x04 | 0x00000000 + + # !BSF NAME:{Bootmode} + # !BSF HELP:{To maintain Bootmode details.} + gPlatformFspPkgTokenSpaceGuid.Bootmode | * | 0x04 | 0x00000000 + + # !HDR EMBED:{FSPM_ARCH_UPD:FspmArchUpd:END} + gQemuFspPkgTokenSpaceGuid.Reserved1 | * | 0x08 | {0x00} + + # !HDR COMMENT:{FSP_M_CONFIG:Fsp M Configuration} + # !HDR EMBED:{FSP_M_CONFIG:FspmConfig:START} + # !BSF PAGE:{MEM} + # !BSF NAME:{Debug Serial Port Base address} + # !BSF TYPE:{EditNum, HEX, (0x00000000,0xFFFFFFFF)} + # !BSF HELP:{Debug serial port base address. This option will be used only when the 'Serial Port Debug Device'} + # !BSF HELP:{+ option is set to 'External Device'. 0x00000000(Default).} + gQemuFspPkgTokenSpaceGuid.SerialDebugPortAddress | * | 0x04 | 0x00000000 + + # !BSF NAME:{Debug Serial Port Type} TYPE:{Combo} + # !BSF OPTION:{0:NONE, 1:I/O, 2:MMIO} + # !BSF HELP:{16550 compatible debug serial port resource type. NONE means no serial port support. 0x02:MMIO(Default).} + gQemuFspPkgTokenSpaceGuid.SerialDebugPortType | * | 0x01 | 0x02 + + # !BSF NAME:{Serial Port Debug Device} TYPE:{Combo} + # !BSF OPTION:{0:SOC UART0, 1:SOC UART1, 2:SOC UART2, 3:External Device} + # !BSF HELP:{Select active serial port device for debug.} + # !BSF HELP:{+For SOC UART devices,'Debug Serial Port Base' options will be ignored. 0x02:SOC UART2(Default).} + gQemuFspPkgTokenSpaceGuid.SerialDebugPortDevice | * | 0x01 | 0x02 + + # !BSF NAME:{Debug Serial Port Stride Size} TYPE:{Combo} + # !BSF OPTION:{0:1, 2:4} + # !BSF HELP:{Debug serial port register map stride size in bytes. 0x00:1, 0x02:4(Default).} + gQemuFspPkgTokenSpaceGuid.SerialDebugPortStrideSize | * | 0x01 | 0x02 + + + # !HDR EMBED:{FSP_M_CONFIG:FspmConfig:END} + gQemuFspPkgTokenSpaceGuid.ReservedFspmUpd | * | 0x04 | {0x00} + + + # Note please keep "UpdTerminator" at the end of each UPD region. + # The tool will use this field to determine the actual end of the UPD data + # structure. + gQemuFspPkgTokenSpaceGuid.UpdTerminator | * | 0x02 | 0x55AA + + ################################################################################ + # + # UPDs consumed in FspSiliconInit Api + # + ################################################################################ + # !BSF FIND:{QEMUPD_S} + # !HDR COMMENT:{FSP_UPD_HEADER:FSP UPD Header} + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:START} + # FspsUpdSignature: {QEMUPD_S} + gQemuFspPkgTokenSpaceGuid.Signature | * | 0x08 | 0x535F4450554D4551 + # !BSF NAME:{FspsUpdRevision} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + # !HDR EMBED:{FSP_UPD_HEADER:FspUpdHeader:END} + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x17 | {0x00} + + # !HDR COMMENT:{FSPS_ARCH_UPD:FSPS_ARCH_UPD} + # !HDR EMBED:{FSPS_ARCH_UPD:FspsArchUpd:START} + gQemuFspPkgTokenSpaceGuid.Revision | * | 0x01 | 0x01 + gQemuFspPkgTokenSpaceGuid.Reserved | * | 0x03 | {0x00} + gQemuFspPkgTokenSpaceGuid.Length | * | 0x04 | 0x00000020 + gQemuFspPkgTokenSpaceGuid.FspEventHandler | * | 0x04 | 0x00000000 + gQemuFspPkgTokenSpaceGuid.EnableMultiPhaseSiliconInit | * | 0x01 | 0x00 + # !HDR EMBED:{FSPS_ARCH_UPD:FspsArchUpd:END} + gQemuFspPkgTokenSpaceGuid.Reserved1 | * | 0x13 | {0x00} + + # !HDR COMMENT:{FSP_S_CONFIG:Fsp S Configuration} + # !HDR EMBED:{FSP_S_CONFIG:FspsConfig:START} + # !BSF PAGE:{SIL} + + # !BSF NAME:{BMP Logo Data Size} + # !BSF TYPE:{Reserved} + # !BSF HELP:{BMP logo data buffer size. 0x00000000(Default).} + gQemuFspPkgTokenSpaceGuid.LogoSize | * | 0x04 | 0x00000000 + + # !BSF NAME:{BMP Logo Data Pointer} + # !BSF TYPE:{Reserved} + # !BSF HELP:{BMP logo data pointer to a BMP format buffer. 0x00000000(Default).} + gQemuFspPkgTokenSpaceGuid.LogoPtr | * | 0x04 | 0x00000000 + + # !BSF NAME:{Graphics Configuration Data Pointer} + # !BSF TYPE:{Reserved} + # !BSF HELP:{Graphics configuration data used for initialization. 0x00000000(Default).} + gQemuFspPkgTokenSpaceGuid.GraphicsConfigPtr | * | 0x04 | 0x00000000 + + # !BSF NAME:{PCI GFX Temporary MMIO Base} + # !BSF TYPE:{EditNum, HEX, (0x80000000,0xDFFFFFFF)} + # !BSF HELP:{PCI Temporary PCI GFX Base used before full PCI enumeration. 0x80000000(Default).} + gQemuFspPkgTokenSpaceGuid.PciTempResourceBase | * | 0x04 | 0x80000000 + + # !HDR EMBED:{FSP_S_CONFIG:FspsConfig:END} + gQemuFspPkgTokenSpaceGuid.ReservedFspsUpd | * | 0x01 | 0x00 + + # Note please keep "UpdTerminator" at the end of each UPD region. + # The tool will use this field to determine the actual end of the UPD data + # structure. + gQemuFspPkgTokenSpaceGuid.UpdTerminator | * | 0x02 | 0x55AA + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### +[Components.IA32] + # + # FSP Binary Components + # + $(FSP_PACKAGE)/FspHeader/FspHeader.inf + + # + # SEC + # + IntelFsp2Pkg/FspSecCore/FspSecCoreT.inf { + <LibraryClasses> + FspSecPlatformLib|$(FSP_PACKAGE)/Library/PlatformSecLib/Vtf0PlatformSecTLib.inf + } + +[Components.$(FSP_ARCH)] + IntelFsp2Pkg/FspSecCore/FspSecCoreV.inf { + <LibraryClasses> + FspSecPlatformLib|$(FSP_PACKAGE)/Library/PlatformSecLib/Vtf0PlatformSecVLib.inf + } + + IntelFsp2Pkg/FspSecCore/FspSecCoreM.inf { + <LibraryClasses> + FspSecPlatformLib|$(FSP_PACKAGE)/Library/PlatformSecLib/Vtf0PlatformSecMLib.inf + } + + IntelFsp2Pkg/FspSecCore/FspSecCoreS.inf { + <LibraryClasses> + FspSecPlatformLib|$(FSP_PACKAGE)/Library/PlatformSecLib/Vtf0PlatformSecSLib.inf + } + + # + # PEI Core + # + MdeModulePkg/Core/Pei/PeiMain.inf + + # + # PCD + # + MdeModulePkg/Universal/PCD/Pei/Pcd.inf { + <LibraryClasses> + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + } + + $(FSP_PACKAGE)/FspvInit/FspvInit.inf + $(FSP_PACKAGE)/FspmInit/FspmInit.inf + $(FSP_PACKAGE)/FspsInit/FspsInit.inf + $(FSP_PACKAGE)/QemuVideo/QemuVideo.inf + MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf { + <LibraryClasses> + DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf + ResetSystemLib|MdeModulePkg/Library/BaseResetSystemLibNull/BaseResetSystemLibNull.inf + } + IntelFsp2Pkg/FspNotifyPhase/FspNotifyPhasePeim.inf + +################################################################################################### +# +# BuildOptions Section - Define the module specific tool chain flags that should be used as +# the default flags for a module. These flags are appended to any +# standard flags that are defined by the build process. They can be +# applied for any modules or only those modules with the specific +# module style (EDK or EDKII) specified in [Components] section. +# +################################################################################################### +[BuildOptions] +# Append build options for EDK and EDKII drivers (= is Append, == is Replace) + # Enable link-time optimization when building with GCC49 + *_GCC49_IA32_CC_FLAGS = -flto + *_GCC49_IA32_DLINK_FLAGS = -flto + *_GCC5_IA32_CC_FLAGS = -fno-pic + *_GCC5_IA32_DLINK_FLAGS = -no-pie + *_GCC5_IA32_ASLCC_FLAGS = -fno-pic + *_GCC5_IA32_ASLDLINK_FLAGS = -no-pie diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/test_yaml.py b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/test_yaml.py new file mode 100644 index 00000000..a71073ba --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/Tests/test_yaml.py @@ -0,0 +1,96 @@ +# @file +# Split a file into two pieces at the request offset. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +# Import Modules +import unittest +import tempfile +import os +import shutil +import struct as st +import filecmp + +import os, sys +currentdir = os.path.dirname(os.path.realpath(__file__)) +parentdir = os.path.dirname(currentdir) +sys.path.append(parentdir) +import FspDscBsf2Yaml + +YamlHeaderLineLength = 10 +HdrFileHeaderLineLength = 32 +BsfFileHeaderLineLength = 19 + +def GenFileWithoutHdr(inputfile, numLineToStrip): + yaml_file = open(inputfile, "r") + lines = yaml_file.readlines() + yaml_file.close() + del lines[:numLineToStrip] + + noHdrOutputFileName = "no-header-" + inputfile + stripped_file = open(noHdrOutputFileName, "w") + for line in lines: + stripped_file.write(line) + stripped_file.close() + return noHdrOutputFileName + +class TestFspScripts(unittest.TestCase): + def test_generateFspHeader_fromDsc(self): + # Generate HEADER + cmd = '{} {} HEADER {} {} {}'.format( + 'python', + '..\GenCfgOpt.py', + 'QemuFspPkg.dsc', + '.', + "") + os.system(cmd) + noHdrOutputFileName = GenFileWithoutHdr("FspUpd.h", HdrFileHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedFspUpd.h')) + + def test_generateFspsHeader_fromDsc(self): + noHdrOutputFileName = GenFileWithoutHdr("FspsUpd.h", HdrFileHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedFspsUpd.h')) + + def test_generateFsptHeader_fromDsc(self): + noHdrOutputFileName = GenFileWithoutHdr("FsptUpd.h", HdrFileHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedFsptUpd.h')) + + def test_generateFspmHeader_fromDsc(self): + noHdrOutputFileName = GenFileWithoutHdr("FspmUpd.h", HdrFileHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedFspmUpd.h')) + + def test_generateBsf_fromDsc(self): + # Generate BSF + cmd = '{} {} GENBSF {} {} {}'.format( + 'python', + '..\GenCfgOpt.py', + 'QemuFspPkg.dsc', + '.', + "Output.bsf") + os.system(cmd) + noHdrOutputFileName = GenFileWithoutHdr("Output.bsf", BsfFileHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedOutput.bsf')) + + def test_generateYaml_fromDsc(self): + # Generate YAML + cmd = '{} {} {} {}'.format( + 'python', + '..\FspDscBsf2Yaml.py', + 'QemuFspPkg.dsc', + "Output.yaml") + os.system(cmd) + noHdrOutputFileName = GenFileWithoutHdr("Output.yaml", YamlHeaderLineLength) + self.assertTrue(filecmp.cmp(noHdrOutputFileName, + 'ExpectedOutput.yaml')) + +if __name__ == '__main__': + unittest.main() diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/FspDscBsf2YamlUserManual.md b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/FspDscBsf2YamlUserManual.md new file mode 100644 index 00000000..e739ffc2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/FspDscBsf2YamlUserManual.md @@ -0,0 +1,39 @@ +#Name +**FspDscBsf2Yaml.py** The python script that generates YAML file for +the Boot Settings from an EDK II Platform Description (**DSC**) file +or from a Boot Settings File (**BSF**). It is created to help +transitioning FSP Updateable Product Data (**UPD**) file format to +new standardized YAML format so that it can be configured through +open source tools. + +#Synopsis +``` +FspDscBsf2Yaml DscFile|BsfFile YamlFile +``` + +#Description +**FspDscBsf2Yaml.py** is a script that generates configuration options from an +**EDK II Platform Description (DSC)** file or **a Boot Settings File (BSF)** file. + +It generates a **YAML file** that can be used by the **Config Editor** to provide +a graphical user interface for manipulating settings in the UPD regions. + +The following sections explain the usage of this script. + +## 1. FspDscBsf2Yaml.py DscFile YamlFile + +The **DscFile** option is an input DSC file. + +The **YamlFile** option is an output YAML file. + +The script takes the FSP DSC file consisting BSF syntax and generates a YAML +output file describing the boot settings. + +## 2. FspDscBsf2Yaml.py BsfFile YamlFile + +The **BsfFile** option is an input BSF file. + +The **YamlFile** option is an output YAML file. + +The script generates a YAML output file from a BSF file. The BSF file +can be generated using GenCfgOpt tool. diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/GenCfgOptUserManual.md b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/GenCfgOptUserManual.md new file mode 100644 index 00000000..53a42f94 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/GenCfgOptUserManual.md @@ -0,0 +1,353 @@ +#Name +**GenCfgOpt.py** The python script that generates UPD text (**.txt**) files for +the compiler, header files for the UPD regions, and generates a Boot Settings +File (**BSF**), all from an EDK II Platform Description (**DSC**) file. + +#Synopsis +``` +GenCfgOpt UPDTXT PlatformDscFile BuildFvDir [TxtOutFile] [-D Macros] +GenCfgOpt HEADER PlatformDscFile BuildFvDir [InputHFile] [-D Macros] +GenCfgOpt GENBSF PlatformDscFile BuildFvDir BsfOutFile [-D Macros] +``` + +#Description +**GenCfgOpt.py** is a script that generates configuration options from an +**EDK II Platform Description (DSC)** file. It has three functions. + + 1. It produces a **.txt** file that is used by the compiler that summarizes + the UPD section in the DSC file. + 2. It generates header files for the UPD regions. + 3. It generates a **Boot Settings File (BSF)** that can be used by the + **Binary Configuration Tool (BCT)** to provide a graphical user + interface for manipulating settings in the UPD regions. + +The **GenCfgOpt.py** script generates important files that are vital parts of +your build process. The **UPDTXT** and **HEADER** use cases must be done before +the **'build'** command; the **GENBSF** use case may be done at any time. + +The following sections explain the three use cases. + +## 1. GenCfgOpt.py UPDTXT +The **UPDTXT** option creates a text file with all the UPD entries, offsets, +size in bytes, and values. **GenCfgOpt** reads this information from the +**[PcdsDynamicVpd.Upd]** section of the project's DSC file. The DSC file allows +you to specify offsets and sizes for each entry, opening up the possibility of +introducing gaps between entries. **GenCfgOpt** fills in these gaps with UPD +entries that have the generic names **UnusedUpdSpaceN** where N begins with 0 +and increments. The command signature for **UPDTXT** is: + +``` +GenCfgOpt UPDTXT PlatformDscFile BuildFvDir [TxtOutFile] [-D Macros] +``` + +**PlatformDscFile** must be the location of the DSC file for the platform you're +building. **BuildFvDir** is the location where the binary will be stored. The +optional **TxtOutFile** is a name and location for the output of **GenCfgOpt**. +The default name and location is the ```<UPD_TOOL_GUID>.txt``` in the directory +specified by **BuildFvDir**. The macro ```UPD_TOOL_GUID``` must be defined in +the DSC file or in the optional Macros arguments. Each optional macro argument +must follow the form ```?D <MACRO_NAME>=<VALUE>```. + +**GenCfgOpt** checks to see if the UPD txt file has already been created and +will only re-create it if the DSC was modified after it was created. + +## 2. GenCfgOpt.py HEADER +The **HEADER** option creates header files in the build folder. Both header +files define the ```_UPD_DATA_REGION``` data structures in FspUpd.h, FsptUpd.h, +FspmUpd.h and FspsUpd.h. In these header files any undefined elements of +structures will be added as **ReservedUpdSpaceN** beginning with N=0. The +command signature for **HEADER** is + +```GenCfgOpt HEADER PlatformDscFile BuildFvDir [InputHFile] [-D Macros]``` + +**PlatformDscFile** and **BuildFvDir** are described in the previous section. +The optional **InputHFile** is a header file that may contain data definitions +that are used by variables in the UPD regions. This header file must contain +the special keywords ```!EXPORT EXTERNAL_BOOTLOADER_STRUCT_BEGIN``` and +```!EXPORT EXTERNAL_BOOTLOADER_STRUCT_END``` in comments. Everything between +these two keywords will be included in the generated header file. +The mechanism to specify whether a variable appears as **ReservedUpdSpaceN** in +the FspUpd.h header file is in special commands that appear in the comments of +the DSC file. The special commands begin with ```!HDR```, for header. The +following table summarizes the two command options. + +### HEADER +Use the **HEADER** command to hide specific variables in the public header file. +In your project DSC file, use ```!HDR HEADER:{OFF}``` at the beginning of the +section you wish to hide and ```!HDR HEADER:{ON}``` at the end. + +### STRUCT +The **STRUCT** command allows you to specify a specific data type for a +variable. You can specify a pointer to a data struct, for example. You define +the data structure in the **InputHFile** between +```!EXPORT EXTERNAL_BOOTLOADER_STRUCT_BEGIN``` and +```!EXPORT EXTERNAL_BOOTLOADER_STRUCT_END```. + +#####Example: +```!HDR STRUCT:{MY_DATA_STRUCT*}``` + +You then define ```MY_DATA_STRUCT``` in **InputHFile**. + +### EMBED +The **EMBED** command allows you to put one or more UPD data into a specify data +structure. You can utilize it as a group of UPD for example. You must specify a +start and an end for the specify data structure. + +#####Example: +``` + !HDR EMBED:{MY_DATA_STRUCT:MyDataStructure:START} + gTokenSpaceGuid.Upd1 | 0x0020 | 0x01 | 0x00 + gTokenSpaceGuid.Upd2 | 0x0021 | 0x01 | 0x00 + !HDR EMBED:{MY_DATA_STRUCT:MyDataStructure:END} + gTokenSpaceGuid.UpdN | 0x0022 | 0x01 | 0x00 +``` + +#####Result: +``` + typedef struct { + /** Offset 0x0020 + **/ + UINT8 Upd1; + /** Offset 0x0021 + **/ + UINT8 Upd2; + /** Offset 0x0022 + **/ + UINT8 UpdN; + } MY_DATA_STRUCT; + + typedef struct _UPD_DATA_REGION { + ... + /** Offset 0x0020 + **/ + MY_DATA_STRUCT MyDataStruct; + ... + } UPD_DATA_REGION; +``` + +## 3. GenCfgOpt .py GENBSF +The **GENBSF** option generates a BSF from the UPD entries in a package's DSC +file. It does this by parsing special commands found in the comments of the DSC +file. They roughly match the keywords that define the different sections of the +BSF. + +The command signature for **GENBSF** is + +```GenCfgOpt GENBSF PlatformDscFile BuildFvDir BsfOutFile [-D Macros]``` + +In this case, the **BsfOutFile** parameter is required; it should be the +relative path to where the BSF should be stored. + +Every BSF command in the DSC file begins with **!BSF** or **@Bsf**. The +following table summarizes the options that come after **!BSF** or **@Bsf**: + +# BSF Commands Description +###PAGES +**PAGES** maps abbreviations to friendly-text descriptions of the pages in a BSF. + +#####Example: +```!BSF PAGES:{PG1:?Page 1?, PG2:?Page 2?}``` or + +```@Bsf PAGES:{PG1:?Page 1?, PG2:?Page 2?}``` + +###PAGE +This marks the beginning of a page. Use the abbreviation specified in **PAGES** +command. + +#####Example: +```!BSF PAGE:{PG1}``` or + +```@Bsf PAGE:{PG1}``` + +All the entries that come after this command are assumed to be on that page, +until the next **PAGE** command + +###FIND +FIND maps to the BSF **Find** command. It will be placed in the **StructDef** +region of the BSF and should come at the beginning of the UPD sections of the +DSC, immediately before the signatures that mark the beginning of these +sections. The content should be the plain-text equivalent of the signature. The +signature is usually 8 characters. + +#####Example: +```!BSF FIND:{PROJSIG1}``` or + +```@Bsf FIND:{PROJSIG1}``` + +###BLOCK +The BLOCK command maps to the **BeginInfoBlock** section of the BSF. There are +two elements: a version number and a plain-text description. + +#####Example: +```!BSF BLOCK:{NAME:"My platform name", VER:"0.1"}``` or + +```@Bsf BLOCK:{NAME:"My platform name", VER:"0.1"}``` + +###NAME +**NAME** gives a plain-text for a variable. This is the text label that will +appear next to the control in **BCT**. + +#####Example: +```!BSF NAME:{Variable 0}``` or + +```@Bsf NAME:{Variable 0}``` + +If the **!BSF NAME** or **@Bsf NAME** command does not appear before an entry +in the UPD region of the DSC file, then that entry will not appear in the BSF. + +###TYPE +The **TYPE** command is used either by itself or with the **NAME** command. It +is usually used by itself when defining an **EditNum** field for the BSF. You +specify the type of data in the second parameter and the range of valid values +in the third. + +#####Example: +```!BSF TYPE:{EditNum, HEX, (0x00,0xFF)}``` or + +```@Bsf TYPE:{EditNum, HEX, (0x00,0xFF)}``` + +**TYPE** appears on the same line as the **NAME** command when using a combo-box. + +#####Example: +```!BSF NAME:{Variable 1} TYPE:{Combo}``` or +```@Bsf NAME:{Variable 1} TYPE:{Combo}``` + +There is a special **None** type that puts the variable in the **StructDef** +region of the BSF, but doesn't put it in any **Page** section. This makes the +variable visible to BCT, but not to the end user. + +###HELP +The **HELP** command defines what will appear in the help text for each control +in BCT. + +#####Example: +```!BSF HELP:{Enable/disable LAN controller.}``` or + +```@Bsf HELP:{Enable/disable LAN controller.}``` + +###OPTION +The **OPTION** command allows you to custom-define combo boxes and map integer +or hex values to friendly-text options. + +#####Example: +```!BSF OPTION:{0:IDE, 1:AHCI, 2:RAID}``` + +```!BSF OPTION:{0x00:0 MB, 0x01:32 MB, 0x02:64 MB}``` + +or + +```@Bsf OPTION:{0:IDE, 1:AHCI, 2:RAID}``` + +```@Bsf OPTION:{0x00:0 MB, 0x01:32 MB, 0x02:64 MB}``` + +###FIELD +The **FIELD** command can be used to define a section of a consolidated PCD +such that the PCD will be displayed in several fields via BCT interface instead +of one long entry. + +#####Example: +```!BSF FIELD:{PcdDRAMSpeed:1}``` or + +```@Bsf FIELD:{PcdDRAMSpeed:1}``` + +###ORDER +The **ORDER** command can be used to adjust the display order for the BSF items. +By default the order value for a BSF item is assigned to be the UPD item +```(Offset * 256)```. It can be overridden by declaring **ORDER** command using +format ORDER: ```{HexMajor.HexMinor}```. In this case the order value will be +```(HexMajor*256+HexMinor)```. The item order value will be used as the sort key +during the BSF item display. + +#####Example: +```!BSF ORDER:{0x0040.01}``` or + +```@Bsf ORDER:{0x0040.01}``` + +For **OPTION** and **HELP** commands, it allows to split the contents into +multiple lines by adding multiple **OPTION** and **HELP** command lines. The +lines except for the very first line need to start with **+** in the content to +tell the tool to append this string to the previous one. + +For example, the statement + +```!BSF OPTION:{0x00:0 MB, 0x01:32 MB, 0x02:64 MB}``` + +is equivalent to: + +```!BSF OPTION:{0x00:0 MB, 0x01:32 MB,}``` + +```!BSF OPTION:{+ 0x02:64 MB}``` + +or + +```@Bsf OPTION:{0x00:0 MB, 0x01:32 MB, 0x02:64 MB}``` + +is equivalent to: + +```@Bsf OPTION:{0x00:0 MB, 0x01:32 MB,}``` + +```@Bsf OPTION:{+ 0x02:64 MB}``` + +The **NAME**, **OPTION**, **TYPE**, and **HELP** commands can all appear on the +same line following the **!BSF** or **@Bsf** keyword or they may appear on +separate lines to improve readability. + +There are four alternative ways to replace current BSF commands. +### 1. ```# @Prompt``` +An alternative way replacing **NAME** gives a plain-text for a +variable. This is the text label that will appear next to the control in BCT. + +#####Example: +```# @Prompt Variable 0``` + +The above example can replace the two methods as below. + +```!BSF NAME:{Variable 0}``` or + +```@Bsf NAME:{Variable 0}``` + +If the ```# @Prompt``` command does not appear before an entry in the UPD region +of the DSC file, then that entry will not appear in the BSF. + +### 2. ```##``` +An alternative way replacing **HELP** command defines what will appear in the +help text for each control in BCT. + +#####Example: +```## Enable/disable LAN controller.``` + +The above example can replace the two methods as below. + +```!BSF HELP:{Enable/disable LAN controller.}``` or + +```@Bsf HELP:{Enable/disable LAN controller.}``` + +### 3. ```# @ValidList``` +An alternative way replacing **OPTION** command allows you to custom-define +combo boxes and map integer or hex values to friendly-text options. + +#####Example: +``` # @ValidList 0x80000003 | 0, 1, 2 | IDE, AHCI, RAID + Error Code | Options | Descriptions +``` + +The above example can replace the two methods as below. + +```!BSF OPTION:{0:IDE, 1:AHCI, 2:RAID}``` or + +```@Bsf OPTION:{0:IDE, 1:AHCI, 2:RAID}``` + +### 4. ```# @ValidRange``` +An alternative way replace **EditNum** field for the BSF. + +#####Example: +```# @ValidRange 0x80000001 | 0x0 ? 0xFF + Error Code | Range +``` + +The above example can replace the two methods as below. + +```!BSF TYPE:{EditNum, HEX, (0x00,0xFF)}``` or + +```@Bsf TYPE:{EditNum, HEX, (0x00,0xFF)}``` + diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/PatchFvUserManual.md b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/PatchFvUserManual.md new file mode 100644 index 00000000..f0b10b18 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/PatchFvUserManual.md @@ -0,0 +1,123 @@ +#Name +**_PatchFv.py_** - The python script that patches the firmware volumes (**FV**) +with in the flash device (**FD**) file post FSP build. + +#Synopsis + +``` +PatchFv FvBuildDir [FvFileBaseNames:]FdFileBaseNameToPatch ["Offset, Value"]+ + | ["Offset, Value, @Comment"]+ + | ["Offset, Value, $Command"]+ + | ["Offset, Value, $Command, @Comment"]+ +``` + +#Description +The **_PatchFv.py_** tool allows the developer to fix up FD images to follow the +Intel FSP Architecture specification. It also makes the FD image relocatable. +The tool is written in Python and uses Python 2.7 or later to run. +Consider using the tool in a build script. + +#FvBuildDir (Argument 1) +This is the first argument that **_PatchFv.py_** requires. It is the build +directory for all firmware volumes created during the FSP build. The path must +be either an absolute path or a relevant path, relevant to the top level of the +FSP tree. + +####Example usage: +``` + Build\YouPlatformFspPkg\%BD_TARGET%_%VS_VERSION%%VS_X86%\FV +``` + +The example used contains Windows batch script %VARIABLES%. + +#FvFileBaseNames (Argument 2: Optional Part 1) +The firmware volume file base names (**_FvFileBaseNames_**) are the independent +Fv?s that are to be patched within the FD. (0 or more in the form +**FVFILEBASENAME:**) The colon **:** is used for delimiting the single +argument and must be appended to the end of each (**_FvFileBaseNames_**). + +####Example usage: +``` +STAGE1:STAGE2:MANIFEST:YOURPLATFORM +``` + +In the example **STAGE1** is **STAGE1.Fv** in **YOURPLATFORM.fd**. + +# FdFileNameToPatch (Argument 2: Mandatory Part 2) + +Firmware device file name to patch (**_FdFileNameToPatch_**) is the base name of +the FD file that is to be patched. (1 only, in the form **YOURPLATFORM**) + +####Example usage: +``` +STAGE1:STAGE2:MANIFEST:YOURPLATFORM +``` + +In the example **YOURPLATFORM** is from **_YOURPLATFORM.fd_** + +#"Offset, Value[, Command][, Comment]" (Argument 3) +The **_Offset_** can be a positive or negative number and represents where the +**_Value_** to be patched is located within the FD. The **_Value_** is what +will be written at the given **_Offset_** in the FD. Constants may be used for +both offsets and values. Also, this argument handles expressions for both +offsets and values using these operators: + +``` + = - * & | ~ ( ) [ ] { } < > +``` + +The entire argument includes the quote marks like in the example argument below: + +``` +0xFFFFFFC0, SomeCore:__EntryPoint - [0x000000F0],@SomeCore Entry +``` + +###Constants: + Hexadecimal (use **0x** as prefix) | Decimal + +####Examples: + +| **Positive Hex** | **Negative Hex** | **Positive Decimal** | **Negative Decimal** | +| ---------------: | ---------------: | -------------------: | -------------------: | +| 0x000000BC | 0xFFFFFFA2 | 188 | -94 | + +``` +ModuleName:FunctionName | ModuleName:GlobalVariableName +ModuleGuid:Offset +``` + +###Operators: + +``` + + + Addition + - Subtraction + * Multiplication + & Logical and + | Logical or + ~ Complement + ( ) Evaluation control + [ ] Get a DWord value at the specified offset expression from [expr] + { } Convert an offset {expr} into an absolute address (FSP_BASE + expr) + < > Convert absolute address <expr> into an image offset (expr & FSP_SIZE) + +``` + +###Special Commands: +Special commands must use the **$** symbol as a prefix to the command itself. +There is only one command available at this time. + +``` +$COPY ? Copy a binary block from source to destination. +``` + +####Example: + +``` +0x94, [PlatformInit:__gPcd_BinPatch_FvRecOffset] + 0x94, [0x98], $COPY, @Sync up 2nd FSP Header +``` + +###Comments: +Comments are allowed in the **Offset, Value [, Comment]** argument. Comments +must use the **@** symbol as a prefix. The comment will output to the build +window upon successful completion of patching along with the offset and value data. diff --git a/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/SplitFspBinUserManual.md b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/SplitFspBinUserManual.md new file mode 100644 index 00000000..ebaa6830 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/IntelFsp2Pkg/Tools/UserManuals/SplitFspBinUserManual.md @@ -0,0 +1,72 @@ +# SplitFspBin.py is a python script to support some operations on Intel FSP 1.x/2.x image. + +It supports: + +- Split Intel FSP 2.x image into individual FSP-T/M/S/O component + +- Rebase Intel FSP 1.x/2.x components to different base addresses + +- Generate Intel FSP 1.x/2.x C header file + +- Display Intel FSP 1.x/2.x information header for each FSP component + +## Split Intel FSP 2.x image + +FSP 1.x image is not supported by split command. +To split individual FSP component in Intel FSP 2.x image, the following +command can be used: + + **python SplitFspBin.py split [-h] -f FSPBINARY [-o OUTPUTDIR] [-n NAMETEMPLATE]** + +For example: + + `python SplitFspBin.py split -f FSP.bin` + + It will create FSP_T.bin, FSP_M.bin and FSP_S.bin in current directory. + +## Rebase Intel FSP 1.x/2.x components + +To rebase one or multiple FSP components in Intel FSP 1.x/2.x image, the following +command can be used: + + **python SplitFspBin.py rebase [-h] -f FSPBINARY -c {t,m,s,o} [{t,m,s,o} ...] -b FSPBASE [FSPBASE ...] [-o OUTPUTDIR] [-n OUTPUTFILE]** + +For example: + + `python SplitFspBin.py rebase -f FSP.bin -c t -b 0xFFF00000 -n FSP_new.bin` + + It will rebase FSP-T component inside FSP.bin to new base 0xFFF00000 and save the + rebased Intel FSP 2.x image into file FSP_new.bin. + For FSP 1.x image there is only one component in binary so above command also + works for FSP 1.x image. + + `python SplitFspBin.py rebase -f FSP.bin -c t m -b 0xFFF00000 0xFEF80000 -n FSP_new.bin` + + It will rebase FSP-T and FSP-M components inside FSP.bin to new base 0xFFF00000 + and 0xFEF80000 respectively, and save the rebased Intel FSP 2.x image into file + FSP_new.bin file. + +## Generate Intel FSP 1.x/2.x C header file + +To generate Intel FSP 1.x/2.x C header file, the following command can be used: + + **Python SplitFspBin.py genhdr [-h] -f FSPBINARY [-o OUTPUTDIR] [-n HFILENAME]** + +For example: + + `python SplitFspBin.py genhdr -f FSP.bin -n FSP.h` + + It will create the C header file FSP.h containing the image ID, revision, offset + and size for each individual FSP component. + +## Display Intel FSP 1.x/2.x information header + +To display Intel FSP 1.x/2.x information headers, the following command can be used: + + **Python SplitFspBin.py info [-h] -f FSPBINARY** + +For example: + + `python SplitFspBin.py info -f FSP.bin` + + It will print out the FSP information header for each FSP component. |