]>
Commit | Line | Data |
---|---|---|
cf1d4549 JY |
1 | ## @ FspTool.py\r |
2 | #\r | |
3 | # Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r | |
4 | # This program and the accompanying materials are licensed and made available under\r | |
5 | # the terms and conditions of the BSD License that accompanies this distribution.\r | |
6 | # The full text of the license may be found at\r | |
7 | # http://opensource.org/licenses/bsd-license.php.\r | |
8 | #\r | |
9 | # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
10 | # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
11 | #\r | |
12 | ##\r | |
13 | \r | |
14 | import os\r | |
15 | import sys\r | |
16 | import uuid\r | |
17 | import copy\r | |
18 | import struct\r | |
19 | import argparse\r | |
20 | from ctypes import *\r | |
21 | \r | |
22 | """\r | |
23 | This utility supports some operations for Intel FSP image.\r | |
24 | It supports:\r | |
25 | - Split a FSP 2.0 compatibale image into individual FSP-T/M/S/C\r | |
26 | and generate the mapping header file.\r | |
27 | """\r | |
28 | \r | |
29 | class c_uint24(Structure):\r | |
30 | """Little-Endian 24-bit Unsigned Integer"""\r | |
31 | _pack_ = 1\r | |
32 | _fields_ = [\r | |
33 | ('Data', (c_uint8 * 3))\r | |
34 | ]\r | |
35 | \r | |
36 | def __init__(self, val=0):\r | |
37 | self.set_value(val)\r | |
38 | \r | |
39 | def __str__(self, indent=0):\r | |
40 | return '0x%.6x' % self.value\r | |
41 | \r | |
42 | def get_value(self):\r | |
43 | return (\r | |
44 | (self.Data[0] ) +\r | |
45 | (self.Data[1] << 8) +\r | |
46 | (self.Data[2] << 16)\r | |
47 | )\r | |
48 | \r | |
49 | def set_value(self, val):\r | |
50 | self.Data[0] = (val ) & 0xff\r | |
51 | self.Data[1] = (val >> 8) & 0xff\r | |
52 | self.Data[2] = (val >> 16) & 0xff\r | |
53 | \r | |
54 | value = property(get_value, set_value)\r | |
55 | \r | |
56 | class EFI_FIRMWARE_VOLUME_HEADER(Structure):\r | |
57 | _fields_ = [\r | |
58 | ('ZeroVector', ARRAY(c_uint8, 16)),\r | |
59 | ('FileSystemGuid', ARRAY(c_char, 16)),\r | |
60 | ('FvLength', c_uint64),\r | |
61 | ('Signature', c_uint32),\r | |
62 | ('Attributes', c_uint32),\r | |
63 | ('HeaderLength', c_uint16),\r | |
64 | ('Checksum', c_uint16),\r | |
65 | ('ExtHeaderOffset', c_uint16),\r | |
66 | ('Reserved', c_uint8),\r | |
67 | ('Revision', c_uint8)\r | |
68 | ]\r | |
69 | \r | |
70 | class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):\r | |
71 | _fields_ = [\r | |
72 | ('FvName', ARRAY(c_char, 16)),\r | |
73 | ('ExtHeaderSize', c_uint32)\r | |
74 | ]\r | |
75 | \r | |
76 | class EFI_FFS_INTEGRITY_CHECK(Structure):\r | |
77 | _fields_ = [\r | |
78 | ('Header', c_uint8),\r | |
79 | ('File', c_uint8)\r | |
80 | ]\r | |
81 | \r | |
82 | class EFI_FFS_FILE_HEADER(Structure):\r | |
83 | _fields_ = [\r | |
84 | ('Name', ARRAY(c_char, 16)),\r | |
85 | ('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),\r | |
86 | ('Type', c_uint8),\r | |
87 | ('Attributes', c_uint8),\r | |
88 | ('Size', c_uint24),\r | |
89 | ('State', c_uint8)\r | |
90 | ]\r | |
91 | \r | |
92 | class EFI_COMMON_SECTION_HEADER(Structure):\r | |
93 | _fields_ = [\r | |
94 | ('Size', c_uint24),\r | |
95 | ('Type', c_uint8)\r | |
96 | ]\r | |
97 | \r | |
98 | class FSP_INFORMATION_HEADER(Structure):\r | |
99 | _fields_ = [\r | |
100 | ('Signature', c_uint32),\r | |
101 | ('HeaderLength', c_uint32),\r | |
102 | ('Reserved1', ARRAY(c_uint8, 3)),\r | |
103 | ('HeaderRevision', c_uint8),\r | |
104 | ('ImageRevision', c_uint32),\r | |
105 | ('ImageId', c_uint64),\r | |
106 | ('ImageSize', c_uint32),\r | |
107 | ('ImageBase', c_uint32),\r | |
108 | ('ImageAttribute', c_uint32),\r | |
109 | ('CfgRegionOffset', c_uint32),\r | |
110 | ('CfgRegionSize', c_uint32),\r | |
111 | ('ApiEntryNum', c_uint32),\r | |
112 | ('NemInitEntry', c_uint32),\r | |
113 | ('FspInitEntry', c_uint32),\r | |
114 | ('NotifyPhaseEntry', c_uint32),\r | |
115 | ('FspMemoryInitEntry', c_uint32),\r | |
116 | ('TempRamExitEntry', c_uint32),\r | |
117 | ('FspSiliconInitEntry', c_uint32)\r | |
118 | ]\r | |
119 | \r | |
120 | class FspFv:\r | |
121 | HeaderFile = """/*\r | |
122 | *\r | |
123 | * Automatically generated file; DO NOT EDIT.\r | |
124 | * FSP mapping file\r | |
125 | *\r | |
126 | */\r | |
127 | """\r | |
128 | FspNameDict = {\r | |
129 | "0" : "-C.Fv",\r | |
130 | "1" : "-T.Fv",\r | |
131 | "2" : "-M.Fv",\r | |
132 | "3" : "-S.Fv",\r | |
133 | }\r | |
134 | \r | |
135 | def __init__(self, FvBin):\r | |
136 | self.FspFv = {}\r | |
137 | self.FvList = []\r | |
138 | self.FspBin = FvBin\r | |
139 | hfsp = open (self.FspBin, 'r+b')\r | |
140 | self.FspDat = bytearray(hfsp.read())\r | |
141 | hfsp.close()\r | |
142 | \r | |
143 | def OutputStruct (self, obj, indent = 0):\r | |
144 | max_key_len = 20\r | |
145 | pstr = (' ' * indent + '{0:<%d} = {1}\n') % max_key_len\r | |
146 | if indent:\r | |
147 | s = ''\r | |
148 | else:\r | |
149 | s = (' ' * indent + '<%s>:\n') % obj.__class__.__name__\r | |
150 | for field in obj._fields_:\r | |
151 | key = field[0]\r | |
152 | val = getattr(obj, key)\r | |
153 | rep = ''\r | |
154 | \r | |
155 | if not isinstance(val, c_uint24) and isinstance(val, Structure): \r | |
156 | s += pstr.format(key, val.__class__.__name__)\r | |
157 | s += self.OutputStruct (val, indent + 1) \r | |
158 | else:\r | |
159 | if type(val) in (int, long):\r | |
160 | rep = hex(val)\r | |
161 | elif isinstance(val, str) and (len(val) == 16):\r | |
162 | rep = str(uuid.UUID(bytes = val))\r | |
163 | elif isinstance(val, c_uint24): \r | |
164 | rep = hex(val.get_value())\r | |
165 | elif 'c_ubyte_Array' in str(type(val)): \r | |
166 | rep = str(list(bytearray(val)))\r | |
167 | else:\r | |
168 | rep = str(val)\r | |
169 | s += pstr.format(key, rep)\r | |
170 | return s\r | |
171 | \r | |
172 | def PrintFv (self):\r | |
173 | print ("FV LIST:")\r | |
174 | idx = 0\r | |
175 | for (fvh, fvhe, offset) in self.FvList: \r | |
176 | guid = uuid.UUID(bytes = fvhe.FvName)\r | |
177 | print ("FV%d FV GUID:%s Offset:0x%08X Length:0x%08X" % (idx, str(guid), offset, fvh.FvLength))\r | |
178 | idx = idx + 1\r | |
179 | print ("\nFSP LIST:")\r | |
180 | for fsp in self.FspFv:\r | |
181 | print "FSP%s contains FV%s" % (fsp, str(self.FspFv[fsp][1]))\r | |
182 | print "\nFSP%s Info Header:" % fsp\r | |
183 | fih = self.FspFv[fsp][0] \r | |
184 | \r | |
185 | def AlaignPtr (self, offset, alignment = 8):\r | |
186 | return (offset + alignment - 1) & ~(alignment - 1)\r | |
187 | \r | |
188 | def GetFspInfoHdr (self, fvh, fvhe, fvoffset):\r | |
189 | if fvhe:\r | |
190 | offset = fvh.ExtHeaderOffset + fvhe.ExtHeaderSize\r | |
191 | else:\r | |
192 | offset = fvh.HeaderLength\r | |
193 | offset = self.AlaignPtr(offset)\r | |
194 | \r | |
195 | # Now it should be 1st FFS\r | |
196 | ffs = EFI_FFS_FILE_HEADER.from_buffer (self.FspDat, offset) \r | |
197 | offset += sizeof(ffs)\r | |
198 | offset = self.AlaignPtr(offset)\r | |
199 | \r | |
200 | # Now it should be 1st Section\r | |
201 | sec = EFI_COMMON_SECTION_HEADER.from_buffer (self.FspDat, offset)\r | |
202 | offset += sizeof(sec)\r | |
203 | \r | |
204 | # Now it should be FSP_INFO_HEADER\r | |
205 | offset += fvoffset\r | |
206 | fih = FSP_INFORMATION_HEADER.from_buffer (self.FspDat, offset)\r | |
207 | if 'FSPH' != bytearray.fromhex('%08X' % fih.Signature)[::-1]:\r | |
208 | return None\r | |
209 | \r | |
210 | return fih\r | |
211 | \r | |
212 | def GetFvHdr (self, offset):\r | |
213 | fvh = EFI_FIRMWARE_VOLUME_HEADER.from_buffer (self.FspDat, offset)\r | |
214 | if '_FVH' != bytearray.fromhex('%08X' % fvh.Signature)[::-1]:\r | |
215 | return None, None\r | |
216 | if fvh.ExtHeaderOffset > 0:\r | |
217 | offset += fvh.ExtHeaderOffset\r | |
218 | fvhe = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer (self.FspDat, offset)\r | |
219 | else:\r | |
220 | fvhe = None\r | |
221 | return fvh, fvhe\r | |
222 | \r | |
223 | def GetFvData(self, idx):\r | |
224 | (fvh, fvhe, offset) = self.FvList[idx]\r | |
225 | return self.FspDat[offset:offset+fvh.FvLength]\r | |
226 | \r | |
227 | def CheckFsp (self):\r | |
228 | if len(self.FspFv) == 0:\r | |
229 | return\r | |
230 | \r | |
231 | fih = None\r | |
232 | for fv in self.FspFv:\r | |
233 | if not fih:\r | |
234 | fih = self.FspFv[fv][0]\r | |
235 | else:\r | |
236 | newfih = self.FspFv[fv][0]\r | |
237 | if (newfih.ImageId != fih.ImageId) or (newfih.ImageRevision != fih.ImageRevision):\r | |
238 | raise Exception("Inconsistent FSP ImageId or ImageRevision detected !")\r | |
239 | return\r | |
240 | \r | |
241 | def WriteFsp(self, dir, name):\r | |
242 | if not name:\r | |
243 | name = self.FspBin\r | |
244 | fspname, ext = os.path.splitext(os.path.basename(name))\r | |
245 | for fv in self.FspFv:\r | |
246 | filename = os.path.join(dir, fspname + fv + ext)\r | |
247 | hfsp = open(filename, 'w+b')\r | |
248 | for fvidx in self.FspFv[fv][1]:\r | |
249 | hfsp.write (self.GetFvData(fvidx))\r | |
250 | hfsp.close()\r | |
251 | \r | |
252 | def WriteMap(self, dir, hfile):\r | |
253 | if not hfile:\r | |
254 | hfile = os.path.splitext(os.path.basename(self.FspBin))[0] + '.h'\r | |
255 | fspname, ext = os.path.splitext(os.path.basename(hfile))\r | |
256 | filename = os.path.join(dir, fspname + ext)\r | |
257 | hfsp = open(filename, 'w')\r | |
258 | hfsp.write ('%s\n\n' % self.HeaderFile)\r | |
259 | \r | |
260 | firstfv = True\r | |
261 | for fsp in self.FspFv:\r | |
262 | fih = self.FspFv[fsp][0]\r | |
263 | fvs = self.FspFv[fsp][1]\r | |
264 | if firstfv:\r | |
265 | IdStr = str(bytearray.fromhex('%016X' % fih.ImageId)[::-1])\r | |
266 | hfsp.write("#define FSP_IMAGE_ID 0x%016X /* '%s' */\n" % (fih.ImageId, IdStr))\r | |
267 | hfsp.write("#define FSP_IMAGE_REV 0x%08X \n\n" % fih.ImageRevision)\r | |
268 | firstfv = False\r | |
269 | hfsp.write ('#define FSP%s_BASE 0x%08X\n' % (fsp, fih.ImageBase))\r | |
270 | hfsp.write ('#define FSP%s_OFFSET 0x%08X\n' % (fsp, self.FvList[fvs[0]][-1]))\r | |
271 | hfsp.write ('#define FSP%s_LENGTH 0x%08X\n\n' % (fsp, fih.ImageSize))\r | |
272 | hfsp.close()\r | |
273 | \r | |
274 | def ParseFsp (self):\r | |
275 | self.FspFv = {}\r | |
276 | flen = 0\r | |
277 | for (fvh, fvhe, offset) in self.FvList:\r | |
278 | fih = self.GetFspInfoHdr (fvh, fvhe, offset)\r | |
279 | if fih:\r | |
280 | if flen != 0:\r | |
281 | raise Exception("Incorrect FV size in image !")\r | |
282 | ftype = str((fih.ImageAttribute >> 28) & 0xF)\r | |
283 | if ftype not in self.FspNameDict:\r | |
284 | raise Exception("Unknown Attribute in image !")\r | |
285 | fname = self.FspNameDict[str(ftype)]\r | |
286 | if fname in self.FspFv:\r | |
287 | raise Exception("Multiple '%s' in image !" % fname)\r | |
288 | self.FspFv[fname] = (copy.deepcopy(fih), [])\r | |
289 | flen = fih.ImageSize\r | |
290 | if flen > 0:\r | |
291 | flen = flen - fvh.FvLength\r | |
292 | if flen < 0:\r | |
293 | raise Exception("Incorrect FV size in image !")\r | |
294 | self.FspFv[fname][1].append(self.FvList.index((fvh, fvhe, offset)))\r | |
295 | \r | |
296 | def AddFv(self, offset):\r | |
297 | fvh, fvhe = self.GetFvHdr (offset)\r | |
298 | if fvh is None:\r | |
299 | raise Exception('FV signature is not valid !')\r | |
300 | fvitem = (copy.deepcopy(fvh), copy.deepcopy(fvhe), offset)\r | |
301 | self.FvList.append(fvitem)\r | |
302 | return fvh.FvLength\r | |
303 | \r | |
304 | def ParseFv(self):\r | |
305 | offset = 0\r | |
306 | while (offset < len(self.FspDat)):\r | |
307 | fv_len = self.AddFv (offset)\r | |
308 | offset += fv_len\r | |
309 | \r | |
310 | def GenFspHdr (fspfile, outdir, hfile, show):\r | |
311 | fsp_fv = FspFv(fspfile)\r | |
312 | fsp_fv.ParseFv()\r | |
313 | fsp_fv.ParseFsp()\r | |
314 | fsp_fv.CheckFsp()\r | |
315 | if show:\r | |
316 | fsp_fv.PrintFv()\r | |
317 | fsp_fv.WriteMap(outdir, hfile)\r | |
318 | \r | |
319 | def SplitFspBin (fspfile, outdir, nametemplate, show):\r | |
320 | fsp_fv = FspFv(fspfile)\r | |
321 | fsp_fv.ParseFv()\r | |
322 | fsp_fv.ParseFsp()\r | |
323 | if show:\r | |
324 | fsp_fv.PrintFv()\r | |
325 | fsp_fv.WriteFsp(outdir, nametemplate)\r | |
326 | \r | |
327 | def main ():\r | |
328 | parser = argparse.ArgumentParser()\r | |
329 | subparsers = parser.add_subparsers(title='commands')\r | |
330 | \r | |
331 | parser_split = subparsers.add_parser('split', help='split a FSP into multiple components')\r | |
332 | parser_split.set_defaults(which='split')\r | |
333 | parser_split.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)\r | |
334 | parser_split.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.')\r | |
335 | parser_split.add_argument('-n', '--nametpl', dest='NameTemplate', type=str, help='Output name template', default = '')\r | |
336 | parser_split.add_argument('-p', action='store_true', help='Print FSP FV information', default = False)\r | |
337 | \r | |
338 | parser_genhdr = subparsers.add_parser('genhdr', help='generate a header file for FSP binary')\r | |
339 | parser_genhdr.set_defaults(which='genhdr')\r | |
340 | parser_genhdr.add_argument('-f', '--fspbin' , dest='FspBinary', type=str, help='FSP binary file path', required = True)\r | |
341 | parser_genhdr.add_argument('-o', '--outdir' , dest='OutputDir', type=str, help='Output directory path', default = '.')\r | |
342 | parser_genhdr.add_argument('-n', '--hfile', dest='HFileName', type=str, help='Output header file name', default = '')\r | |
343 | parser_genhdr.add_argument('-p', action='store_true', help='Print FSP FV information', default = False)\r | |
344 | \r | |
345 | args = parser.parse_args()\r | |
346 | if args.which in ['split', 'genhdr']:\r | |
347 | if not os.path.exists(args.FspBinary):\r | |
348 | raise Exception ("Could not locate FSP binary file '%s' !" % args.FspBinary)\r | |
349 | if not os.path.exists(args.OutputDir):\r | |
350 | raise Exception ("Invalid output directory '%s' !" % args.OutputDir)\r | |
351 | \r | |
352 | if args.which == 'split':\r | |
353 | SplitFspBin (args.FspBinary, args.OutputDir, args.NameTemplate, args.p)\r | |
354 | elif args.which == 'genhdr':\r | |
355 | GenFspHdr (args.FspBinary, args.OutputDir, args.HFileName, args.p)\r | |
356 | else:\r | |
357 | pass\r | |
358 | \r | |
359 | print 'Done!'\r | |
360 | return 0\r | |
361 | \r | |
362 | if __name__ == '__main__':\r | |
363 | sys.exit(main())\r |