## @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)