## @file # Patch value into the binary file. # # Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # ## # Import Modules # import Common.LongFilePathOs as os from Common.LongFilePathSupport import OpenLongFilePath as open import sys from optparse import OptionParser from optparse import make_option from Common.BuildToolError import * import Common.EdkLogger as EdkLogger from Common.BuildVersion import gBUILD_VERSION import array from Common.DataType import * # Version and Copyright __version_number__ = ("0.10" + " " + gBUILD_VERSION) __version__ = "%prog Version " + __version_number__ __copyright__ = "Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved." ## PatchBinaryFile method # # This method mainly patches the data into binary file. # # @param FileName File path of the binary file # @param ValueOffset Offset value # @param TypeName DataType Name # @param Value Value String # @param MaxSize MaxSize value # # @retval 0 File is updated successfully. # @retval not 0 File is updated failed. # def PatchBinaryFile(FileName, ValueOffset, TypeName, ValueString, MaxSize=0): # # Length of Binary File # FileHandle = open(FileName, 'rb') FileHandle.seek (0, 2) FileLength = FileHandle.tell() FileHandle.close() # # Unify string to upper string # TypeName = TypeName.upper() # # Get PCD value data length # ValueLength = 0 if TypeName == 'BOOLEAN': ValueLength = 1 elif TypeName == TAB_UINT8: ValueLength = 1 elif TypeName == TAB_UINT16: ValueLength = 2 elif TypeName == TAB_UINT32: ValueLength = 4 elif TypeName == TAB_UINT64: ValueLength = 8 elif TypeName == TAB_VOID: if MaxSize == 0: return OPTION_MISSING, "PcdMaxSize is not specified for VOID* type PCD." ValueLength = int(MaxSize) else: return PARAMETER_INVALID, "PCD type %s is not valid." % (CommandOptions.PcdTypeName) # # Check PcdValue is in the input binary file. # if ValueOffset + ValueLength > FileLength: return PARAMETER_INVALID, "PcdOffset + PcdMaxSize(DataType) is larger than the input file size." # # Read binary file into array # FileHandle = open(FileName, 'rb') ByteArray = array.array('B') ByteArray.fromfile(FileHandle, FileLength) FileHandle.close() OrigByteList = ByteArray.tolist() ByteList = ByteArray.tolist() # # Clear the data in file # for Index in range(ValueLength): ByteList[ValueOffset + Index] = 0 # # Patch value into offset # SavedStr = ValueString ValueString = ValueString.upper() ValueNumber = 0 if TypeName == 'BOOLEAN': # # Get PCD value for BOOLEAN data type # try: if ValueString == 'TRUE': ValueNumber = 1 elif ValueString == 'FALSE': ValueNumber = 0 ValueNumber = int (ValueString, 0) if ValueNumber != 0: ValueNumber = 1 except: return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string." % (ValueString) # # Set PCD value into binary data # ByteList[ValueOffset] = ValueNumber elif TypeName in TAB_PCD_CLEAN_NUMERIC_TYPES: # # Get PCD value for UINT* data type # try: ValueNumber = int (ValueString, 0) except: return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string." % (ValueString) # # Set PCD value into binary data # for Index in range(ValueLength): ByteList[ValueOffset + Index] = ValueNumber % 0x100 ValueNumber = ValueNumber // 0x100 elif TypeName == TAB_VOID: ValueString = SavedStr if ValueString.startswith('L"'): # # Patch Unicode String # Index = 0 for ByteString in ValueString[2:-1]: # # Reserve zero as unicode tail # if Index + 2 >= ValueLength: break # # Set string value one by one/ 0x100 # ByteList[ValueOffset + Index] = ord(ByteString) Index = Index + 2 elif ValueString.startswith("{") and ValueString.endswith("}"): # # Patch {0x1, 0x2, ...} byte by byte # ValueList = ValueString[1 : len(ValueString) - 1].split(',') Index = 0 try: for ByteString in ValueList: ByteString = ByteString.strip() if ByteString.upper().startswith('0X'): ByteValue = int(ByteString, 16) else: ByteValue = int(ByteString) ByteList[ValueOffset + Index] = ByteValue % 0x100 Index = Index + 1 if Index >= ValueLength: break except: return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string array." % (ValueString) else: # # Patch ascii string # Index = 0 for ByteString in ValueString[1:-1]: # # Reserve zero as string tail # if Index + 1 >= ValueLength: break # # Set string value one by one # ByteList[ValueOffset + Index] = ord(ByteString) Index = Index + 1 # # Update new data into input file. # if ByteList != OrigByteList: ByteArray = array.array('B') ByteArray.fromlist(ByteList) FileHandle = open(FileName, 'wb') ByteArray.tofile(FileHandle) FileHandle.close() return 0, "Patch Value into File %s successfully." % (FileName) ## Parse command line options # # Using standard Python module optparse to parse command line option of this tool. # # @retval Options A optparse.Values object containing the parsed options # @retval InputFile Path of file to be trimmed # def Options(): OptionList = [ make_option("-f", "--offset", dest="PcdOffset", action="store", type="int", help="Start offset to the image is used to store PCD value."), make_option("-u", "--value", dest="PcdValue", action="store", help="PCD value will be updated into the image."), make_option("-t", "--type", dest="PcdTypeName", action="store", help="The name of PCD data type may be one of VOID*,BOOLEAN, UINT8, UINT16, UINT32, UINT64."), make_option("-s", "--maxsize", dest="PcdMaxSize", action="store", type="int", help="Max size of data buffer is taken by PCD value.It must be set when PCD type is VOID*."), make_option("-v", "--verbose", dest="LogLevel", action="store_const", const=EdkLogger.VERBOSE, help="Run verbosely"), make_option("-d", "--debug", dest="LogLevel", type="int", help="Run with debug information"), make_option("-q", "--quiet", dest="LogLevel", action="store_const", const=EdkLogger.QUIET, help="Run quietly"), make_option("-?", action="help", help="show this help message and exit"), ] # use clearer usage to override default usage message UsageString = "%prog -f Offset -u Value -t Type [-s MaxSize] " Parser = OptionParser(description=__copyright__, version=__version__, option_list=OptionList, usage=UsageString) Parser.set_defaults(LogLevel=EdkLogger.INFO) Options, Args = Parser.parse_args() # error check if len(Args) == 0: EdkLogger.error("PatchPcdValue", PARAMETER_INVALID, ExtraData=Parser.get_usage()) InputFile = Args[len(Args) - 1] return Options, InputFile ## Entrance method # # This method mainly dispatch specific methods per the command line options. # If no error found, return zero value so the caller of this tool can know # if it's executed successfully or not. # # @retval 0 Tool was successful # @retval 1 Tool failed # def Main(): try: # # Check input parameter # EdkLogger.Initialize() CommandOptions, InputFile = Options() if CommandOptions.LogLevel < EdkLogger.DEBUG_9: EdkLogger.SetLevel(CommandOptions.LogLevel + 1) else: EdkLogger.SetLevel(CommandOptions.LogLevel) if not os.path.exists (InputFile): EdkLogger.error("PatchPcdValue", FILE_NOT_FOUND, ExtraData=InputFile) return 1 if CommandOptions.PcdOffset is None or CommandOptions.PcdValue is None or CommandOptions.PcdTypeName is None: EdkLogger.error("PatchPcdValue", OPTION_MISSING, ExtraData="PcdOffset or PcdValue of PcdTypeName is not specified.") return 1 if CommandOptions.PcdTypeName.upper() not in TAB_PCD_NUMERIC_TYPES_VOID: EdkLogger.error("PatchPcdValue", PARAMETER_INVALID, ExtraData="PCD type %s is not valid." % (CommandOptions.PcdTypeName)) return 1 if CommandOptions.PcdTypeName.upper() == TAB_VOID and CommandOptions.PcdMaxSize is None: EdkLogger.error("PatchPcdValue", OPTION_MISSING, ExtraData="PcdMaxSize is not specified for VOID* type PCD.") return 1 # # Patch value into binary image. # ReturnValue, ErrorInfo = PatchBinaryFile (InputFile, CommandOptions.PcdOffset, CommandOptions.PcdTypeName, CommandOptions.PcdValue, CommandOptions.PcdMaxSize) if ReturnValue != 0: EdkLogger.error("PatchPcdValue", ReturnValue, ExtraData=ErrorInfo) return 1 return 0 except: return 1 if __name__ == '__main__': r = Main() sys.exit(r)