1 | ## @file
|
---|
2 | # This file is a script to build fit image.
|
---|
3 | # It generate a dtb header and combine a binary file after this header.
|
---|
4 | #
|
---|
5 | # Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
|
---|
6 | # SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
7 | ##
|
---|
8 |
|
---|
9 | from os.path import exists
|
---|
10 | import libfdt
|
---|
11 | from ctypes import *
|
---|
12 | import time
|
---|
13 | import os
|
---|
14 |
|
---|
15 | class FIT_IMAGE_INFO_HEADER:
|
---|
16 | """Class for user setting data to use MakeFitImage()
|
---|
17 | """
|
---|
18 | _pack_ = 1
|
---|
19 | _fields_ = [
|
---|
20 | ('Compatible', str),
|
---|
21 | ('UplVersion', int),
|
---|
22 | ('Description', str),
|
---|
23 | ('Type', str),
|
---|
24 | ('Arch', str),
|
---|
25 | ('Compression', str),
|
---|
26 | ('Revision', int),
|
---|
27 | ('BuildType', str),
|
---|
28 | ('Capabilities', str),
|
---|
29 | ('Producer', str),
|
---|
30 | ('ImageId', str),
|
---|
31 | ('DataOffset', int),
|
---|
32 | ('DataSize', int),
|
---|
33 | ('RelocStart', int),
|
---|
34 | ('LoadAddr', int),
|
---|
35 | ('Entry', int),
|
---|
36 | ('Binary', str),
|
---|
37 | ('TargetPath', str),
|
---|
38 | ('UefifvPath', str),
|
---|
39 | ('BdsfvPath', str),
|
---|
40 | ('NetworkfvPath', str),
|
---|
41 | ('Project', str),
|
---|
42 | ]
|
---|
43 |
|
---|
44 | def __init__(self):
|
---|
45 | self.Compatible = 'universal-payload'
|
---|
46 | self.UplVersion = 0x0100
|
---|
47 | self.TargetPath = 'mkimage.fit'
|
---|
48 |
|
---|
49 | def CreatFdt(Fdt):
|
---|
50 | FdtEmptyTree = libfdt.fdt_create_empty_tree(Fdt, len(Fdt))
|
---|
51 | if FdtEmptyTree != 0:
|
---|
52 | print('\n- Failed - Create Fdt failed!')
|
---|
53 | return False
|
---|
54 | return True
|
---|
55 |
|
---|
56 | def BuildConfNode(Fdt, ParentNode, MultiImage):
|
---|
57 | ConfNode1 = libfdt.fdt_add_subnode(Fdt, ParentNode, 'conf-1')
|
---|
58 |
|
---|
59 | libfdt.fdt_setprop(Fdt, ConfNode1, 'require-fit', b'', 0)
|
---|
60 | libfdt.fdt_setprop(Fdt, ConfNode1, 'firmware', bytes('tianocore', 'utf-8'), len('tianocore') + 1)
|
---|
61 |
|
---|
62 | def BuildFvImageNode(Fdt, InfoHeader, ParentNode, DataOffset, DataSize, Description, Arch):
|
---|
63 | libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-size', DataSize)
|
---|
64 | libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-offset', DataOffset)
|
---|
65 | libfdt.fdt_setprop(Fdt, ParentNode, 'compression', bytes('none', 'utf-8'), len('none') + 1)
|
---|
66 | libfdt.fdt_setprop(Fdt, ParentNode, 'project ', bytes('tianocore', 'utf-8'), len('tianocore') + 1)
|
---|
67 | libfdt.fdt_setprop(Fdt, ParentNode, 'arch', bytes(Arch, 'utf-8'), len(Arch) + 1)
|
---|
68 | libfdt.fdt_setprop(Fdt, ParentNode, 'type', bytes('flat-binary', 'utf-8'), len('flat-binary') + 1)
|
---|
69 | libfdt.fdt_setprop(Fdt, ParentNode, 'description', bytes(Description, 'utf-8'), len(Description) + 1)
|
---|
70 |
|
---|
71 | def BuildTianoImageNode(Fdt, InfoHeader, ParentNode, DataOffset, DataSize, Description, Arch):
|
---|
72 | #
|
---|
73 | # Set 'load' and 'data-offset' to reserve the memory first.
|
---|
74 | # They would be set again when Fdt completes or this function parses target binary file.
|
---|
75 | #
|
---|
76 | if InfoHeader.LoadAddr is not None:
|
---|
77 | libfdt.fdt_setprop_u64(Fdt, ParentNode, 'load', InfoHeader.LoadAddr)
|
---|
78 | if InfoHeader.Entry is not None:
|
---|
79 | libfdt.fdt_setprop_u64(Fdt, ParentNode, 'entry-start', InfoHeader.Entry)
|
---|
80 | if InfoHeader.RelocStart is not None:
|
---|
81 | libfdt.fdt_setprop_u32(Fdt, ParentNode, 'reloc-start', InfoHeader.RelocStart)
|
---|
82 | if InfoHeader.DataSize is not None:
|
---|
83 | libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-size', DataSize)
|
---|
84 | if InfoHeader.DataOffset is not None:
|
---|
85 | libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-offset', DataOffset)
|
---|
86 | if InfoHeader.Producer is not None:
|
---|
87 | libfdt.fdt_setprop(Fdt, ParentNode, 'producer ', bytes(InfoHeader.Producer, 'utf-8'), len(InfoHeader.Producer) + 1)
|
---|
88 | if InfoHeader.Capabilities is not None:
|
---|
89 | CapStrs = ','.join(InfoHeader.Capabilities)
|
---|
90 | libfdt.fdt_setprop(Fdt, ParentNode, 'capabilities ', bytes(CapStrs, 'utf-8'), len(CapStrs) + 1)
|
---|
91 | if InfoHeader.Type is not None:
|
---|
92 | libfdt.fdt_setprop(Fdt, ParentNode, 'type ', bytes(InfoHeader.Type, 'utf-8'), len(InfoHeader.Type) + 1)
|
---|
93 | if InfoHeader.Arch is not None:
|
---|
94 | libfdt.fdt_setprop(Fdt, ParentNode, 'arch ', bytes(InfoHeader.Arch, 'utf-8'), len(InfoHeader.Arch) + 1)
|
---|
95 | if InfoHeader.Project is not None:
|
---|
96 | libfdt.fdt_setprop(Fdt, ParentNode, 'project ', bytes(InfoHeader.Project, 'utf-8'), len(InfoHeader.Project) + 1)
|
---|
97 | if InfoHeader.Description is not None:
|
---|
98 | libfdt.fdt_setprop(Fdt, ParentNode, 'description', bytes(Description, 'utf-8'), len(Description) + 1)
|
---|
99 |
|
---|
100 | #
|
---|
101 | # The subnode would be inserted from bottom to top of structure block.
|
---|
102 | #
|
---|
103 | def BuildFitImage(Fdt, InfoHeader, Arch):
|
---|
104 | MultiImage = [
|
---|
105 | ["tianocore", InfoHeader.Binary, BuildTianoImageNode , InfoHeader.Description, None, 0 ],
|
---|
106 | ["uefi-fv", InfoHeader.UefifvPath, BuildFvImageNode, "UEFI Firmware Volume", None, 0 ],
|
---|
107 | ["bds-fv", InfoHeader.BdsfvPath, BuildFvImageNode , "BDS Firmware Volume", None, 0 ],
|
---|
108 | ["network-fv", InfoHeader.NetworkfvPath, BuildFvImageNode , "Network Firmware Volume", None, 0 ],
|
---|
109 | ]
|
---|
110 |
|
---|
111 | #
|
---|
112 | # Set basic information
|
---|
113 | #
|
---|
114 | libfdt.fdt_setprop_u32(Fdt, 0, 'build-revision ', InfoHeader.Revision)
|
---|
115 | libfdt.fdt_setprop_u32(Fdt, 0, 'spec-version', InfoHeader.UplVersion)
|
---|
116 |
|
---|
117 | #
|
---|
118 | # Build configurations node
|
---|
119 | #
|
---|
120 | ConfNode = libfdt.fdt_add_subnode(Fdt, 0, 'configurations')
|
---|
121 | BuildConfNode(Fdt, ConfNode, MultiImage)
|
---|
122 |
|
---|
123 | # Build image
|
---|
124 | DataOffset = InfoHeader.DataOffset
|
---|
125 | for Index in range (0, len (MultiImage)):
|
---|
126 | _, Path, _, _, _, _ = MultiImage[Index]
|
---|
127 | if exists(Path) == 1:
|
---|
128 | TempBinary = open(Path, 'rb')
|
---|
129 | BinaryData = TempBinary.read()
|
---|
130 | TempBinary.close()
|
---|
131 | MultiImage[Index][-2] = BinaryData
|
---|
132 | MultiImage[Index][-1] = DataOffset
|
---|
133 | DataOffset += len (BinaryData)
|
---|
134 | libfdt.fdt_setprop_u32(Fdt, 0, 'size', DataOffset)
|
---|
135 | posix_time = int(time.time())
|
---|
136 | libfdt.fdt_setprop_u32(Fdt, 0, 'timestamp', posix_time)
|
---|
137 | DescriptionFit = 'Uefi OS Loader'
|
---|
138 | libfdt.fdt_setprop(Fdt, 0, 'description', bytes(DescriptionFit, 'utf-8'), len(DescriptionFit) + 1)
|
---|
139 |
|
---|
140 | ImageNode = libfdt.fdt_add_subnode(Fdt, 0, 'images')
|
---|
141 | for Item in reversed (MultiImage):
|
---|
142 | Name, Path, BuildFvNode, Description, BinaryData, DataOffset = Item
|
---|
143 | if os.path.exists (Item[1]) == False:
|
---|
144 | continue
|
---|
145 | FvNode = libfdt.fdt_add_subnode(Fdt, ImageNode, Name)
|
---|
146 | BuildFvNode (Fdt, InfoHeader, FvNode, DataOffset, len(BinaryData), Description, Arch)
|
---|
147 |
|
---|
148 | #
|
---|
149 | # Create new image file and combine all binary.
|
---|
150 | #
|
---|
151 | DtbFile = open(InfoHeader.TargetPath, "wb")
|
---|
152 | DtbFile.truncate()
|
---|
153 | DtbFile.write(Fdt)
|
---|
154 | for Item in MultiImage:
|
---|
155 | _, FilePath, _, _, BinaryData, _ = Item
|
---|
156 | if os.path.exists (Item[1]) == False:
|
---|
157 | continue
|
---|
158 | DtbFile.write(BinaryData)
|
---|
159 | DtbFile.close()
|
---|
160 |
|
---|
161 | return True
|
---|
162 |
|
---|
163 | def MakeFitImage(InfoHeader, Arch):
|
---|
164 | #
|
---|
165 | # Allocate fdt byte array.
|
---|
166 | #
|
---|
167 | Fdt = bytearray(InfoHeader.DataOffset)
|
---|
168 |
|
---|
169 | #
|
---|
170 | # Create fdt empty tree.
|
---|
171 | #
|
---|
172 | if CreatFdt(Fdt) is False:
|
---|
173 | return False
|
---|
174 |
|
---|
175 | #
|
---|
176 | # Parse args to build fit image.
|
---|
177 | #
|
---|
178 | return BuildFitImage(Fdt, InfoHeader, Arch)
|
---|
179 |
|
---|
180 | def ReplaceFv (UplBinary, SectionFvFile, SectionName, Arch):
|
---|
181 | try:
|
---|
182 | #
|
---|
183 | # Get Original Multi Fv
|
---|
184 | #
|
---|
185 | with open (UplBinary, "rb") as File:
|
---|
186 | Dtb = File.read ()
|
---|
187 | Fit = libfdt.Fdt (Dtb)
|
---|
188 | NewFitHeader = bytearray(Dtb[0:Fit.totalsize()])
|
---|
189 | FitSize = len(Dtb)
|
---|
190 |
|
---|
191 | LoadablesList = []
|
---|
192 | ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
|
---|
193 | FvNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, 'uefi-fv')
|
---|
194 | NodeDepth = libfdt.fdt_node_depth (NewFitHeader, ImagesNode)
|
---|
195 | node_name = libfdt.fdt_get_name(NewFitHeader, FvNode)
|
---|
196 | FvNode = libfdt.fdt_next_node(NewFitHeader, FvNode, NodeDepth)
|
---|
197 |
|
---|
198 | while node_name[0][-2:] == 'fv':
|
---|
199 | LoadablesList.append (node_name[0])
|
---|
200 | node_name = libfdt.fdt_get_name(NewFitHeader, FvNode[0])
|
---|
201 | FvNode = libfdt.fdt_next_node(NewFitHeader, FvNode[0], NodeDepth)
|
---|
202 | #
|
---|
203 | # Get current Fit Binary FV data
|
---|
204 | #
|
---|
205 | MultiFvList = []
|
---|
206 | for Item in LoadablesList:
|
---|
207 | ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, Item)
|
---|
208 | ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
|
---|
209 | ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
|
---|
210 | MultiFvList.append ([Item, Dtb[ImageOffset:ImageOffset + ImageSize]])
|
---|
211 |
|
---|
212 | IsFvExist = False
|
---|
213 | for Index in range (0, len (MultiFvList)):
|
---|
214 | if MultiFvList[Index][0] == SectionName:
|
---|
215 | with open (SectionFvFile, 'rb') as File:
|
---|
216 | MultiFvList[Index][1] = File.read ()
|
---|
217 | ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, SectionName)
|
---|
218 | ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
|
---|
219 | ReplaceOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
|
---|
220 | OffsetDelta = len(MultiFvList[Index][1]) - ImageSize
|
---|
221 | FitSize += OffsetDelta
|
---|
222 | IsFvExist = True
|
---|
223 | libfdt.fdt_setprop_u32(NewFitHeader, ImageNode, 'data-size', len(MultiFvList[Index][1]))
|
---|
224 |
|
---|
225 | #
|
---|
226 | # Update new fit header
|
---|
227 | #
|
---|
228 | ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
|
---|
229 | if (IsFvExist == False):
|
---|
230 | with open (SectionFvFile, 'rb') as File:
|
---|
231 | SectionFvFileBinary = File.read ()
|
---|
232 | MultiFvList.append ([SectionName, SectionFvFileBinary])
|
---|
233 | FvNode = libfdt.fdt_add_subnode(NewFitHeader, ImagesNode, SectionName)
|
---|
234 | BuildFvImageNode (NewFitHeader, None, FvNode, FitSize, len(SectionFvFileBinary), SectionName + " Firmware Volume", Arch)
|
---|
235 | FitSize += len(SectionFvFileBinary)
|
---|
236 | else:
|
---|
237 | for Index in range (0, len (MultiFvList)):
|
---|
238 | ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, MultiFvList[Index][0])
|
---|
239 | ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
|
---|
240 | if ImageOffset > ReplaceOffset:
|
---|
241 | libfdt.fdt_setprop_u32(NewFitHeader, ImageNode, 'data-offset', ImageOffset + OffsetDelta)
|
---|
242 |
|
---|
243 | ConfNodes = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'configurations')
|
---|
244 | libfdt.fdt_setprop(NewFitHeader, ConfNodes, 'default ', bytes('conf-1', 'utf-8'), len('conf-1') + 1)
|
---|
245 | ConfNode = libfdt.fdt_subnode_offset(NewFitHeader, ConfNodes, 'conf-1')
|
---|
246 |
|
---|
247 | libfdt.fdt_setprop_u32(NewFitHeader, 0, 'size', FitSize)
|
---|
248 |
|
---|
249 | #
|
---|
250 | # Generate new fit image
|
---|
251 | #
|
---|
252 | ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
|
---|
253 | TianoNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, 'tianocore')
|
---|
254 | TianoOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, TianoNode, 'data-offset')[0], 'big')
|
---|
255 | TianoSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, TianoNode, 'data-size')[0], 'big')
|
---|
256 | TianoBinary = Dtb[TianoOffset:TianoOffset + TianoSize]
|
---|
257 |
|
---|
258 | print("\nGenerate new fit image:")
|
---|
259 | NewUplBinary = bytearray(FitSize)
|
---|
260 | print("Update fit header\t to 0x0\t\t ~ " + str(hex(len(NewFitHeader))))
|
---|
261 | NewUplBinary[:len(NewFitHeader)] = NewFitHeader
|
---|
262 | print("Update tiano image\t to " + str(hex(len(NewFitHeader))) + "\t ~ " + str(hex(len(NewFitHeader) + len(TianoBinary))))
|
---|
263 | NewUplBinary[len(NewFitHeader):len(NewFitHeader) + len(TianoBinary)] = TianoBinary
|
---|
264 | for Index in range (0, len (MultiFvList)):
|
---|
265 | ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, MultiFvList[Index][0])
|
---|
266 | ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
|
---|
267 | ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
|
---|
268 | NewUplBinary[ImageOffset:ImageOffset + ImageSize] = MultiFvList[Index][1]
|
---|
269 | print("Update " + MultiFvList[Index][0] + "\t\t to " + str(hex(ImageOffset)) + "\t ~ " + str(hex(ImageOffset + ImageSize)))
|
---|
270 |
|
---|
271 | with open (UplBinary, "wb") as File:
|
---|
272 | File.write (NewUplBinary)
|
---|
273 |
|
---|
274 | return 0
|
---|
275 | except Exception as Ex:
|
---|
276 | print(Ex)
|
---|
277 | return 1
|
---|