mirror of https://github.com/acidanthera/audk.git
278 lines
13 KiB
Python
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
|