Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / Source / Python / PatchPcdValue / PatchPcdValue.py
1 ## @file
2 # Patch value into the binary file.
3 #
4 # Copyright (c) 2010, Intel Corporation
5 # All rights reserved. This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution.  The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
9 #
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 #
13
14 ##
15 # Import Modules
16 #
17 import os
18 import sys
19 import re
20
21 from optparse import OptionParser
22 from optparse import make_option
23 from Common.BuildToolError import *
24 import Common.EdkLogger as EdkLogger
25 import array
26
27 # Version and Copyright
28 __version_number__ = "0.10"
29 __version__ = "%prog Version " + __version_number__
30 __copyright__ = "Copyright (c) 2010, Intel Corporation. All rights reserved."
31
32 ## PatchBinaryFile method
33 #
34 # This method mainly patches the data into binary file.
35
36 # @param FileName    File path of the binary file
37 # @param ValueOffset Offset value 
38 # @param TypeName    DataType Name
39 # @param Value       Value String
40 # @param MaxSize     MaxSize value
41 #
42 # @retval 0     File is updated successfully.
43 # @retval not 0 File is updated failed.
44 #
45 def PatchBinaryFile(FileName, ValueOffset, TypeName, ValueString, MaxSize=0):
46     #
47     # Length of Binary File
48     #
49     FileHandle = open (FileName, 'rb')
50     FileHandle.seek (0, 2)
51     FileLength = FileHandle.tell()
52     FileHandle.close()
53     #
54     # Unify string to upper string
55     #
56     TypeName = TypeName.upper()
57     #
58     # Get PCD value data length
59     #
60     ValueLength = 0
61     if TypeName == 'BOOLEAN':
62         ValueLength = 1
63     elif TypeName == 'UINT8':
64         ValueLength = 1
65     elif TypeName == 'UINT16':
66         ValueLength = 2
67     elif TypeName == 'UINT32':
68         ValueLength = 4
69     elif TypeName == 'UINT64':
70         ValueLength = 8
71     elif TypeName == 'VOID*':
72         if MaxSize == 0:
73             return OPTION_MISSING, "PcdMaxSize is not specified for VOID* type PCD."
74         ValueLength = MaxSize
75     else:
76         return PARAMETER_INVALID,  "PCD type %s is not valid." %(CommandOptions.PcdTypeName)
77     #
78     # Check PcdValue is in the input binary file.
79     #
80     if ValueOffset + ValueLength > FileLength:
81         return PARAMETER_INVALID, "PcdOffset + PcdMaxSize(DataType) is larger than the input file size."
82     #
83     # Read binary file into array
84     #
85     FileHandle = open (FileName, 'rb')
86     ByteArray = array.array('B')
87     ByteArray.fromfile(FileHandle, FileLength)
88     FileHandle.close()
89     OrigByteList = ByteArray.tolist()
90     ByteList = ByteArray.tolist()
91     #
92     # Clear the data in file
93     #
94     for Index in range(ValueLength):
95         ByteList[ValueOffset + Index] = 0
96     #
97     # Patch value into offset
98     #
99     ValueString = ValueString.upper()
100     ValueNumber = 0
101     if TypeName == 'BOOLEAN':
102         #
103         # Get PCD value for BOOLEAN data type
104         #
105         try:
106             if ValueString == 'TRUE':
107                 ValueNumber = 1
108             elif ValueString == 'FALSE':
109                 ValueNumber = 0
110             elif ValueString.startswith('0X'):
111                 ValueNumber = int (Value, 16)
112             else:
113                 ValueNumber = int (Value)
114             if ValueNumber != 0:
115                 ValueNumber = 1
116         except:
117             return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string." %(ValueString)
118         #
119         # Set PCD value into binary data
120         #
121         ByteList[ValueOffset] = ValueNumber
122     elif TypeName in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
123         #
124         # Get PCD value for UINT* data type
125         #
126         try:
127             if ValueString.startswith('0X'):
128                 ValueNumber = int (ValueString, 16)
129             else:
130                 ValueNumber = int (ValueString)
131         except:
132             return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string." %(ValueString)
133         #
134         # Set PCD value into binary data
135         #
136         for Index in range(ValueLength):
137             ByteList[ValueOffset + Index] = ValueNumber % 0x100
138             ValueNumber = ValueNumber / 0x100
139     elif TypeName == 'VOID*':
140         if ValueString.startswith("L "):
141             #
142             # Patch Unicode String
143             #
144             Index = 0
145             for ByteString in ValueString[2:]:
146                 #
147                 # Reserve zero as unicode tail
148                 #
149                 if Index + 2 >= ValueLength:
150                     break
151                 #
152                 # Set string value one by one
153                 #
154                 ByteList[ValueOffset + Index] = ord(ByteString)
155                 Index = Index + 2
156         elif ValueString.startswith("{") and ValueString.endswith("}"):
157             #
158             # Patch {0x1, 0x2, ...} byte by byte
159             #
160             ValueList = ValueString[1 : len(ValueString) - 1].split(', ')
161             Index = 0
162             try:
163                 for ByteString in ValueList:
164                     if ByteString.upper().startswith('0X'):
165                         ByteValue = int(ByteString, 16)
166                     else:
167                         ByteValue = int(ByteString)
168                     ByteList[ValueOffset + Index] = ByteValue % 0x100
169                     Index = Index + 1
170                     if Index >= ValueLength:
171                         break
172             except:
173                 return PARAMETER_INVALID, "PCD Value %s is not valid dec or hex string array." %(ValueString)
174         else:
175             #
176             # Patch ascii string 
177             #
178             Index = 0
179             for ByteString in ValueString:
180                 #
181                 # Reserve zero as string tail
182                 #
183                 if Index + 1 >= ValueLength:
184                     break
185                 #
186                 # Set string value one by one
187                 #
188                 ByteList[ValueOffset + Index] = ord(ByteString)
189                 Index = Index + 1
190     #
191     # Update new data into input file.
192     #
193     if ByteList != OrigByteList:
194         ByteArray = array.array('B')
195         ByteArray.fromlist(ByteList)
196         FileHandle = open (FileName, 'wb')
197         ByteArray.tofile(FileHandle)
198         FileHandle.close()
199     return 0, "Patch Value into File %s successfully." %(FileName)
200
201 ## Parse command line options
202 #
203 # Using standard Python module optparse to parse command line option of this tool.
204 #
205 # @retval Options   A optparse.Values object containing the parsed options
206 # @retval InputFile Path of file to be trimmed
207 #
208 def Options():
209     OptionList = [
210         make_option("-f", "--offset", dest="PcdOffset", action="store", type="int",
211                           help="Start offset to the image is used to store PCD value."),
212         make_option("-u", "--value", dest="PcdValue", action="store",
213                           help="PCD value will be updated into the image."),
214         make_option("-t", "--type", dest="PcdTypeName", action="store",
215                           help="The name of PCD data type may be one of VOID*,BOOLEAN, UINT8, UINT16, UINT32, UINT64."),
216         make_option("-s", "--maxsize", dest="PcdMaxSize", action="store", type="int",
217                           help="Max size of data buffer is taken by PCD value.It must be set when PCD type is VOID*."),
218         make_option("-v", "--verbose", dest="LogLevel", action="store_const", const=EdkLogger.VERBOSE,
219                           help="Run verbosely"),
220         make_option("-d", "--debug", dest="LogLevel", type="int",
221                           help="Run with debug information"),
222         make_option("-q", "--quiet", dest="LogLevel", action="store_const", const=EdkLogger.QUIET,
223                           help="Run quietly"),
224         make_option("-?", action="help", help="show this help message and exit"),
225     ]
226
227     # use clearer usage to override default usage message
228     UsageString = "%prog -f Offset -u Value -t Type [-s MaxSize] <input_file>"
229
230     Parser = OptionParser(description=__copyright__, version=__version__, option_list=OptionList, usage=UsageString)
231     Parser.set_defaults(LogLevel=EdkLogger.INFO)
232
233     Options, Args = Parser.parse_args()
234
235     # error check
236     if len(Args) == 0:
237         EdkLogger.error("PatchPcdValue", PARAMETER_INVALID, ExtraData=Parser.get_usage())
238
239     InputFile = Args[len(Args) - 1]
240     return Options, InputFile
241
242 ## Entrance method
243 #
244 # This method mainly dispatch specific methods per the command line options.
245 # If no error found, return zero value so the caller of this tool can know
246 # if it's executed successfully or not.
247 #
248 # @retval 0     Tool was successful
249 # @retval 1     Tool failed
250 #
251 def Main():
252     try:
253         #
254         # Check input parameter
255         #
256         EdkLogger.Initialize()
257         CommandOptions, InputFile = Options()
258         if CommandOptions.LogLevel < EdkLogger.DEBUG_9:
259             EdkLogger.SetLevel(CommandOptions.LogLevel + 1)
260         else:
261             EdkLogger.SetLevel(CommandOptions.LogLevel)
262         if not os.path.exists (InputFile):
263             EdkLogger.error("PatchPcdValue", FILE_NOT_FOUND, ExtraData=InputFile)
264             return 1
265         if CommandOptions.PcdOffset == None or CommandOptions.PcdValue == None or CommandOptions.PcdTypeName == None:
266             EdkLogger.error("PatchPcdValue", OPTION_MISSING, ExtraData="PcdOffset or PcdValue of PcdTypeName is not specified.")
267             return 1
268         if CommandOptions.PcdTypeName.upper() not in ["BOOLEAN", "UINT8", "UINT16", "UINT32", "UINT64", "VOID*"]:
269             EdkLogger.error("PatchPcdValue", PARAMETER_INVALID, ExtraData="PCD type %s is not valid." %(CommandOptions.PcdTypeName))
270             return 1
271         if CommandOptions.PcdTypeName.upper() == "VOID*" and CommandOptions.PcdMaxSize == None:
272             EdkLogger.error("PatchPcdValue", OPTION_MISSING, ExtraData="PcdMaxSize is not specified for VOID* type PCD.")
273             return 1
274         #
275         # Patch value into binary image.
276         #
277         ReturnValue, ErrorInfo = PatchBinaryFile (InputFile, CommandOptions.PcdOffset, CommandOptions.PcdTypeName, CommandOptions.PcdValue, CommandOptions.PcdMaxSize)
278         if ReturnValue != 0:
279             EdkLogger.error("PatchPcdValue", ReturnValue, ExtraData=ErrorInfo)
280             return 1
281         return 0
282     except:
283         return 1
284
285 if __name__ == '__main__':
286     r = Main()
287     sys.exit(r)