| ## @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 | |
| import os | |
| 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 | |
| if os.path.exists (Item[1]) == False: | |
| continue | |
| 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: | |
| _, FilePath, _, _, BinaryData, _ = Item | |
| if os.path.exists (Item[1]) == False: | |
| continue | |
| 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 |