alder_lake_bios/Intel/AlderLake/AlderLakeBoardPkg/Tools/GenFlashmap/GenFlashmap.py

391 lines
17 KiB
Python

#! python3
## @file
# GenFlashmap.py
#
# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
# This software and associated documentation (if any) is furnished
# under a license and may only be used or copied in accordance
# with the terms of the license. Except as permitted by such
# license, no part of this software or documentation may be
# reproduced, stored in a retrieval system, or transmitted in any
# form or by any means without the express written consent of
# Intel Corporation.
#
#
##
# This script supports new flashmap FDF generation by using a template FDF file and a base flashmap FDF file.
#
"""
usage: GenFlashmap.py [optional arguments] [positional arguments]
Flash Map FDF file generation script
positional arguments:
{extended}
extended Command to generate the flashmap supporting Extended BIOS Region
optional arguments:
-h, --help show this help message and exit
-b BASEFLASHMAP, --baseflashmap BASEFLASHMAP
Base FlashMapInclude FDF file path
-t TEMPLATE, --template TEMPLATE
Template FlashMap FDF for Extended BIOS Region file path
-o OUT_FILE, --out_file OUT_FILE
Output FDF file path
"""
import os
import re
import sys
import argparse
from pprint import pprint
__prog__ = sys.argv[0]
__description__ = "Flash Map FDF file generation script"
SIZE_1MB = 0x100000
SIZE_16MB = 0x1000000
SIZE_4GB = 0x100000000
##
# This class has base flashmap resource.
#
# param[in] Base flashmap file path
#
class Basemap ():
def __init__ (self, in_file):
self.file_path = in_file
self.desc = ["#\n# Generated by the base flashmap {}\n#\n\n".format (self.file_path)]
with open (self.file_path, "r") as fdf:
self.__full = fdf.readlines ()
# This list stores whole lines settings PCDs.
self.map_list = self.__full [[self.__full.index (i) for i in self.__full if re.match (r"(?m)^\s*(SET|!)\s*", i)][0]:]
self.map_list = self.desc + self.map_list
# This list stores all flashmap offset PCD names except NVS region.
# Do NOT include size PCD names.
self.fv_offset_list = [
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvPreMemoryOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvSecurityOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvFspTOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvFspMOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvFspSOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvPostMemoryOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvMicrocodeOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashFvFirmwareBinariesOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashFvRsvdOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashIbbOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashIbbROffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvUefiBootOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvOsBootOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashFvOptionalOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashFvAdvancedOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashFvTestMenuOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashFvValidationOffset",
"gBoardModuleTokenSpaceGuid.PcdFlashObbOffset"
]
# This list stores flashmap offset PCD names for NVS region.
self.nvs_offset_list = [
"gMinPlatformPkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashNvStorageVariableOffset",
"gMinPlatformPkgTokenSpaceGuid.PcdFlashNvStorageOffset"
]
# The following variables store settings which are defined by macro.
self.flash_base = "FLASH_BASE"
self.flash_size = "FLASH_SIZE"
self.flash_block_size = "FLASH_BLOCK_SIZE"
self.flash_num_blocks = "FLASH_NUM_BLOCKS"
# This method returns empty region size out of 16MB.
def get_empty_size (self):
if SIZE_4GB - CommonFunc.read (self.flash_base, self.__full) < SIZE_16MB:
size = SIZE_16MB - (SIZE_4GB - CommonFunc.read (self.flash_base, self.__full))
else:
size = 0
return size
#
# This method self-diagnoses the resource in the class.
# The script can not generate a correct flashmap file if this class has some missing settings,
# ending up a build failure around the end of build process. Running the self-diagnosis logic
# on a file generation helps for capturing missing settings at the begining of build process.
# It self-diagnoses by the following steps,
# 1. parsing the list which has all the lines for setting PCDs in a flashmap file,
# and finding the PCD names which suffix "Offset",
# 2. looking for the matching "Offset" PCDs in all PCD list of this class (this_class_list),
# 3. capturing the lines which contain the missing "Offset" PCD, and showing the PCD names.
# If missing PCDs are captured, self-diagnosis method returns False. Otherwise returns True.
# No failure should occur even if all the PCD list in this class support more than the PCDs
# which are actually supported by a flashmap.
#
def self_diagnosis (self):
this_class_list = self.fv_offset_list + self.nvs_offset_list # this list is diagnosed by the logic
result = True
pattern_offset = "(?P<pcd>g.*Guid\.Pcd.*Offset)"
pattern = r"(?m)^\s*SET\s*" + pattern_offset + "\s*=\s*0x(?P<value>[0-9a-fA-F]*).*"
missing_pcd = []
for full in self.map_list:
m_offset_pcd_in_full = re.match (pattern, full)
if m_offset_pcd_in_full:
match = False
for this_class_pcd_list in this_class_list:
if this_class_pcd_list in full:
match = True
break
if not match:
missing_pcd.append (m_offset_pcd_in_full.group ("pcd"))
if missing_pcd:
result = False
error_message = " Found the PCD(s) are not supported by the script : {}\n".format (missing_pcd)
error_message += " Please add the PCD(s) to Basemap class in the script ({})\n".format (__prog__)
error_message += " so the script can help for generating a new flashmap file\n"
error_message += " based on the flashmap ({})".format (self.file_path)
if not result:
print (" Self-diagnosis : {}".format (result))
print (error_message)
else:
result = True
return result
##
# This class has Extended BIOS Region template flashmap resource.
#
# param[in] Extended BIOS Region template flashmap file path
#
class ExtendedBiosRegionmap ():
def __init__ (self, in_file):
self.file_path = in_file
self.desc = ["# Template file {}\n#\n".format (self.file_path)]
with open (self.file_path, "r") as fdf:
self.__full = fdf.readlines ()
# This list stores whole lines which set PCDs.
# self.map_list = self.__full [[self.__full.index (i) for i in self.__full if re.match (r"(?m)^\s*SET\s*", i)][0]:]
self.map_list = self.__full [[self.__full.index (i) for i in self.__full if re.match (r"(?m)^\s*(^DEFINE|#)\s*", i)][0]:]
# The following variables store settings which are defined by macro.
self.flash_base = "FLASH_BASE"
self.flash_size = "FLASH_SIZE"
self.flash_block_size = "FLASH_BLOCK_SIZE"
self.flash_num_blocks = "FLASH_NUM_BLOCKS"
self.extended_region_memmap_address = "EXTENDED_REGION_MEMMAP_ADDRESS"
self.extended_region_size = "EXTENDED_REGION_SIZE"
self.extended_region_in_use = "EXTENDED_REGION_IN_USE"
# This method inserts comments to the empty region out of 16MB region in a flashmap file.
# This purposes just to explicitly show the empty region presence in the auto generated flashmap file.
def insert_comment_to_fill_16mb_region (self, empty_size, in_list):
comment = []
comment.append ("\n")
comment.append ("#===================================================================================#\n")
comment.append ("# #\n")
comment.append ("# Empty {0:.0f} MB to fill up 16MB region 0x{1:08X} #\n".format (empty_size/SIZE_1MB, empty_size))
comment.append ("# #\n")
comment.append ("#===================================================================================#\n")
comment.append("\n")
pattern_nvs = "g.*\.PcdFlashNvStorage.*"
pattern_pcd = r"(?m)^\s*SET\s*" + pattern_nvs + "\s*=\s*0x(?P<value>[0-9a-fA-F]*).*"
end_of_nvs = [in_list.index (i) for i in in_list if re.match (pattern_pcd, i)][-1] + 1
out_list = in_list [:end_of_nvs]
out_list.extend (comment)
out_list.extend (in_list [end_of_nvs + 1:])
return out_list
# This method returns extended region size.
def get_extended_region_size (self):
return CommonFunc.read (self.extended_region_size, self.__full)
##
# Common Functions
#
class CommonFunc ():
def __init__ (self):
pass
##
# This method parses an list which stores whole lines of a flashmap file
# and returns a value found by a setting name.
#
# param[in] setting Setting name. PCD name or macro name
# param[in] in_list List which stores flashmap FDF file lines
# reval return_value Value found by a setting name
#
# Note : This method assumes the flashmap fdf has only one entry of the setting.
#
@classmethod
def read (cls, setting, in_list):
setting_list = []
pattern = r"(?m)^\s*DEFINE\s*" + setting + "\s*=\s*0x(?P<value>[0-9a-fA-F]*).*"
pattern_pcd = r"(?m)^\s*SET\s*" + setting + "\s*=\s*0x(?P<value>[0-9a-fA-F]*).*"
pattern_acm_option = r"(?m)^\s*DEFINE\s*" + setting + "\s*=\s*(?P<value>[0-9]*K).*"
for line in in_list:
m = re.match (pattern, line)
if m:
setting_list.append (int (m.group ("value"), base = 16))
m = re.match (pattern_pcd, line)
if m:
setting_list.append (int (m.group ("value"), base = 16))
m = re.match (pattern_acm_option, line)
if m:
setting_list.append (m.group ("value"))
try:
read_value = setting_list [-1]
except IndexError as err:
print ("\n\n Error!!! {} is not found in {} \n\n".format (setting, self.in_file))
raise
finally:
return read_value
##
# This method writes a list to a file.
#
# param[in] in_list List which is written to an output file
# param[in] template_file Template file path which has contents to be written
# to the top of an output file
# Input None if no template.
# param[in] out_file Output file path
# reval return_value 0 if output file is successfully written
#
@classmethod
def write_fdf (cls, in_list, template_file, out_file):
if os.path.isfile (out_file):
print (" {} has already existed so overwriting ...".format (out_file))
os.remove (out_file)
with open (out_file, "w") as g:
if template_file is not None:
with open (template_file, "r") as t:
template = t.read ()
for rd in template:
g.write (rd)
for wt in in_list:
g.write (wt)
return_code = 0 if os.path.isfile (out_file) else 1
return return_code
##
# This method updates a list.
#
# param[in] original_map_list List to be updated
# param[in] target_setting_list List of setting names which values are to be updated
# param[in] offset_add_value Value which are added to the value of offset field
# param[in] addr_add_value Value which are added to the value of addr field
# reval new_map_list Updated list
#
@classmethod
def update_offset (cls, original_map_list, target_setting_list, offset_add_value, addr_add_value):
new_map_list = []
for line in original_map_list:
for pcd in target_setting_list:
pattern_pcd = r"(?m)^\s*SET\s*" + pcd + "\s*=\s*0x(?P<value>[0-9a-fA-F]*).*\(0x(?P<addr>[0-9a-fA-F]{8})\).*"
m = re.match (pattern_pcd, line)
if m:
new_offset_str = format ((int (m.group ("value"), base = 16) + offset_add_value), "08X")
line = re.sub (r"=\s*0x[0-9a-fA-F]{8}", "= 0x" + new_offset_str, line)
new_addr_str = format ((int (m.group ("addr"), base = 16) + addr_add_value), "08X")
line = re.sub (r"\(*0x[0-9a-fA-F]{8}\)", "(0x" + new_addr_str + ")", line)
new_map_list.append (line)
return new_map_list
def main ():
def is_valid_file_path (Argument):
if os.path.isfile (Argument):
return Argument
else:
message = "[" + Argument + "] is not found"
raise argparse.ArgumentTypeError (message)
def is_valid_32bit_all (Argument):
try:
value = int (Argument, base = 16)
except:
if Argument == "all":
return Argument
else:
message = "[" + Argument + "] is not a valid 32bit number"
raise argparse.ArgumentTypeError (message)
if value < 0 or value > 0xffffffff:
message = "[" + Argument + "] is not a valid 32bit number"
raise argparse.ArgumentTypeError (message)
return Argument
##
# Command to generates Extended BIOS Region flashmap file
#
# This method reads the offset addresses from a default flashmap FDF file,
# adjusts the offset addresses based on the macro settings defined by the
# template FDF for Extended BIOS Region, and writes the adjusted flashmap
# settings to a new Extended BIOS Region flashmap FDF file.
#
def gen_extended_bios_region_flashmap (Argument):
print ("\nGenerating FlashMap FDF supporting Extended BIOS Region ... \n")
if args.baseflashmap is None:
print ("Error!!! baseflashmap option is not set")
return 1
if args.template is None:
print ("Error!!! template option is not set")
return 1
if args.out_file is None:
print ("Error!!! out_file option is not set")
return 1
# initialize
bm = Basemap (args.baseflashmap)
if not bm.self_diagnosis (): return 1
em = ExtendedBiosRegionmap (args.template)
# update
in_list = bm.map_list
if bm.get_empty_size () != 0:
in_list = em.insert_comment_to_fill_16mb_region (bm.get_empty_size (), in_list)
out_list1 = CommonFunc.update_offset (in_list, bm.nvs_offset_list, 0, (-1) * bm.get_empty_size ())
pprint (out_list1)
out_list2 = CommonFunc.update_offset (out_list1, bm.nvs_offset_list, em.get_extended_region_size (), 0)
out_list3 = CommonFunc.update_offset (out_list2, bm.fv_offset_list, bm.get_empty_size (), 0)
out_list4 = CommonFunc.update_offset (out_list3, bm.fv_offset_list, em.get_extended_region_size (), 0)
out_listn = out_list4
# write to a new flashmap
return_value = CommonFunc.write_fdf (out_listn, args.template, args.out_file)
if return_value == 0:
print (" Flashmap fdf generation has been completed successfully".format (args.out_file))
print (" Base flashmap fdf : {}".format (args.baseflashmap))
print (" Generated flashmap fdf : {}".format (args.out_file))
return return_value
#
# Command options
#
parser = argparse.ArgumentParser (prog = __prog__, description =__description__, usage = "%(prog)s [optional arguments] [positional arguments]")
parser.add_argument ("-b", "--baseflashmap", type = is_valid_file_path, help = "Base FlashMapInclude FDF file path")
parser.add_argument ("-t", "--template", type = is_valid_file_path, help = "Template FlashMap FDF for Extended BIOS Region file path")
parser.add_argument ("-o", "--out_file", help = "Output FDF file path")
subparsers = parser.add_subparsers ()
parser_test = subparsers.add_parser ("extended", help = "Command to generate the flashmap supporting Extended BIOS Region")
parser_test.set_defaults (handler = gen_extended_bios_region_flashmap)
args = parser.parse_args ()
returncode = args.handler (args) if hasattr (args, "handler") else 1
return returncode
if __name__ == "__main__":
sys.exit (main())