Current implementation makes assumptions about arch it will be built for. Need to make it more generic to add follow up support for RISCV. Right now it does not build for RV until relevant dsc file is available. Cc: Guo Dong <guo.dong@intel.com> Cc: Sean Rhodes <sean@starlabs.systems> Cc: James Lu <james.lu@intel.com> Reviewed-by: Gua Guo <gua.guo@intel.com> Signed-off-by: Dhaval Sharma <dhaval@rivosinc.com>
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
|