|
@@ -0,0 +1,397 @@
|
|
|
+## @file
|
|
|
+# Generate capsules for Vlv2TbltDevicePkg
|
|
|
+# openssl must be install and in path
|
|
|
+#
|
|
|
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
|
|
+#
|
|
|
+# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
+#
|
|
|
+
|
|
|
+'''
|
|
|
+GenCapsuleAll
|
|
|
+'''
|
|
|
+
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import argparse
|
|
|
+import subprocess
|
|
|
+import glob
|
|
|
+import shutil
|
|
|
+import struct
|
|
|
+import datetime
|
|
|
+
|
|
|
+#
|
|
|
+# Globals for help information
|
|
|
+#
|
|
|
+__prog__ = 'GenCapsuleAll'
|
|
|
+__copyright__ = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
|
|
|
+__description__ = 'Generate Vlv2Tbl2DevicePkg capsules.\n'
|
|
|
+
|
|
|
+#
|
|
|
+# Globals
|
|
|
+#
|
|
|
+gWorkspace = ''
|
|
|
+gBaseToolsPath = ''
|
|
|
+gArgs = None
|
|
|
+
|
|
|
+def LogAlways(Message):
|
|
|
+ sys.stdout.write (__prog__ + ': ' + Message + '\n')
|
|
|
+ sys.stdout.flush()
|
|
|
+
|
|
|
+def Log(Message):
|
|
|
+ global gArgs
|
|
|
+ if not gArgs.Verbose:
|
|
|
+ return
|
|
|
+ sys.stdout.write (__prog__ + ': ' + Message + '\n')
|
|
|
+ sys.stdout.flush()
|
|
|
+
|
|
|
+def Error(Message, ExitValue=1):
|
|
|
+ sys.stderr.write (__prog__ + ': ERROR: ' + Message + '\n')
|
|
|
+ sys.exit (ExitValue)
|
|
|
+
|
|
|
+def RelativePath(target):
|
|
|
+ global gWorkspace
|
|
|
+ Log('RelativePath' + target)
|
|
|
+ return os.path.relpath (target, gWorkspace)
|
|
|
+
|
|
|
+def NormalizePath(target):
|
|
|
+ if isinstance(target, tuple):
|
|
|
+ return os.path.normpath (os.path.join (*target))
|
|
|
+ else:
|
|
|
+ return os.path.normpath (target)
|
|
|
+
|
|
|
+def RemoveFile(target):
|
|
|
+ target = NormalizePath(target)
|
|
|
+ if not target or target == os.pathsep:
|
|
|
+ Error ('RemoveFile() invalid target')
|
|
|
+ if os.path.exists(target):
|
|
|
+ os.remove (target)
|
|
|
+ Log ('remove %s' % (RelativePath (target)))
|
|
|
+
|
|
|
+def RemoveDirectory(target):
|
|
|
+ target = NormalizePath(target)
|
|
|
+ if not target or target == os.pathsep:
|
|
|
+ Error ('RemoveDirectory() invalid target')
|
|
|
+ if os.path.exists(target):
|
|
|
+ Log ('rmdir %s' % (RelativePath (target)))
|
|
|
+ shutil.rmtree(target)
|
|
|
+
|
|
|
+def CreateDirectory(target):
|
|
|
+ target = NormalizePath(target)
|
|
|
+ if not os.path.exists(target):
|
|
|
+ Log ('mkdir %s' % (RelativePath (target)))
|
|
|
+ os.makedirs (target)
|
|
|
+
|
|
|
+def Copy(src, dst):
|
|
|
+ src = NormalizePath(src)
|
|
|
+ dst = NormalizePath(dst)
|
|
|
+ for File in glob.glob(src):
|
|
|
+ Log ('copy %s -> %s' % (RelativePath (File), RelativePath (dst)))
|
|
|
+ shutil.copy (File, dst)
|
|
|
+
|
|
|
+GenerateCapsuleCommand = '''
|
|
|
+GenerateCapsule
|
|
|
+--encode
|
|
|
+--guid {FMP_CAPSULE_GUID}
|
|
|
+--fw-version {FMP_CAPSULE_VERSION}
|
|
|
+--lsv {FMP_CAPSULE_LSV}
|
|
|
+--capflag PersistAcrossReset
|
|
|
+--capflag InitiateReset
|
|
|
+--signer-private-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestCert.pem
|
|
|
+--other-public-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestSub.pub.pem
|
|
|
+--trusted-public-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestRoot.pub.pem
|
|
|
+-o {FMP_CAPSULE_FILE}
|
|
|
+{FMP_CAPSULE_PAYLOAD}
|
|
|
+'''
|
|
|
+MetaInfoXmlTemplate = '''
|
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
|
+<component type="firmware">
|
|
|
+ <id>com.intel.FMP_CAPSULE_BASE_NAME.firmware</id>
|
|
|
+ <name>FMP_CAPSULE_BASE_NAME</name>
|
|
|
+ <summary>System firmware for the FMP_CAPSULE_BASE_NAME</summary>
|
|
|
+ <description>
|
|
|
+ Description of System firmware for the FMP_CAPSULE_BASE_NAME
|
|
|
+ </description>
|
|
|
+ <provides>
|
|
|
+ <firmware type="flashed">FMP_CAPSULE_GUID</firmware>
|
|
|
+ </provides>
|
|
|
+ <url type="homepage">http://www.tianocore.org</url>
|
|
|
+ <metadata_license>CC0-1.0</metadata_license>
|
|
|
+ <project_license>BSD</project_license>
|
|
|
+ <developer_name>Tianocore</developer_name>
|
|
|
+ <releases>
|
|
|
+ <release version="FMP_CAPSULE_VERSION_DECIMAL" date="FMP_CAPSULE_DATE">
|
|
|
+ <description>
|
|
|
+ Build FMP_CAPSULE_STRING
|
|
|
+ </description>
|
|
|
+ </release>
|
|
|
+ </releases>
|
|
|
+ <!-- most OEMs do not need to do this... -->
|
|
|
+ <custom>
|
|
|
+ <value key="LVFS::InhibitDownload"/>
|
|
|
+ </custom>
|
|
|
+</component>
|
|
|
+'''
|
|
|
+
|
|
|
+LvfsDdfTemplate = '''
|
|
|
+.OPTION EXPLICIT ; Generate errors on variable typos
|
|
|
+
|
|
|
+.Set CabinetNameTemplate=firmware.cab ; The name of the file
|
|
|
+.set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory
|
|
|
+.Set Cabinet=on ;
|
|
|
+.Set Compress=on ;
|
|
|
+.Set DiskDirectory1=.
|
|
|
+.Set MaxDiskSize=99999744 ; multiple of 512
|
|
|
+
|
|
|
+;*** Files to zip ;
|
|
|
+;
|
|
|
+firmware.bin
|
|
|
+firmware.metainfo.xml
|
|
|
+;***
|
|
|
+'''
|
|
|
+
|
|
|
+def GenCapsuleDevice (BaseName, PayloadFileName, Guid, Version, Lsv, CapsulesPath, CapsulesSubDir):
|
|
|
+ global gBaseToolsPath
|
|
|
+ LogAlways ('Generate Capsule: {0} {1:08x} {2:08x} {3}'.format (Guid, Version, Lsv, PayloadFileName))
|
|
|
+
|
|
|
+ VersionString = '.'.join([str(ord(x)) for x in struct.pack('>I', Version).decode()])
|
|
|
+
|
|
|
+ FmpCapsuleFile = NormalizePath ((CapsulesPath, CapsulesSubDir, BaseName + '.' + VersionString + '.cap'))
|
|
|
+ Command = GenerateCapsuleCommand.format (
|
|
|
+ FMP_CAPSULE_GUID = Guid,
|
|
|
+ FMP_CAPSULE_VERSION = Version,
|
|
|
+ FMP_CAPSULE_LSV = Lsv,
|
|
|
+ BASE_TOOLS_PATH = gBaseToolsPath,
|
|
|
+ FMP_CAPSULE_FILE = FmpCapsuleFile,
|
|
|
+ FMP_CAPSULE_PAYLOAD = PayloadFileName
|
|
|
+ )
|
|
|
+ Command = ' '.join(Command.splitlines()).strip()
|
|
|
+ if gArgs.Verbose:
|
|
|
+ Command = Command + ' -v'
|
|
|
+
|
|
|
+ Log (Command)
|
|
|
+
|
|
|
+ Process = subprocess.Popen(Command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
+ ProcessOutput = Process.communicate()
|
|
|
+
|
|
|
+ if Process.returncode == 0:
|
|
|
+ Log (ProcessOutput[0].decode())
|
|
|
+ else:
|
|
|
+ LogAlways (Command)
|
|
|
+ LogAlways (ProcessOutput[0].decode())
|
|
|
+ Error ('GenerateCapsule returned an error')
|
|
|
+
|
|
|
+ Copy (PayloadFileName, (CapsulesPath, 'firmware.bin'))
|
|
|
+ MetaInfoXml = MetaInfoXmlTemplate
|
|
|
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_GUID', Guid)
|
|
|
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_BASE_NAME', BaseName)
|
|
|
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_VERSION_DECIMAL', str(Version))
|
|
|
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_STRING', VersionString)
|
|
|
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_DATE', str(datetime.date.today()))
|
|
|
+ f = open (NormalizePath ((CapsulesPath, 'firmware.metainfo.xml')), 'w')
|
|
|
+ f.write(MetaInfoXml)
|
|
|
+ f.close()
|
|
|
+
|
|
|
+ f = open (NormalizePath ((CapsulesPath, 'Lvfs.ddf')), 'w')
|
|
|
+ f.write(LvfsDdfTemplate)
|
|
|
+ f.close()
|
|
|
+
|
|
|
+ if sys.platform == "win32":
|
|
|
+ Command = 'makecab /f ' + NormalizePath ((CapsulesPath, 'Lvfs.ddf'))
|
|
|
+ else:
|
|
|
+ Command = 'gcab --create firmware.cab firmware.bin firmware.metainfo.xml'
|
|
|
+ Log (Command)
|
|
|
+
|
|
|
+ Process = subprocess.Popen(Command, cwd=CapsulesPath, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
+ ProcessOutput = Process.communicate()
|
|
|
+
|
|
|
+ if Process.returncode == 0:
|
|
|
+ Log (ProcessOutput[0].decode())
|
|
|
+ else:
|
|
|
+ LogAlways (Command)
|
|
|
+ LogAlways (ProcessOutput[0].decode())
|
|
|
+ Error ('GenerateCapsule returned an error')
|
|
|
+
|
|
|
+ FmpCabinetFile = NormalizePath ((CapsulesPath, CapsulesSubDir, BaseName + '.' + VersionString + '.cab'))
|
|
|
+
|
|
|
+ Copy ((CapsulesPath, 'firmware.cab'), FmpCabinetFile)
|
|
|
+
|
|
|
+ RemoveFile ((CapsulesPath, 'firmware.cab'))
|
|
|
+ RemoveFile ((CapsulesPath, 'setup.inf'))
|
|
|
+ RemoveFile ((CapsulesPath, 'setup.rpt'))
|
|
|
+ RemoveFile ((CapsulesPath, 'Lvfs.ddf'))
|
|
|
+ RemoveFile ((CapsulesPath, 'firmware.metainfo.xml'))
|
|
|
+ RemoveFile ((CapsulesPath, 'firmware.bin'))
|
|
|
+
|
|
|
+def GenCapsuleSampleDevice (SampleDeviceName, Guid, Version, Lsv, CapsulesPath, CapsulesSubDir):
|
|
|
+ BinaryPayload = SampleDeviceName.encode() + bytearray(0x18 - len (SampleDeviceName.encode()))
|
|
|
+ BinaryPayload = BinaryPayload + struct.pack('<I', Version)
|
|
|
+ BinaryPayload = BinaryPayload + struct.pack('<I', Lsv)
|
|
|
+
|
|
|
+ PayloadFileName = NormalizePath ((CapsulesPath, SampleDeviceName + '.bin'))
|
|
|
+ f = open (PayloadFileName, 'wb')
|
|
|
+ f.write(BinaryPayload)
|
|
|
+ f.close()
|
|
|
+
|
|
|
+ GenCapsuleDevice(SampleDeviceName, PayloadFileName, Guid, Version, Lsv, CapsulesPath, CapsulesSubDir)
|
|
|
+
|
|
|
+ RemoveFile (PayloadFileName)
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ #
|
|
|
+ # Create command line argument parser object
|
|
|
+ #
|
|
|
+ parser = argparse.ArgumentParser (
|
|
|
+ prog = __prog__,
|
|
|
+ description = __description__ + __copyright__,
|
|
|
+ conflict_handler = 'resolve'
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-a', '--arch', dest = 'Arch', nargs = '+', action = 'append',
|
|
|
+ required = True,
|
|
|
+ help = '''ARCHS is one of list: IA32, X64, IPF, ARM, AARCH64 or EBC,
|
|
|
+ which overrides target.txt's TARGET_ARCH definition. To
|
|
|
+ specify more archs, please repeat this option.'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-t', '--tagname', dest = 'ToolChain', required = True,
|
|
|
+ help = '''Using the Tool Chain Tagname to build the platform,
|
|
|
+ overriding target.txt's TOOL_CHAIN_TAG definition.'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-p', '--platform', dest = 'PlatformFile', required = True,
|
|
|
+ help = '''Build the platform specified by the DSC file name argument,
|
|
|
+ overriding target.txt's ACTIVE_PLATFORM definition.'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-b', '--buildtarget', dest = 'BuildTarget', required = True,
|
|
|
+ help = '''Using the TARGET to build the platform, overriding
|
|
|
+ target.txt's TARGET definition.'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '--conf=', dest = 'ConfDirectory', required = True,
|
|
|
+ help = '''Specify the customized Conf directory.'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-D', '--define', dest = 'Define', nargs='*', action = 'append',
|
|
|
+ help = '''Macro: "Name [= Value]".'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '-v', '--verbose', dest = 'Verbose', action = 'store_true',
|
|
|
+ help = '''Turn on verbose output with informational messages printed'''
|
|
|
+ )
|
|
|
+ parser.add_argument (
|
|
|
+ '--package', dest = 'Package', nargs = '*', action = 'append',
|
|
|
+ help = '''The directory name of a package of tests to copy'''
|
|
|
+ )
|
|
|
+
|
|
|
+ #
|
|
|
+ # Parse command line arguments
|
|
|
+ #
|
|
|
+ gArgs, remaining = parser.parse_known_args()
|
|
|
+ gArgs.BuildType = 'all'
|
|
|
+ for BuildType in ['all', 'fds', 'genc', 'genmake', 'clean', 'cleanall', 'modules', 'libraries', 'run']:
|
|
|
+ if BuildType in remaining:
|
|
|
+ gArgs.BuildType = BuildType
|
|
|
+ remaining.remove(BuildType)
|
|
|
+ break
|
|
|
+ gArgs.Remaining = ' '.join(remaining)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Get WORKSPACE environment variable
|
|
|
+ #
|
|
|
+ try:
|
|
|
+ gWorkspace = os.environ['WORKSPACE']
|
|
|
+ except:
|
|
|
+ Error ('WORKSPACE environment variable not set')
|
|
|
+
|
|
|
+ #
|
|
|
+ # Get PACKAGES_PATH and generate prioritized list of paths
|
|
|
+ #
|
|
|
+ PathList = [gWorkspace]
|
|
|
+ try:
|
|
|
+ PathList += os.environ['PACKAGES_PATH'].split(os.pathsep)
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ #
|
|
|
+ # Determine full path to BaseTools
|
|
|
+ #
|
|
|
+ for Path in PathList:
|
|
|
+ if os.path.exists (os.path.join (Path, 'BaseTools')):
|
|
|
+ gBaseToolsPath = os.path.join (Path, 'BaseTools')
|
|
|
+ break
|
|
|
+
|
|
|
+ #
|
|
|
+ # Parse OUTPUT_DIRECTORY from DSC file
|
|
|
+ #
|
|
|
+ for Path in PathList:
|
|
|
+ if os.path.exists (os.path.join (Path, gArgs.PlatformFile)):
|
|
|
+ Dsc = open (os.path.join (Path, gArgs.PlatformFile), 'r').readlines()
|
|
|
+ break
|
|
|
+ for Line in Dsc:
|
|
|
+ if Line.strip().startswith('OUTPUT_DIRECTORY'):
|
|
|
+ OutputDirectory = Line.strip().split('=')[1].strip()
|
|
|
+ break
|
|
|
+
|
|
|
+ #
|
|
|
+ # Determine full paths to EDK II build directory, EDK II build output
|
|
|
+ # directory and the CPU arch of the UEFI phase.
|
|
|
+ #
|
|
|
+ CommandDir = os.path.dirname(sys.argv[0])
|
|
|
+ EdkiiBuildDir = os.path.join (gWorkspace, OutputDirectory)
|
|
|
+ EdkiiBuildOutput = os.path.join (EdkiiBuildDir, gArgs.BuildTarget + '_' + gArgs.ToolChain)
|
|
|
+ UefiArch = gArgs.Arch[0][0]
|
|
|
+ if len (gArgs.Arch) > 1:
|
|
|
+ if ['X64'] in gArgs.Arch:
|
|
|
+ UefiArch = 'X64'
|
|
|
+
|
|
|
+ CapsulesPath = NormalizePath((EdkiiBuildDir, 'Capsules'))
|
|
|
+
|
|
|
+ CapsulesSubDir = 'TestCert' + '_' + UefiArch + '_' + gArgs.BuildTarget + '_' + gArgs.ToolChain
|
|
|
+
|
|
|
+ #
|
|
|
+ # Create output directories
|
|
|
+ #
|
|
|
+ try:
|
|
|
+ CreateDirectory ((CapsulesPath))
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ CreateDirectory ((CapsulesPath, CapsulesSubDir))
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ #
|
|
|
+ # Copy CapsuleApp
|
|
|
+ #
|
|
|
+ Copy ((EdkiiBuildOutput, UefiArch, 'CapsuleApp.efi'), (CapsulesPath, CapsulesSubDir))
|
|
|
+
|
|
|
+ #
|
|
|
+ # Generate capsules for the Red Sample Device
|
|
|
+ #
|
|
|
+ GenCapsuleSampleDevice('Red','72E2945A-00DA-448E-9AA7-075AD840F9D4',0x00000010,0x00000000, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Red','72E2945A-00DA-448E-9AA7-075AD840F9D4',0x00000011,0x00000000, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Red','72E2945A-00DA-448E-9AA7-075AD840F9D4',0x00000012,0x00000000, CapsulesPath, CapsulesSubDir)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Generate capsules for the Green Sample Device
|
|
|
+ #
|
|
|
+ GenCapsuleSampleDevice('Green','79179BFD-704D-4C90-9E02-0AB8D968C18A',0x00000020,0x00000020, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Green','79179BFD-704D-4C90-9E02-0AB8D968C18A',0x00000021,0x00000020, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Green','79179BFD-704D-4C90-9E02-0AB8D968C18A',0x00000022,0x00000020, CapsulesPath, CapsulesSubDir)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Generate capsules for the Blue Sample Device
|
|
|
+ #
|
|
|
+ GenCapsuleSampleDevice('Blue','149DA854-7D19-4FAA-A91E-862EA1324BE6',0x00000010,0x00000000, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Blue','149DA854-7D19-4FAA-A91E-862EA1324BE6',0x00000011,0x00000000, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Blue','149DA854-7D19-4FAA-A91E-862EA1324BE6',0x00000012,0x00000012, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Blue','149DA854-7D19-4FAA-A91E-862EA1324BE6',0x00000013,0x00000012, CapsulesPath, CapsulesSubDir)
|
|
|
+ GenCapsuleSampleDevice('Blue','149DA854-7D19-4FAA-A91E-862EA1324BE6',0x00000014,0x00000012, CapsulesPath, CapsulesSubDir)
|
|
|
+
|
|
|
+ #
|
|
|
+ # Generate capsules for Minnow Max Firmware Updates
|
|
|
+ #
|
|
|
+ RomFileName = os.path.join (EdkiiBuildOutput, 'FV', 'VLV.fd')
|
|
|
+ GenCapsuleDevice('MinnowMax', RomFileName,'4096267B-DA0A-42EB-B5EB-FEF31D207CB4',0x0000000C,0x00000000, CapsulesPath, CapsulesSubDir)
|