Files
system76-edk2/UefiPayloadPkg/Tools/MkFitImage.py
Dhaval 11ad164bce UefiPayloadPkg: Make UPL build script arch agnostic
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>
2024-02-23 12:44:58 +00:00

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