audk/UefiPayloadPkg/Tools/MkFitImage.py

278 lines
13 KiB
Python

## @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, Arch):
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(Arch, 'utf-8'), len(Arch) + 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, Arch):
#
# 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, Arch):
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, Arch)
#
# 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, Arch):
#
# 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, Arch)
def ReplaceFv (UplBinary, SectionFvFile, SectionName, Arch):
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", Arch)
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