summaryrefslogtreecommitdiff
path: root/UefiPayloadPkg/Tools/MkFitImage.py
diff options
context:
space:
mode:
Diffstat (limited to 'UefiPayloadPkg/Tools/MkFitImage.py')
-rw-r--r--UefiPayloadPkg/Tools/MkFitImage.py272
1 files changed, 272 insertions, 0 deletions
diff --git a/UefiPayloadPkg/Tools/MkFitImage.py b/UefiPayloadPkg/Tools/MkFitImage.py
new file mode 100644
index 0000000..82ab933
--- /dev/null
+++ b/UefiPayloadPkg/Tools/MkFitImage.py
@@ -0,0 +1,272 @@
+## @file
+# This file is a script to build fit image.
+# It generate a dtb header and combine a binary file after this header.
+#
+# Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+from os.path import exists
+import libfdt
+from ctypes import *
+import time
+
+class FIT_IMAGE_INFO_HEADER:
+ """Class for user setting data to use MakeFitImage()
+ """
+ _pack_ = 1
+ _fields_ = [
+ ('Compatible', str),
+ ('UplVersion', int),
+ ('Description', str),
+ ('Type', str),
+ ('Arch', str),
+ ('Compression', str),
+ ('Revision', int),
+ ('BuildType', str),
+ ('Capabilities', str),
+ ('Producer', str),
+ ('ImageId', str),
+ ('DataOffset', int),
+ ('DataSize', int),
+ ('RelocStart', int),
+ ('LoadAddr', int),
+ ('Entry', int),
+ ('Binary', str),
+ ('TargetPath', str),
+ ('UefifvPath', str),
+ ('BdsfvPath', str),
+ ('NetworkfvPath', str),
+ ('Project', str),
+ ]
+
+ def __init__(self):
+ self.Compatible = 'universal-payload'
+ self.UplVersion = 0x0100
+ self.TargetPath = 'mkimage.fit'
+
+def CreatFdt(Fdt):
+ FdtEmptyTree = libfdt.fdt_create_empty_tree(Fdt, len(Fdt))
+ if FdtEmptyTree != 0:
+ print('\n- Failed - Create Fdt failed!')
+ return False
+ return True
+
+def BuildConfNode(Fdt, ParentNode, MultiImage):
+ ConfNode1 = libfdt.fdt_add_subnode(Fdt, ParentNode, 'conf-1')
+
+ libfdt.fdt_setprop(Fdt, ConfNode1, 'require-fit', b'', 0)
+ libfdt.fdt_setprop(Fdt, ConfNode1, 'firmware', bytes('tianocore', 'utf-8'), len('tianocore') + 1)
+
+def BuildFvImageNode(Fdt, InfoHeader, ParentNode, DataOffset, DataSize, Description):
+ libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-size', DataSize)
+ libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-offset', DataOffset)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'compression', bytes('none', 'utf-8'), len('none') + 1)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'project ', bytes('tianocore', 'utf-8'), len('tianocore') + 1)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'arch', bytes('x86_64', 'utf-8'), len('x86_64') + 1)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'type', bytes('flat-binary', 'utf-8'), len('flat-binary') + 1)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'description', bytes(Description, 'utf-8'), len(Description) + 1)
+
+def BuildTianoImageNode(Fdt, InfoHeader, ParentNode, DataOffset, DataSize, Description):
+ #
+ # Set 'load' and 'data-offset' to reserve the memory first.
+ # They would be set again when Fdt completes or this function parses target binary file.
+ #
+ if InfoHeader.LoadAddr is not None:
+ libfdt.fdt_setprop_u64(Fdt, ParentNode, 'load', InfoHeader.LoadAddr)
+ if InfoHeader.Entry is not None:
+ libfdt.fdt_setprop_u64(Fdt, ParentNode, 'entry-start', InfoHeader.Entry)
+ if InfoHeader.RelocStart is not None:
+ libfdt.fdt_setprop_u32(Fdt, ParentNode, 'reloc-start', InfoHeader.RelocStart)
+ if InfoHeader.DataSize is not None:
+ libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-size', DataSize)
+ if InfoHeader.DataOffset is not None:
+ libfdt.fdt_setprop_u32(Fdt, ParentNode, 'data-offset', DataOffset)
+ if InfoHeader.Producer is not None:
+ libfdt.fdt_setprop(Fdt, ParentNode, 'producer ', bytes(InfoHeader.Producer, 'utf-8'), len(InfoHeader.Producer) + 1)
+ if InfoHeader.Capabilities is not None:
+ CapStrs = ','.join(InfoHeader.Capabilities)
+ libfdt.fdt_setprop(Fdt, ParentNode, 'capabilities ', bytes(CapStrs, 'utf-8'), len(CapStrs) + 1)
+ if InfoHeader.Type is not None:
+ libfdt.fdt_setprop(Fdt, ParentNode, 'type ', bytes(InfoHeader.Type, 'utf-8'), len(InfoHeader.Type) + 1)
+ if InfoHeader.Arch is not None:
+ libfdt.fdt_setprop(Fdt, ParentNode, 'arch ', bytes(InfoHeader.Arch, 'utf-8'), len(InfoHeader.Arch) + 1)
+ if InfoHeader.Project is not None:
+ libfdt.fdt_setprop(Fdt, ParentNode, 'project ', bytes(InfoHeader.Project, 'utf-8'), len(InfoHeader.Project) + 1)
+ if InfoHeader.Description is not None:
+ libfdt.fdt_setprop(Fdt, ParentNode, 'description', bytes(Description, 'utf-8'), len(Description) + 1)
+
+#
+# The subnode would be inserted from bottom to top of structure block.
+#
+def BuildFitImage(Fdt, InfoHeader):
+ MultiImage = [
+ ["tianocore", InfoHeader.Binary, BuildTianoImageNode , InfoHeader.Description, None, 0 ],
+ ["uefi-fv", InfoHeader.UefifvPath, BuildFvImageNode, "UEFI Firmware Volume", None, 0 ],
+ ["bds-fv", InfoHeader.BdsfvPath, BuildFvImageNode , "BDS Firmware Volume", None, 0 ],
+ ["network-fv", InfoHeader.NetworkfvPath, BuildFvImageNode , "Network Firmware Volume", None, 0 ],
+ ]
+
+ #
+ # Set basic information
+ #
+ libfdt.fdt_setprop_u32(Fdt, 0, 'build-revision ', InfoHeader.Revision)
+ libfdt.fdt_setprop_u32(Fdt, 0, 'spec-version', InfoHeader.UplVersion)
+
+ #
+ # Build configurations node
+ #
+ ConfNode = libfdt.fdt_add_subnode(Fdt, 0, 'configurations')
+ BuildConfNode(Fdt, ConfNode, MultiImage)
+
+ # Build image
+ DataOffset = InfoHeader.DataOffset
+ for Index in range (0, len (MultiImage)):
+ _, Path, _, _, _, _ = MultiImage[Index]
+ if exists(Path) == 1:
+ TempBinary = open(Path, 'rb')
+ BinaryData = TempBinary.read()
+ TempBinary.close()
+ MultiImage[Index][-2] = BinaryData
+ MultiImage[Index][-1] = DataOffset
+ DataOffset += len (BinaryData)
+ libfdt.fdt_setprop_u32(Fdt, 0, 'size', DataOffset)
+ posix_time = int(time.time())
+ libfdt.fdt_setprop_u32(Fdt, 0, 'timestamp', posix_time)
+ DescriptionFit = 'Uefi OS Loader'
+ libfdt.fdt_setprop(Fdt, 0, 'description', bytes(DescriptionFit, 'utf-8'), len(DescriptionFit) + 1)
+
+ ImageNode = libfdt.fdt_add_subnode(Fdt, 0, 'images')
+ for Item in reversed (MultiImage):
+ Name, Path, BuildFvNode, Description, BinaryData, DataOffset = Item
+ FvNode = libfdt.fdt_add_subnode(Fdt, ImageNode, Name)
+ BuildFvNode (Fdt, InfoHeader, FvNode, DataOffset, len(BinaryData), Description)
+
+ #
+ # Create new image file and combine all binary.
+ #
+ DtbFile = open(InfoHeader.TargetPath, "wb")
+ DtbFile.truncate()
+ DtbFile.write(Fdt)
+ for Item in MultiImage:
+ _, _, _, _, BinaryData, _ = Item
+ DtbFile.write(BinaryData)
+ DtbFile.close()
+
+ return True
+
+def MakeFitImage(InfoHeader):
+ #
+ # Allocate fdt byte array.
+ #
+ Fdt = bytearray(InfoHeader.DataOffset)
+
+ #
+ # Create fdt empty tree.
+ #
+ if CreatFdt(Fdt) is False:
+ return False
+
+ #
+ # Parse args to build fit image.
+ #
+ return BuildFitImage(Fdt, InfoHeader)
+
+def ReplaceFv (UplBinary, SectionFvFile, SectionName):
+ try:
+ #
+ # Get Original Multi Fv
+ #
+ with open (UplBinary, "rb") as File:
+ Dtb = File.read ()
+ Fit = libfdt.Fdt (Dtb)
+ NewFitHeader = bytearray(Dtb[0:Fit.totalsize()])
+ FitSize = len(Dtb)
+
+ LoadablesList = []
+ ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
+ FvNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, 'uefi-fv')
+ NodeDepth = libfdt.fdt_node_depth (NewFitHeader, ImagesNode)
+ node_name = libfdt.fdt_get_name(NewFitHeader, FvNode)
+ FvNode = libfdt.fdt_next_node(NewFitHeader, FvNode, NodeDepth)
+
+ while node_name[0][-2:] == 'fv':
+ LoadablesList.append (node_name[0])
+ node_name = libfdt.fdt_get_name(NewFitHeader, FvNode[0])
+ FvNode = libfdt.fdt_next_node(NewFitHeader, FvNode[0], NodeDepth)
+ #
+ # Get current Fit Binary FV data
+ #
+ MultiFvList = []
+ for Item in LoadablesList:
+ ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, Item)
+ ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
+ ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
+ MultiFvList.append ([Item, Dtb[ImageOffset:ImageOffset + ImageSize]])
+
+ IsFvExist = False
+ for Index in range (0, len (MultiFvList)):
+ if MultiFvList[Index][0] == SectionName:
+ with open (SectionFvFile, 'rb') as File:
+ MultiFvList[Index][1] = File.read ()
+ ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, SectionName)
+ ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
+ ReplaceOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
+ OffsetDelta = len(MultiFvList[Index][1]) - ImageSize
+ FitSize += OffsetDelta
+ IsFvExist = True
+ libfdt.fdt_setprop_u32(NewFitHeader, ImageNode, 'data-size', len(MultiFvList[Index][1]))
+
+ #
+ # Update new fit header
+ #
+ ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
+ if (IsFvExist == False):
+ with open (SectionFvFile, 'rb') as File:
+ SectionFvFileBinary = File.read ()
+ MultiFvList.append ([SectionName, SectionFvFileBinary])
+ FvNode = libfdt.fdt_add_subnode(NewFitHeader, ImagesNode, SectionName)
+ BuildFvImageNode (NewFitHeader, None, FvNode, FitSize, len(SectionFvFileBinary), SectionName + " Firmware Volume")
+ FitSize += len(SectionFvFileBinary)
+ else:
+ for Index in range (0, len (MultiFvList)):
+ ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, MultiFvList[Index][0])
+ ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
+ if ImageOffset > ReplaceOffset:
+ libfdt.fdt_setprop_u32(NewFitHeader, ImageNode, 'data-offset', ImageOffset + OffsetDelta)
+
+ ConfNodes = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'configurations')
+ libfdt.fdt_setprop(NewFitHeader, ConfNodes, 'default ', bytes('conf-1', 'utf-8'), len('conf-1') + 1)
+ ConfNode = libfdt.fdt_subnode_offset(NewFitHeader, ConfNodes, 'conf-1')
+
+ libfdt.fdt_setprop_u32(NewFitHeader, 0, 'size', FitSize)
+
+ #
+ # Generate new fit image
+ #
+ ImagesNode = libfdt.fdt_subnode_offset(NewFitHeader, 0, 'images')
+ TianoNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, 'tianocore')
+ TianoOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, TianoNode, 'data-offset')[0], 'big')
+ TianoSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, TianoNode, 'data-size')[0], 'big')
+ TianoBinary = Dtb[TianoOffset:TianoOffset + TianoSize]
+
+ print("\nGenerate new fit image:")
+ NewUplBinary = bytearray(FitSize)
+ print("Update fit header\t to 0x0\t\t ~ " + str(hex(len(NewFitHeader))))
+ NewUplBinary[:len(NewFitHeader)] = NewFitHeader
+ print("Update tiano image\t to " + str(hex(len(NewFitHeader))) + "\t ~ " + str(hex(len(NewFitHeader) + len(TianoBinary))))
+ NewUplBinary[len(NewFitHeader):len(NewFitHeader) + len(TianoBinary)] = TianoBinary
+ for Index in range (0, len (MultiFvList)):
+ ImageNode = libfdt.fdt_subnode_offset(NewFitHeader, ImagesNode, MultiFvList[Index][0])
+ ImageOffset = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-offset')[0], 'big')
+ ImageSize = int.from_bytes (libfdt.fdt_getprop (NewFitHeader, ImageNode, 'data-size')[0], 'big')
+ NewUplBinary[ImageOffset:ImageOffset + ImageSize] = MultiFvList[Index][1]
+ print("Update " + MultiFvList[Index][0] + "\t\t to " + str(hex(ImageOffset)) + "\t ~ " + str(hex(ImageOffset + ImageSize)))
+
+ with open (UplBinary, "wb") as File:
+ File.write (NewUplBinary)
+
+ return 0
+ except Exception as Ex:
+ print(Ex)
+ return 1