Files
system76-edk2/UefiPayloadPkg/UniversalPayloadBuild.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

362 lines
16 KiB
Python

## @file
# This file contains the script to build UniversalPayload
#
# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
import argparse
import subprocess
import os
import shutil
import sys
import pathlib
from ctypes import *
sys.dont_write_bytecode = True
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class UPLD_INFO_HEADER(LittleEndianStructure):
_pack_ = 1
_fields_ = [
('Identifier', ARRAY(c_char, 4)),
('HeaderLength', c_uint32),
('SpecRevision', c_uint16),
('Reserved', c_uint16),
('Revision', c_uint32),
('Attribute', c_uint32),
('Capability', c_uint32),
('ProducerId', ARRAY(c_char, 16)),
('ImageId', ARRAY(c_char, 16)),
]
def __init__(self):
self.Identifier = b'PLDH'
self.HeaderLength = sizeof(UPLD_INFO_HEADER)
self.SpecRevision = 0x0070
self.Revision = 0x0000010105
self.ImageId = b'UEFI'
self.ProducerId = b'INTEL'
def ValidateSpecRevision (Argument):
try:
(MajorStr, MinorStr) = Argument.split('.')
except:
raise argparse.ArgumentTypeError ('{} is not a valid SpecRevision format (Major[8-bits].Minor[8-bits]).'.format (Argument))
#
# Spec Revision Bits 15 : 8 - Major Version. Bits 7 : 0 - Minor Version.
#
if len(MinorStr) > 0 and len(MinorStr) < 3:
try:
Minor = int(MinorStr, 16) if len(MinorStr) == 2 else (int(MinorStr, 16) << 4)
except:
raise argparse.ArgumentTypeError ('{} Minor version of SpecRevision is not a valid integer value.'.format (Argument))
else:
raise argparse.ArgumentTypeError ('{} is not a valid SpecRevision format (Major[8-bits].Minor[8-bits]).'.format (Argument))
if len(MajorStr) > 0 and len(MajorStr) < 3:
try:
Major = int(MajorStr, 16)
except:
raise argparse.ArgumentTypeError ('{} Major version of SpecRevision is not a valid integer value.'.format (Argument))
else:
raise argparse.ArgumentTypeError ('{} is not a valid SpecRevision format (Major[8-bits].Minor[8-bits]).'.format (Argument))
return int('0x{0:02x}{1:02x}'.format(Major, Minor), 0)
def Validate32BitInteger (Argument):
try:
Value = int (Argument, 0)
except:
raise argparse.ArgumentTypeError ('{} is not a valid integer value.'.format (Argument))
if Value < 0:
raise argparse.ArgumentTypeError ('{} is a negative value.'.format (Argument))
if Value > 0xffffffff:
raise argparse.ArgumentTypeError ('{} is larger than 32-bits.'.format (Argument))
return Value
def ValidateAddFv (Argument):
Value = Argument.split ("=")
if len (Value) != 2:
raise argparse.ArgumentTypeError ('{} is incorrect format with "xxx_fv=xxx.fv"'.format (Argument))
if Value[0][-3:] != "_fv":
raise argparse.ArgumentTypeError ('{} is incorrect format with "xxx_fv=xxx.fv"'.format (Argument))
if Value[1][-3:].lower () != ".fv":
raise argparse.ArgumentTypeError ('{} is incorrect format with "xxx_fv=xxx.fv"'.format (Argument))
if os.path.exists (Value[1]) == False:
raise argparse.ArgumentTypeError ('File {} is not found.'.format (Value[1]))
return Value
def RunCommand(cmd):
print(cmd)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,cwd=os.environ['WORKSPACE'])
while True:
line = p.stdout.readline()
if not line:
break
print(line.strip().decode(errors='ignore'))
p.communicate()
if p.returncode != 0:
print("- Failed - error happened when run command: %s"%cmd)
raise Exception("ERROR: when run command: %s"%cmd)
def BuildUniversalPayload(Args):
BuildTarget = Args.Target
ToolChain = Args.ToolChain
Quiet = "--quiet" if Args.Quiet else ""
if Args.Fit == True:
PayloadEntryToolChain = ToolChain
Args.Macro.append("UNIVERSAL_PAYLOAD_FORMAT=FIT")
UpldEntryFile = "FitUniversalPayloadEntry"
else:
PayloadEntryToolChain = 'CLANGDWARF'
Args.Macro.append("UNIVERSAL_PAYLOAD_FORMAT=ELF")
UpldEntryFile = "UniversalPayloadEntry"
BuildDir = os.path.join(os.environ['WORKSPACE'], os.path.normpath("Build/UefiPayloadPkg{}").format (Args.Arch))
if Args.Arch == 'X64':
BuildArch = "X64"
FitArch = "x86_64"
elif Args.Arch == 'IA32':
BuildArch = "IA32 -a X64"
FitArch = "x86"
elif Args.Arch == 'RISCV64':
BuildArch = "RISCV64"
FitArch = "RISCV64"
else:
print("Incorrect arch option provided")
EntryOutputDir = os.path.join(BuildDir, "{}_{}".format (BuildTarget, PayloadEntryToolChain), os.path.normpath("{}/UefiPayloadPkg/UefiPayloadEntry/{}/DEBUG/{}.dll".format (Args.Arch, UpldEntryFile, UpldEntryFile)))
EntryModuleInf = os.path.normpath("UefiPayloadPkg/UefiPayloadEntry/{}.inf".format (UpldEntryFile))
DscPath = os.path.normpath("UefiPayloadPkg/UefiPayloadPkg.dsc")
DxeFvOutputDir = os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/DXEFV.Fv"))
BdsFvOutputDir = os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/BDSFV.Fv"))
NetworkFvOutputDir = os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/NETWORKFV.Fv"))
PayloadReportPath = os.path.join(BuildDir, "UefiUniversalPayload.txt")
ModuleReportPath = os.path.join(BuildDir, "UefiUniversalPayloadEntry.txt")
UpldInfoFile = os.path.join(BuildDir, "UniversalPayloadInfo.bin")
Pcds = ""
if (Args.pcd != None):
for PcdItem in Args.pcd:
Pcds += " --pcd {}".format (PcdItem)
Defines = ""
Defines += " -D BUILD_ARCH={}".format(Args.Arch)
if (Args.Macro != None):
for MacroItem in Args.Macro:
Defines += " -D {}".format (MacroItem)
#
# Building DXE core and DXE drivers as DXEFV.
#
if Args.BuildEntryOnly == False:
BuildPayload = "build -p {} -b {} -a {} -t {} -y {} {}".format (DscPath, BuildTarget, BuildArch, ToolChain, PayloadReportPath, Quiet)
BuildPayload += Pcds
BuildPayload += Defines
RunCommand(BuildPayload)
#
# Building Universal Payload entry.
#
if Args.PreBuildUplBinary is None:
BuildModule = "build -p {} -b {} -a {} -m {} -t {} -y {} {}".format (DscPath, BuildTarget, BuildArch, EntryModuleInf, PayloadEntryToolChain, ModuleReportPath, Quiet)
BuildModule += Pcds
BuildModule += Defines
RunCommand(BuildModule)
if Args.PreBuildUplBinary is not None:
if Args.Fit == False:
EntryOutputDir = os.path.join(BuildDir, "UniversalPayload.elf")
else:
EntryOutputDir = os.path.join(BuildDir, "UniversalPayload.fit")
shutil.copy (os.path.abspath(Args.PreBuildUplBinary), EntryOutputDir)
#
# Build Universal Payload Information Section ".upld_info"
#
if Args.Fit == False:
upld_info_hdr = UPLD_INFO_HEADER()
upld_info_hdr.SpecRevision = Args.SpecRevision
upld_info_hdr.Revision = Args.Revision
upld_info_hdr.ProducerId = Args.ProducerId.encode()[:16]
upld_info_hdr.ImageId = Args.ImageId.encode()[:16]
upld_info_hdr.Attribute |= 1 if BuildTarget == "DEBUG" else 0
fp = open(UpldInfoFile, 'wb')
fp.write(bytearray(upld_info_hdr))
fp.close()
if Args.BuildEntryOnly == False:
import Tools.ElfFv as ElfFv
ElfFv.ReplaceFv (EntryOutputDir, UpldInfoFile, '.upld_info', Alignment = 4)
if Args.PreBuildUplBinary is None:
if Args.Fit == False:
shutil.copy (EntryOutputDir, os.path.join(BuildDir, 'UniversalPayload.elf'))
else:
shutil.copy (EntryOutputDir, os.path.join(BuildDir, 'UniversalPayload.fit'))
MultiFvList = []
if Args.BuildEntryOnly == False:
MultiFvList = [
['uefi_fv', os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/DXEFV.Fv")) ],
['bds_fv', os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/BDSFV.Fv")) ],
['network_fv', os.path.join(BuildDir, "{}_{}".format (BuildTarget, ToolChain), os.path.normpath("FV/NETWORKFV.Fv"))],
]
if Args.Fit == True:
import Tools.MkFitImage as MkFitImage
import pefile
fit_image_info_header = MkFitImage.FIT_IMAGE_INFO_HEADER()
fit_image_info_header.Description = 'Uefi Universal Payload'
fit_image_info_header.UplVersion = Args.SpecRevision
fit_image_info_header.Type = 'flat-binary'
fit_image_info_header.Arch = FitArch
fit_image_info_header.Compression = 'none'
fit_image_info_header.Revision = Args.Revision
fit_image_info_header.BuildType = Args.Target.lower()
fit_image_info_header.Capabilities = None
fit_image_info_header.Producer = Args.ProducerId.lower()
fit_image_info_header.ImageId = Args.ImageId.lower()
fit_image_info_header.Binary = os.path.join(BuildDir, 'UniversalPayload.fit')
fit_image_info_header.TargetPath = os.path.join(BuildDir, 'UniversalPayload.fit')
fit_image_info_header.UefifvPath = DxeFvOutputDir
fit_image_info_header.BdsfvPath = BdsFvOutputDir
fit_image_info_header.NetworkfvPath = NetworkFvOutputDir
fit_image_info_header.DataOffset = 0x1000
fit_image_info_header.LoadAddr = Args.LoadAddress
fit_image_info_header.Project = 'tianocore'
TargetRebaseFile = fit_image_info_header.Binary.replace (pathlib.Path(fit_image_info_header.Binary).suffix, ".pecoff")
TargetRebaseEntryFile = fit_image_info_header.Binary.replace (pathlib.Path(fit_image_info_header.Binary).suffix, ".entry")
#
# Rebase PECOFF to load address
#
RunCommand (
"GenFw -e SEC -o {} {}".format (
TargetRebaseFile,
fit_image_info_header.Binary
))
RunCommand (
"GenFw --rebase 0x{:02X} -o {} {} ".format (
fit_image_info_header.LoadAddr + fit_image_info_header.DataOffset,
TargetRebaseFile,
TargetRebaseFile,
))
#
# Open PECOFF relocation table binary.
#
RelocBinary = b''
PeCoff = pefile.PE (TargetRebaseFile)
for reloc in PeCoff.DIRECTORY_ENTRY_BASERELOC:
for entry in reloc.entries:
if (entry.type == 0):
continue
Type = entry.type
Offset = entry.rva + fit_image_info_header.DataOffset
RelocBinary += Type.to_bytes (8, 'little') + Offset.to_bytes (8, 'little')
RelocBinary += b'\x00' * (0x1000 - (len(RelocBinary) % 0x1000))
#
# Output UniversalPayload.entry
#
TempBinary = open (TargetRebaseFile, 'rb')
TianoBinary = TempBinary.read ()
TempBinary.close ()
TianoEntryBinary = TianoBinary + RelocBinary
TianoEntryBinary += (b'\x00' * (0x1000 - (len(TianoBinary) % 0x1000)))
TianoEntryBinarySize = len (TianoEntryBinary)
TempBinary = open(TargetRebaseEntryFile, "wb")
TempBinary.truncate()
TempBinary.write(TianoEntryBinary)
TempBinary.close()
#
# Calculate entry and update relocation table start address and data-size.
#
fit_image_info_header.Entry = PeCoff.OPTIONAL_HEADER.ImageBase + PeCoff.OPTIONAL_HEADER.AddressOfEntryPoint
fit_image_info_header.RelocStart = fit_image_info_header.DataOffset + len(TianoBinary)
fit_image_info_header.DataSize = TianoEntryBinarySize
fit_image_info_header.Binary = TargetRebaseEntryFile
if MkFitImage.MakeFitImage(fit_image_info_header, Args.Arch) is True:
print('\nSuccessfully build Fit Image')
else:
sys.exit(1)
return MultiFvList, os.path.join(BuildDir, 'UniversalPayload.fit')
else:
return MultiFvList, os.path.join(BuildDir, 'UniversalPayload.elf')
def main():
parser = argparse.ArgumentParser(description='For building Universal Payload')
parser.add_argument('-t', '--ToolChain')
parser.add_argument('-b', '--Target', default='DEBUG')
parser.add_argument('-a', '--Arch', choices=['IA32', 'X64', 'RISCV64'], help='Specify the ARCH for payload entry module. Default build X64 image.', default ='X64')
parser.add_argument("-D", "--Macro", action="append", default=["UNIVERSAL_PAYLOAD=TRUE"])
parser.add_argument('-i', '--ImageId', type=str, help='Specify payload ID (16 bytes maximal).', default ='UEFI')
parser.add_argument('-q', '--Quiet', action='store_true', help='Disable all build messages except FATAL ERRORS.')
parser.add_argument("-p", "--pcd", action="append")
parser.add_argument("-s", "--SpecRevision", type=ValidateSpecRevision, default ='0.7', help='Indicates compliance with a revision of this specification in the BCD format.')
parser.add_argument("-r", "--Revision", type=Validate32BitInteger, default ='0x0000010105', help='Revision of the Payload binary. Major.Minor.Revision.Build')
parser.add_argument("-o", "--ProducerId", default ='INTEL', help='A null-terminated OEM-supplied string that identifies the payload producer (16 bytes maximal).')
parser.add_argument("-e", "--BuildEntryOnly", action='store_true', help='Build UniversalPayload Entry file')
parser.add_argument("-pb", "--PreBuildUplBinary", default=None, help='Specify the UniversalPayload file')
parser.add_argument("-sk", "--SkipBuild", action='store_true', help='Skip UniversalPayload build')
parser.add_argument("-af", "--AddFv", type=ValidateAddFv, action='append', help='Add or replace specific FV into payload, Ex: uefi_fv=XXX.fv')
parser.add_argument("-f", "--Fit", action='store_true', help='Build UniversalPayload file as UniversalPayload.fit', default=False)
parser.add_argument('-l', "--LoadAddress", type=int, help='Specify payload load address', default =0x000800000)
args = parser.parse_args()
MultiFvList = []
UniversalPayloadBinary = args.PreBuildUplBinary
if (args.SkipBuild == False):
MultiFvList, UniversalPayloadBinary = BuildUniversalPayload(args)
if (args.AddFv != None):
for (SectionName, SectionFvFile) in args.AddFv:
MultiFvList.append ([SectionName, SectionFvFile])
def ReplaceFv (UplBinary, SectionFvFile, SectionName, Arch):
print (bcolors.OKGREEN + "Patch {}={} into {}".format (SectionName, SectionFvFile, UplBinary) + bcolors.ENDC)
if (args.Fit == False):
import Tools.ElfFv as ElfFv
return ElfFv.ReplaceFv (UplBinary, SectionFvFile, '.upld.{}'.format (SectionName))
else:
import Tools.MkFitImage as MkFitImage
return MkFitImage.ReplaceFv (UplBinary, SectionFvFile, SectionName, Arch)
if (UniversalPayloadBinary != None):
for (SectionName, SectionFvFile) in MultiFvList:
if os.path.exists (SectionFvFile) == False:
continue
if (args.Fit == False):
status = ReplaceFv (UniversalPayloadBinary, SectionFvFile, SectionName)
else:
status = ReplaceFv (UniversalPayloadBinary, SectionFvFile, SectionName.replace ("_", "-"), args.Arch)
if status != 0:
print (bcolors.FAIL + "[Fail] Patch {}={}".format (SectionName, SectionFvFile) + bcolors.ENDC)
return status
print ("\nSuccessfully build Universal Payload")
if __name__ == '__main__':
main()