From: "Gao, Liming" <liming.gao@intel.com>
To: "Kinney, Michael D" <michael.d.kinney@intel.com>,
"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: Sean Brogan <sean.brogan@microsoft.com>,
"Yao, Jiewen" <jiewen.yao@intel.com>,
"Zhu, Yonghong" <yonghong.zhu@intel.com>
Subject: Re: [RFC 1/1] BaseTools/Capsule: Add Capsule Generation Tools
Date: Thu, 31 May 2018 14:25:34 +0000 [thread overview]
Message-ID: <4A89E2EF3DFEDB4C8BFDE51014F606A14E291226@SHSMSX104.ccr.corp.intel.com> (raw)
In-Reply-To: <20180529225635.22536-2-michael.d.kinney@intel.com>
Mike:
Which C tool is converted? Does it support all capsule generation functionality in GenFv tool?
And, GenFds has the logic to generate FMP capsule. Can they share same logic?
Thanks
Liming
> -----Original Message-----
> From: Kinney, Michael D
> Sent: Wednesday, May 30, 2018 6:57 AM
> To: edk2-devel@lists.01.org
> Cc: Kinney, Michael D <michael.d.kinney@intel.com>; Sean Brogan <sean.brogan@microsoft.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Zhu, Yonghong <yonghong.zhu@intel.com>; Gao, Liming <liming.gao@intel.com>
> Subject: [RFC 1/1] BaseTools/Capsule: Add Capsule Generation Tools
>
> From: "Kinney, Michael D" <michael.d.kinney@intel.com>
>
> https://bugzilla.tianocore.org/show_bug.cgi?id=945
>
> Based on content from the following branch
>
> https://github.com/Microsoft/MS_UEFI/tree/share/beta/CapsuleTools
>
> * Convert C tools to Python
> * Add common python modules to:
> BaseTools/Source/Python/Common/Uefi/Capsule
> BaseTools/Source/Python/Common/Edk2/Capsule
> * Add GenerateCapsule.py to BaseTools/Source/Python/Capsule
> * Add Windows and Posix wrappers for GenerateCapsule.py
>
> usage: GenerateCapsule [-h] [-o OUTPUTFILE] (-e | -d | --dump-info)
> [--capflag {PersistAcrossReset,PopulateSystemTable,InitiateReset}]
> [--capoemflag CAPSULEOEMFLAG] [--guid GUID]
> [--hardware-instance HARDWAREINSTANCE]
> [--monotonic-count MONOTONICCOUNT]
> [--version FWVERSION] [--lsv LOWESTSUPPORTEDVERSION]
> [--pfx-file SIGNTOOLPFXFILE]
> [--signer-private-cert OPENSSLSIGNERPRIVATECERTFILE]
> [--other-public-cert OPENSSLOTHERPUBLICCERTFILE]
> [--trusted-public-cert OPENSSLTRUSTEDPUBLICCERTFILE]
> [--signing-tool-path SIGNINGTOOLPATH] [-v] [-q]
> [--debug [0-9]]
> InputFile
>
> Generate a capsule. Copyright (c) 2018, Intel Corporation. All rights
> reserved.
>
> positional arguments:
> InputFile Input binary payload filename.
>
> optional arguments:
> -h, --help show this help message and exit
> -o OUTPUTFILE, --output OUTPUTFILE
> Output filename.
> -e, --encode Encode file
> -d, --decode Decode file
> --dump-info Display FMP Payload Header information
> --capflag {PersistAcrossReset,PopulateSystemTable,InitiateReset}
> Capsule flag can be PersistAcrossReset, or
> PopulateSystemTable or InitiateReset or not set
> --capoemflag CAPSULEOEMFLAG
> Capsule OEM Flag is an integer between 0x0000 and
> 0xffff.
> --guid GUID The FMP/ESRT GUID in registry format. Required for
> encode operations.
> --hardware-instance HARDWAREINSTANCE
> The 64-bit hardware instance. The default is
> 0x0000000000000000
> --monotonic-count MONOTONICCOUNT
> 64-bit monotonic count value in header. Default is
> 0x0000000000000000.
> --version FWVERSION The 32-bit version of the binary payload (e.g.
> 0x11223344 or 5678).
> --lsv LOWESTSUPPORTEDVERSION
> The 32-bit lowest supported version of the binary
> payload (e.g. 0x11223344 or 5678).
> --pfx-file SIGNTOOLPFXFILE
> signtool PFX certificate filename.
> --signer-private-cert OPENSSLSIGNERPRIVATECERTFILE
> OpenSSL signer private certificate filename.
> --other-public-cert OPENSSLOTHERPUBLICCERTFILE
> OpenSSL other public certificate filename.
> --trusted-public-cert OPENSSLTRUSTEDPUBLICCERTFILE
> OpenSSL trusted public certificate filename.
> --signing-tool-path SIGNINGTOOLPATH
> Path to signtool or Open SLL tool. Optional if path to
> tools are already in PATH.
> -v, --verbose Increase output messages
> -q, --quiet Reduce output messages
> --debug [0-9] Set debug level
>
> Cc: Sean Brogan <sean.brogan@microsoft.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Yonghong Zhu <yonghong.zhu@intel.com>
> Cc: Liming Gao <liming.gao@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
> ---
> BaseTools/BinWrappers/PosixLike/GenerateCapsule | 14 +
> .../BinWrappers/WindowsLike/GenerateCapsule.bat | 1 +
> BaseTools/Source/Python/Capsule/GenerateCapsule.py | 500 +++++++++++++++++++++
> .../Python/Common/Edk2/Capsule/FmpPayloadHeader.py | 91 ++++
> .../Source/Python/Common/Edk2/Capsule/__init__.py | 15 +
> BaseTools/Source/Python/Common/Edk2/__init__.py | 15 +
> .../Python/Common/Uefi/Capsule/FmpAuthHeader.py | 184 ++++++++
> .../Python/Common/Uefi/Capsule/FmpCapsuleHeader.py | 302 +++++++++++++
> .../Common/Uefi/Capsule/UefiCapsuleHeader.py | 136 ++++++
> .../Source/Python/Common/Uefi/Capsule/__init__.py | 15 +
> BaseTools/Source/Python/Common/Uefi/__init__.py | 15 +
> 11 files changed, 1288 insertions(+)
> create mode 100644 BaseTools/BinWrappers/PosixLike/GenerateCapsule
> create mode 100644 BaseTools/BinWrappers/WindowsLike/GenerateCapsule.bat
> create mode 100644 BaseTools/Source/Python/Capsule/GenerateCapsule.py
> create mode 100644 BaseTools/Source/Python/Common/Edk2/Capsule/FmpPayloadHeader.py
> create mode 100644 BaseTools/Source/Python/Common/Edk2/Capsule/__init__.py
> create mode 100644 BaseTools/Source/Python/Common/Edk2/__init__.py
> create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/FmpAuthHeader.py
> create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py
> create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsuleHeader.py
> create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/__init__.py
> create mode 100644 BaseTools/Source/Python/Common/Uefi/__init__.py
>
> diff --git a/BaseTools/BinWrappers/PosixLike/GenerateCapsule b/BaseTools/BinWrappers/PosixLike/GenerateCapsule
> new file mode 100644
> index 0000000000..59a6c8ba43
> --- /dev/null
> +++ b/BaseTools/BinWrappers/PosixLike/GenerateCapsule
> @@ -0,0 +1,14 @@
> +#!/usr/bin/env bash
> +#python `dirname $0`/RunToolFromSource.py `basename $0` $*
> +
> +# If a python2 command is available, use it in preference to python
> +if command -v python2 >/dev/null 2>&1; then
> + python_exe=python2
> +fi
> +
> +full_cmd=${BASH_SOURCE:-$0} # see http://mywiki.wooledge.org/BashFAQ/028 for a discussion of why $0 is not a good choice here
> +dir=$(dirname "$full_cmd")
> +cmd=${full_cmd##*/}
> +
> +export PYTHONPATH="$dir/../../Source/Python${PYTHONPATH:+:"$PYTHONPATH"}"
> +exec "${python_exe:-python}" "$dir/../../Source/Python/Capsule/$cmd.py" "$@"
> diff --git a/BaseTools/BinWrappers/WindowsLike/GenerateCapsule.bat b/BaseTools/BinWrappers/WindowsLike/GenerateCapsule.bat
> new file mode 100644
> index 0000000000..ca442d181b
> --- /dev/null
> +++ b/BaseTools/BinWrappers/WindowsLike/GenerateCapsule.bat
> @@ -0,0 +1 @@
> +@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\Capsule\GenerateCapsule.py %*
> diff --git a/BaseTools/Source/Python/Capsule/GenerateCapsule.py b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
> new file mode 100644
> index 0000000000..32ab95c3e0
> --- /dev/null
> +++ b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
> @@ -0,0 +1,500 @@
> +## @file
> +# Generate a capsule.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +
> +'''
> +GenerateCapsule
> +'''
> +
> +import sys
> +import argparse
> +import uuid
> +import struct
> +import subprocess
> +import os
> +import tempfile
> +import shutil
> +from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass
> +from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass
> +from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass
> +from Common.Edk2.Capsule.FmpPayloadHeader import FmpPayloadHeaderClass
> +
> +#
> +# Globals for help information
> +#
> +__prog__ = 'GenerateCapsule'
> +__copyright__ = 'Copyright (c) 2018, Intel Corporation. All rights reserved.'
> +__description__ = 'Generate a capsule.\n'
> +
> +def SignPayloadSignTool (Payload, ToolPath, PfxFile):
> + #
> + # Create a temporary directory
> + #
> + TempDirectoryName = tempfile.mkdtemp()
> +
> + #
> + # Generate temp file name for the payload contents
> + #
> + TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')
> +
> + #
> + # Create temporary payload file for signing
> + #
> + try:
> + File = open (TempFileName, mode='wb')
> + File.write (Payload)
> + File.close ()
> + except:
> + shutil.rmtree (TempDirectoryName)
> + raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
> +
> + #
> + # Build signtool command
> + #
> + if ToolPath is None:
> + ToolPath = ''
> + Command = ''
> + Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'signtool.exe'))
> + Command = Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '
> + Command = Command + '/p7 {TempDir} '.format (TempDir = TempDirectoryName)
> + Command = Command + '/f {PfxFile} '.format (PfxFile = PfxFile)
> + Command = Command + TempFileName
> +
> + #
> + # Sign the input file using the specified private key
> + #
> + try:
> + Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell =
> True)
> + Result = Process.communicate('')
> + except:
> + shutil.rmtree (TempDirectoryName)
> + raise ValueError ('GenerateCapsule: error: can not run signtool.')
> +
> + if Process.returncode != 0:
> + shutil.rmtree (TempDirectoryName)
> + print (Result[1].decode())
> + raise ValueError ('GenerateCapsule: error: signtool failed.')
> +
> + #
> + # Read the signature from the generated output file
> + #
> + try:
> + File = open (TempFileName + '.p7', mode='rb')
> + Signature = File.read ()
> + File.close ()
> + except:
> + shutil.rmtree (TempDirectoryName)
> + raise ValueError ('GenerateCapsule: error: can not read signature file.')
> +
> + shutil.rmtree (TempDirectoryName)
> + return Signature
> +
> +def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile):
> + print ('signtool verify is not supported.')
> + raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')
> +
> +def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile):
> + #
> + # Build openssl command
> + #
> + if ToolPath is None:
> + ToolPath = ''
> + Command = ''
> + Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))
> + Command = Command + 'smime -sign -binary -outform DER -md sha256 '
> + Command = Command + '-signer "{Private}" -certfile "{Public}"'.format (Private = SignerPrivateCertFile, Public =
> OtherPublicCertFile)
> +
> + #
> + # Sign the input file using the specified private key and capture signature from STDOUT
> + #
> + try:
> + Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell =
> True)
> + Result = Process.communicate(input = Payload)
> + Signature = Result[0]
> + except:
> + raise ValueError ('GenerateCapsule: error: can not run openssl.')
> +
> + if Process.returncode != 0:
> + print (Result[1].decode())
> + raise ValueError ('GenerateCapsule: error: openssl failed.')
> +
> + return Signature
> +
> +def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile):
> + #
> + # Create a temporary directory
> + #
> + TempDirectoryName = tempfile.mkdtemp()
> +
> + #
> + # Generate temp file name for the payload contents
> + #
> + TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')
> +
> + #
> + # Create temporary payload file for verification
> + #
> + try:
> + File = open (TempFileName, mode='wb')
> + File.write (Payload)
> + File.close ()
> + except:
> + shutil.rmtree (TempDirectoryName)
> + raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
> +
> + #
> + # Build openssl command
> + #
> + if ToolPath is None:
> + ToolPath = ''
> + Command = ''
> + Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))
> + Command = Command + 'smime -verify -inform DER '
> + Command = Command + '-content {Content} -CAfile "{Public}"'.format (Content = TempFileName, Public = TrustedPublicCertFile)
> +
> + #
> + # Verify signature
> + #
> + try:
> + Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell =
> True)
> + Result = Process.communicate(input = CertData)
> + except:
> + shutil.rmtree (TempDirectoryName)
> + raise ValueError ('GenerateCapsule: error: can not run openssl.')
> +
> + if Process.returncode != 0:
> + shutil.rmtree (TempDirectoryName)
> + print (Result[1].decode())
> + raise ValueError ('GenerateCapsule: error: openssl failed.')
> +
> + shutil.rmtree (TempDirectoryName)
> + return Payload
> +
> +if __name__ == '__main__':
> + def convert_arg_line_to_args(arg_line):
> + for arg in arg_line.split():
> + if not arg.strip():
> + continue
> + yield arg
> +
> + def ValidateUnsignedInteger (Argument):
> + try:
> + Value = int (Argument, 0)
> + except:
> + Message = '{Argument} is not a valid integer value.'.format (Argument = Argument)
> + raise argparse.ArgumentTypeError (Message)
> + if Value < 0:
> + Message = '{Argument} is a negative value.'.format (Argument = Argument)
> + raise argparse.ArgumentTypeError (Message)
> + return Value
> +
> + def ValidateRegistryFormatGuid (Argument):
> + try:
> + Value = uuid.UUID (Argument)
> + except:
> + Message = '{Argument} is not a valid registry format GUID value.'.format (Argument = Argument)
> + raise argparse.ArgumentTypeError (Message)
> + return Value
> +
> + #
> + # Create command line argument parser object
> + #
> + parser = argparse.ArgumentParser (
> + prog = __prog__,
> + description = __description__ + __copyright__,
> + conflict_handler = 'resolve',
> + fromfile_prefix_chars = '@'
> + )
> + parser.convert_arg_line_to_args = convert_arg_line_to_args
> +
> + #
> + # Add input and output file arguments
> + #
> + parser.add_argument("InputFile", type = argparse.FileType('rb'),
> + help = "Input binary payload filename.")
> + parser.add_argument("-o", "--output", dest = 'OutputFile', type = argparse.FileType('wb'),
> + help = "Output filename.")
> + #
> + # Add group for -e and -d flags that are mutually exclusive and required
> + #
> + group = parser.add_mutually_exclusive_group (required = True)
> + group.add_argument ("-e", "--encode", dest = 'Encode', action = "store_true",
> + help = "Encode file")
> + group.add_argument ("-d", "--decode", dest = 'Decode', action = "store_true",
> + help = "Decode file")
> + group.add_argument ("--dump-info", dest = 'DumpInfo', action = "store_true",
> + help = "Display FMP Payload Header information")
> + #
> + # Add optional arguments for this command
> + #
> + parser.add_argument ("--capflag", dest = 'CapsuleFlag', action='append', default = [],
> + choices=['PersistAcrossReset', 'PopulateSystemTable', 'InitiateReset'],
> + help = "Capsule flag can be PersistAcrossReset, or PopulateSystemTable or InitiateReset or not set")
> + parser.add_argument ("--capoemflag", dest = 'CapsuleOemFlag', type = ValidateUnsignedInteger, default = 0x0000,
> + help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")
> +
> + parser.add_argument ("--guid", dest = 'Guid', type = ValidateRegistryFormatGuid,
> + help = "The FMP/ESRT GUID in registry format. Required for encode operations.")
> + parser.add_argument ("--hardware-instance", dest = 'HardwareInstance', type = ValidateUnsignedInteger, default =
> 0x0000000000000000,
> + help = "The 64-bit hardware instance. The default is 0x0000000000000000")
> +
> +
> + parser.add_argument ("--monotonic-count", dest = 'MonotonicCount', type = ValidateUnsignedInteger, default =
> 0x0000000000000000,
> + help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")
> +
> + parser.add_argument ("--version", dest = 'FwVersion', type = ValidateUnsignedInteger,
> + help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678).")
> + parser.add_argument ("--lsv", dest = 'LowestSupportedVersion', type = ValidateUnsignedInteger,
> + help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678).")
> +
> + parser.add_argument ("--pfx-file", dest='SignToolPfxFile', type=argparse.FileType('rb'),
> + help="signtool PFX certificate filename.")
> +
> + parser.add_argument ("--signer-private-cert", dest='OpenSslSignerPrivateCertFile', type=argparse.FileType('rb'),
> + help="OpenSSL signer private certificate filename.")
> + parser.add_argument ("--other-public-cert", dest='OpenSslOtherPublicCertFile', type=argparse.FileType('rb'),
> + help="OpenSSL other public certificate filename.")
> + parser.add_argument ("--trusted-public-cert", dest='OpenSslTrustedPublicCertFile', type=argparse.FileType('rb'),
> + help="OpenSSL trusted public certificate filename.")
> +
> + parser.add_argument ("--signing-tool-path", dest = 'SigningToolPath',
> + help = "Path to signtool or Open SLL tool. Optional if path to tools are already in PATH.")
> +
> + #
> + # Add optional arguments common to all commands
> + #
> + parser.add_argument("-v", "--verbose", dest = 'Verbose', action = "store_true",
> + help = "Increase output messages")
> + parser.add_argument("-q", "--quiet", dest = 'Quiet', action = "store_true",
> + help = "Reduce output messages")
> + parser.add_argument("--debug", dest = 'Debug', type = int, metavar = '[0-9]', choices = range(0,10), default = 0,
> + help = "Set debug level")
> +
> + #
> + # Parse command line arguments
> + #
> + args = parser.parse_args()
> +
> + #
> + # Perform additional argument verification
> + #
> + if not args.DumpInfo and args.OutputFile is None:
> + parser.error ('the following arguments are required for all commands except --dump-info: --output')
> +
> + if args.Encode and (args.Guid is None or args.FwVersion is None or args.LowestSupportedVersion is None):
> + parser.error ('the following arguments are required: --version, --lsv, --guid')
> +
> + if not args.DumpInfo and not args.OutputFile:
> + parser.error ('the following arguments are required: --output')
> +
> + if not args.DumpInfo:
> + if args.SignToolPfxFile is None and args.OpenSslSignerPrivateCertFile is None and args.OpenSslOtherPublicCertFile is None
> and args.OpenSslTrustedPublicCertFile is None:
> + parser.error ('certificate file arguments are required: --pfx-file | [--signer-private-cert --other-public-cert
> --trusted-public-cert]')
> +
> + if args.SignToolPfxFile is not None:
> + if args.OpenSslSignerPrivateCertFile is not None:
> + parser.error ('Providing both signtool and OpenSSL options is not supported')
> + if args.OpenSslOtherPublicCertFile is not None:
> + parser.error ('Providing both signtool and OpenSSL options is not supported')
> + if args.OpenSslTrustedPublicCertFile is not None:
> + parser.error ('Providing both signtool and OpenSSL options is not supported')
> + args.SignToolPfxFile.close()
> + args.SignToolPfxFile = args.SignToolPfxFile.name
> +
> + if not args.DumpInfo:
> + if args.SignToolPfxFile is None:
> + if args.OpenSslSignerPrivateCertFile is None:
> + parser.error ('the following arguments are required: --signer-private-cert')
> + if args.OpenSslOtherPublicCertFile is None:
> + parser.error ('the following arguments are required: --other-public-cert')
> + if args.OpenSslTrustedPublicCertFile is None:
> + parser.error ('the following arguments are required: --trusted-public-cert')
> + args.OpenSslSignerPrivateCertFile.close()
> + args.OpenSslOtherPublicCertFile.close()
> + args.OpenSslTrustedPublicCertFile.close()
> + args.OpenSslSignerPrivateCertFile = args.OpenSslSignerPrivateCertFile.name
> + args.OpenSslOtherPublicCertFile = args.OpenSslOtherPublicCertFile.name
> + args.OpenSslTrustedPublicCertFile = args.OpenSslTrustedPublicCertFile.name
> +
> + #
> + # Read binary input file
> + #
> + try:
> + if args.Verbose:
> + print ('Read binary input file {File}'.format (File = args.InputFile.name))
> + Buffer = args.InputFile.read ()
> + args.InputFile.close ()
> + except:
> + print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = args.InputFile.name))
> + sys.exit (1)
> +
> + #
> + # Create objects
> + #
> + UefiCapsuleHeader = UefiCapsuleHeaderClass ()
> + FmpCapsuleHeader = FmpCapsuleHeaderClass ()
> + FmpAuthHeader = FmpAuthHeaderClass ()
> + FmpPayloadHeader = FmpPayloadHeaderClass ()
> +
> + if args.Encode:
> + try:
> + FmpPayloadHeader.FwVersion = args.FwVersion
> + FmpPayloadHeader.LowestSupportedVersion = args.LowestSupportedVersion
> + FmpPayloadHeader.Payload = Buffer
> + Result = FmpPayloadHeader.Encode ()
> + if args.Verbose:
> + FmpPayloadHeader.DumpInfo ()
> + except:
> + print ('GenerateCapsule: error: can not encode FMP Payload Header')
> + sys.exit (1)
> +
> + #
> + # Sign image with 64-bit MonotonicCount appended to end of image
> + #
> + try:
> + if args.SignToolPfxFile is not None:
> + CertData = SignPayloadSignTool (
> + Result + struct.pack ('<Q', args.MonotonicCount),
> + args.SigningToolPath,
> + args.SignToolPfxFile
> + )
> + else:
> + CertData = SignPayloadOpenSsl (
> + Result + struct.pack ('<Q', args.MonotonicCount),
> + args.SigningToolPath,
> + args.OpenSslSignerPrivateCertFile,
> + args.OpenSslOtherPublicCertFile,
> + args.OpenSslTrustedPublicCertFile
> + )
> + except:
> + print ('GenerateCapsule: error: can not sign payload')
> + raise
> + sys.exit (1)
> +
> + try:
> + FmpAuthHeader.MonotonicCount = args.MonotonicCount
> + FmpAuthHeader.CertData = CertData
> + FmpAuthHeader.Payload = Result
> + Result = FmpAuthHeader.Encode ()
> + if args.Verbose:
> + FmpAuthHeader.DumpInfo ()
> + except:
> + print ('GenerateCapsule: error: can not encode FMP Auth Header')
> + sys.exit (1)
> +
> + try:
> + FmpCapsuleHeader.AddPayload (args.Guid, Result, HardwareInstance = args.HardwareInstance)
> + Result = FmpCapsuleHeader.Encode ()
> + if args.Verbose:
> + FmpCapsuleHeader.DumpInfo ()
> + except:
> + print ('GenerateCapsule: error: can not encode FMP Capsule Header')
> + sys.exit (1)
> +
> + try:
> + UefiCapsuleHeader.OemFlags = args.CapsuleOemFlag
> + UefiCapsuleHeader.PersistAcrossReset = 'PersistAcrossReset' in args.CapsuleFlag
> + UefiCapsuleHeader.PopulateSystemTable = 'PopulateSystemTable' in args.CapsuleFlag
> + UefiCapsuleHeader.InitiateReset = 'InitiateReset' in args.CapsuleFlag
> + UefiCapsuleHeader.Payload = Result
> + Result = UefiCapsuleHeader.Encode ()
> + if args.Verbose:
> + UefiCapsuleHeader.DumpInfo ()
> + except:
> + print ('GenerateCapsule: error: can not encode UEFI Capsule Header')
> + sys.exit (1)
> +
> + elif args.Decode:
> + try:
> + Result = UefiCapsuleHeader.Decode (Buffer)
> + FmpCapsuleHeader.Decode (Result)
> + Result = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload
> + Result = FmpAuthHeader.Decode (Result)
> +
> + #
> + # Verify Image with 64-bit MonotonicCount appended to end of image
> + #
> + try:
> + if args.SignToolPfxFile is not None:
> + CertData = VerifyPayloadSignTool (
> + FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),
> + FmpAuthHeader.CertData,
> + args.SigningToolPath,
> + args.SignToolPfxFile
> + )
> + else:
> + CertData = VerifyPayloadOpenSsl (
> + FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),
> + FmpAuthHeader.CertData,
> + args.SigningToolPath,
> + args.OpenSslSignerPrivateCertFile,
> + args.OpenSslOtherPublicCertFile,
> + args.OpenSslTrustedPublicCertFile
> + )
> + except ValueError:
> + print ('GenerateCapsule: warning: can not verify payload.')
> +
> + Result = FmpPayloadHeader.Decode (Result)
> + if args.Verbose:
> + print ('========')
> + UefiCapsuleHeader.DumpInfo ()
> + print ('--------')
> + FmpCapsuleHeader.DumpInfo ()
> + print ('--------')
> + FmpAuthHeader.DumpInfo ()
> + print ('--------')
> + FmpPayloadHeader.DumpInfo ()
> + print ('========')
> + except:
> + print ('GenerateCapsule: error: can not decode capsule')
> + raise
> + sys.exit (1)
> +
> + elif args.DumpInfo:
> + try:
> + Result = UefiCapsuleHeader.Decode (Buffer)
> + FmpCapsuleHeader.Decode (Result)
> + Result = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload
> + Result = FmpAuthHeader.Decode (Result)
> + Result = FmpPayloadHeader.Decode (Result)
> +
> + print ('========')
> + UefiCapsuleHeader.DumpInfo ()
> + print ('--------')
> + FmpCapsuleHeader.DumpInfo ()
> + print ('--------')
> + FmpAuthHeader.DumpInfo ()
> + print ('--------')
> + FmpPayloadHeader.DumpInfo ()
> + print ('========')
> + except:
> + print ('GenerateCapsule: error: can not decode capsule')
> + sys.exit (1)
> + else:
> + print('GenerateCapsule: error: invalid options')
> + sys.exit (1)
> +
> + #
> + # Write binary output file
> + #
> + if args.OutputFile is not None:
> + try:
> + if args.Verbose:
> + print ('Write binary output file {File}'.format (File = args.OutputFile.name))
> + args.OutputFile.write (Result)
> + args.OutputFile.close ()
> + except:
> + print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = args.OutputFile.name))
> + sys.exit (1)
> +
> + if args.Verbose:
> + print('Success')
> diff --git a/BaseTools/Source/Python/Common/Edk2/Capsule/FmpPayloadHeader.py
> b/BaseTools/Source/Python/Common/Edk2/Capsule/FmpPayloadHeader.py
> new file mode 100644
> index 0000000000..0ed51752d3
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Edk2/Capsule/FmpPayloadHeader.py
> @@ -0,0 +1,91 @@
> +## @file
> +# Module that encodes and decodes a FMP_PAYLOAD_HEADER with a payload.
> +# The FMP_PAYLOAD_HEADER is processed by the FmpPayloadHeaderLib in the
> +# FmpDevicePkg.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +
> +'''
> +FmpPayloadHeader
> +'''
> +
> +import struct
> +
> +def _SIGNATURE_32 (A, B, C, D):
> + return struct.unpack ('=I',bytearray (A + B + C + D, 'ascii'))[0]
> +
> +def _SIGNATURE_32_TO_STRING (Signature):
> + return struct.pack ("<I", Signature).decode ()
> +
> +class FmpPayloadHeaderClass (object):
> + #
> + # typedef struct {
> + # UINT32 Signature;
> + # UINT32 HeaderSize;
> + # UINT32 FwVersion;
> + # UINT32 LowestSupportedVersion;
> + # } FMP_PAYLOAD_HEADER;
> + #
> + # #define FMP_PAYLOAD_HEADER_SIGNATURE SIGNATURE_32 ('M', 'S', 'S', '1')
> + #
> + _StructFormat = '<IIII'
> + _StructSize = struct.calcsize (_StructFormat)
> +
> + _FMP_PAYLOAD_HEADER_SIGNATURE = _SIGNATURE_32 ('M', 'S', 'S', '1')
> +
> + def __init__ (self):
> + self._Valid = False
> + self.Signature = self._FMP_PAYLOAD_HEADER_SIGNATURE
> + self.HeaderSize = self._StructSize
> + self.FwVersion = 0x00000000
> + self.LowestSupportedVersion = 0x00000000
> + self.Payload = b''
> +
> + def Encode (self):
> + FmpPayloadHeader = struct.pack (
> + self._StructFormat,
> + self.Signature,
> + self.HeaderSize,
> + self.FwVersion,
> + self.LowestSupportedVersion
> + )
> + self._Valid = True
> + return FmpPayloadHeader + self.Payload
> +
> + def Decode (self, Buffer):
> + if len (Buffer) < self._StructSize:
> + raise ValueError
> + (Signature, HeaderSize, FwVersion, LowestSupportedVersion) = \
> + struct.unpack (
> + self._StructFormat,
> + Buffer[0:self._StructSize]
> + )
> + if Signature != self._FMP_PAYLOAD_HEADER_SIGNATURE:
> + raise ValueError
> + if HeaderSize < self._StructSize:
> + raise ValueError
> + self.Signature = Signature
> + self.HeaderSize = HeaderSize
> + self.FwVersion = FwVersion
> + self.LowestSupportedVersion = LowestSupportedVersion
> + self.Payload = Buffer[self.HeaderSize:]
> +
> + self._Valid = True
> + return self.Payload
> +
> + def DumpInfo (self):
> + if not self._Valid:
> + raise ValueError
> + print ('FMP_PAYLOAD_HEADER.Signature = {Signature:08X} ({SignatureString})'.format (Signature =
> self.Signature, SignatureString = _SIGNATURE_32_TO_STRING (self.Signature)))
> + print ('FMP_PAYLOAD_HEADER.HeaderSize = {HeaderSize:08X}'.format (HeaderSize = self.HeaderSize))
> + print ('FMP_PAYLOAD_HEADER.FwVersion = {FwVersion:08X}'.format (FwVersion = self.FwVersion))
> + print ('FMP_PAYLOAD_HEADER.LowestSupportedVersion = {LowestSupportedVersion:08X}'.format
> (LowestSupportedVersion = self.LowestSupportedVersion))
> + print ('sizeof (Payload) = {Size:08X}'.format (Size = len (self.Payload)))
> diff --git a/BaseTools/Source/Python/Common/Edk2/Capsule/__init__.py
> b/BaseTools/Source/Python/Common/Edk2/Capsule/__init__.py
> new file mode 100644
> index 0000000000..71c6f06838
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Edk2/Capsule/__init__.py
> @@ -0,0 +1,15 @@
> +## @file
> +# Python 'Common.Edk2.Capsule' package initialization file.
> +#
> +# This file is required to make Python interpreter treat the directory
> +# as containing package.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> diff --git a/BaseTools/Source/Python/Common/Edk2/__init__.py b/BaseTools/Source/Python/Common/Edk2/__init__.py
> new file mode 100644
> index 0000000000..97d925cbf8
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Edk2/__init__.py
> @@ -0,0 +1,15 @@
> +## @file
> +# Python 'Common.Edk2' package initialization file.
> +#
> +# This file is required to make Python interpreter treat the directory
> +# as containing package.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/FmpAuthHeader.py
> b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpAuthHeader.py
> new file mode 100644
> index 0000000000..aec52bf772
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpAuthHeader.py
> @@ -0,0 +1,184 @@
> +## @file
> +# Module that encodes and decodes a EFI_FIRMWARE_IMAGE_AUTHENTICATION with
> +# certificate data and payload data.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +
> +'''
> +FmpAuthHeader
> +'''
> +
> +import struct
> +import uuid
> +
> +class FmpAuthHeaderClass (object):
> + # ///
> + # /// Image Attribute -Authentication Required
> + # ///
> + # typedef struct {
> + # ///
> + # /// It is included in the signature of AuthInfo. It is used to ensure freshness/no replay.
> + # /// It is incremented during each firmware image operation.
> + # ///
> + # UINT64 MonotonicCount;
> + # ///
> + # /// Provides the authorization for the firmware image operations. It is a signature across
> + # /// the image data and the Monotonic Count value. Caller uses the private key that is
> + # /// associated with a public key that has been provisioned via the key exchange.
> + # /// Because this is defined as a signature, WIN_CERTIFICATE_UEFI_GUID.CertType must
> + # /// be EFI_CERT_TYPE_PKCS7_GUID.
> + # ///
> + # WIN_CERTIFICATE_UEFI_GUID AuthInfo;
> + # } EFI_FIRMWARE_IMAGE_AUTHENTICATION;
> + #
> + # ///
> + # /// Certificate which encapsulates a GUID-specific digital signature
> + # ///
> + # typedef struct {
> + # ///
> + # /// This is the standard WIN_CERTIFICATE header, where
> + # /// wCertificateType is set to WIN_CERT_TYPE_EFI_GUID.
> + # ///
> + # WIN_CERTIFICATE Hdr;
> + # ///
> + # /// This is the unique id which determines the
> + # /// format of the CertData. .
> + # ///
> + # EFI_GUID CertType;
> + # ///
> + # /// The following is the certificate data. The format of
> + # /// the data is determined by the CertType.
> + # /// If CertType is EFI_CERT_TYPE_RSA2048_SHA256_GUID,
> + # /// the CertData will be EFI_CERT_BLOCK_RSA_2048_SHA256 structure.
> + # ///
> + # UINT8 CertData[1];
> + # } WIN_CERTIFICATE_UEFI_GUID;
> + #
> + # ///
> + # /// The WIN_CERTIFICATE structure is part of the PE/COFF specification.
> + # ///
> + # typedef struct {
> + # ///
> + # /// The length of the entire certificate,
> + # /// including the length of the header, in bytes.
> + # ///
> + # UINT32 dwLength;
> + # ///
> + # /// The revision level of the WIN_CERTIFICATE
> + # /// structure. The current revision level is 0x0200.
> + # ///
> + # UINT16 wRevision;
> + # ///
> + # /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI
> + # /// certificate types. The UEFI specification reserves the range of
> + # /// certificate type values from 0x0EF0 to 0x0EFF.
> + # ///
> + # UINT16 wCertificateType;
> + # ///
> + # /// The following is the actual certificate. The format of
> + # /// the certificate depends on wCertificateType.
> + # ///
> + # /// UINT8 bCertificate[ANYSIZE_ARRAY];
> + # ///
> + # } WIN_CERTIFICATE;
> + #
> + # #define WIN_CERT_TYPE_EFI_GUID 0x0EF1
> + #
> + # ///
> + # /// This identifies a signature containing a DER-encoded PKCS #7 version 1.5 [RFC2315]
> + # /// SignedData value.
> + # ///
> + # #define EFI_CERT_TYPE_PKCS7_GUID \
> + # { \
> + # 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} \
> + # }
> +
> + _StructFormat = '<QIHH16s'
> + _StructSize = struct.calcsize (_StructFormat)
> +
> + _MonotonicCountFormat = '<Q'
> + _MonotonicCountSize = struct.calcsize (_MonotonicCountFormat)
> +
> + _StructAuthInfoFormat = '<IHH16s'
> + _StructAuthInfoSize = struct.calcsize (_StructAuthInfoFormat)
> +
> + _WIN_CERT_REVISION = 0x0200
> + _WIN_CERT_TYPE_EFI_GUID = 0x0EF1
> + _EFI_CERT_TYPE_PKCS7_GUID = uuid.UUID ('4aafd29d-68df-49ee-8aa9-347d375665a7')
> +
> + def __init__ (self):
> + self._Valid = False
> + self.MonotonicCount = 0
> + self.dwLength = self._StructAuthInfoSize
> + self.wRevision = self._WIN_CERT_REVISION
> + self.wCertificateType = self._WIN_CERT_TYPE_EFI_GUID
> + self.CertType = self._EFI_CERT_TYPE_PKCS7_GUID
> + self.CertData = b''
> + self.Payload = b''
> +
> +
> + def Encode (self):
> + if self.wRevision != self._WIN_CERT_REVISION:
> + raise ValueError
> + if self.wCertificateType != self._WIN_CERT_TYPE_EFI_GUID:
> + raise ValueError
> + if self.CertType != self._EFI_CERT_TYPE_PKCS7_GUID:
> + raise ValueError
> + self.dwLength = self._StructAuthInfoSize + len (self.CertData)
> +
> + FmpAuthHeader = struct.pack (
> + self._StructFormat,
> + self.MonotonicCount,
> + self.dwLength,
> + self.wRevision,
> + self.wCertificateType,
> + self.CertType.bytes_le
> + )
> + self._Valid = True
> +
> + return FmpAuthHeader + self.CertData + self.Payload
> +
> + def Decode (self, Buffer):
> + if len (Buffer) < self._StructSize:
> + raise ValueError
> + (MonotonicCount, dwLength, wRevision, wCertificateType, CertType) = \
> + struct.unpack (
> + self._StructFormat,
> + Buffer[0:self._StructSize]
> + )
> + if dwLength < self._StructAuthInfoSize:
> + raise ValueError
> + if wRevision != self._WIN_CERT_REVISION:
> + raise ValueError
> + if wCertificateType != self._WIN_CERT_TYPE_EFI_GUID:
> + raise ValueError
> + if CertType != self._EFI_CERT_TYPE_PKCS7_GUID.bytes_le:
> + raise ValueError
> + self.MonotonicCount = MonotonicCount
> + self.dwLength = dwLength
> + self.wRevision = wRevision
> + self.wCertificateType = wCertificateType
> + self.CertType = uuid.UUID (bytes = CertType)
> + self.CertData = Buffer[self._StructSize:self._MonotonicCountSize + self.dwLength]
> + self.Payload = Buffer[self._MonotonicCountSize + self.dwLength:]
> + self._Valid = True
> + return self.Payload
> +
> + def DumpInfo (self):
> + if not self._Valid:
> + raise ValueError
> + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount = {MonotonicCount:016X}'.format
> (MonotonicCount = self.MonotonicCount))
> + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength = {dwLength:08X}'.format (dwLength =
> self.dwLength))
> + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision = {wRevision:04X}'.format (wRevision =
> self.wRevision))
> + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertificateType = {wCertificateType:04X}'.format
> (wCertificateType = self.wCertificateType))
> + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType = {Guid}'.format (Guid =
> str(self.CertType).upper()))
> + print ('sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertData) = {Size:08X}'.format (Size = len
> (self.CertData)))
> + print ('sizeof (Payload) = {Size:08X}'.format (Size = len
> (self.Payload)))
> diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py
> b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py
> new file mode 100644
> index 0000000000..2461fb5068
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py
> @@ -0,0 +1,302 @@
> +## @file
> +# Module that encodes and decodes a EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER with
> +# a payload.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +
> +'''
> +FmpCapsuleHeader
> +'''
> +
> +import struct
> +import uuid
> +
> +class FmpCapsuleImageHeaderClass (object):
> + # typedef struct {
> + # UINT32 Version;
> + #
> + # ///
> + # /// Used to identify device firmware targeted by this update. This guid is matched by
> + # /// system firmware against ImageTypeId field within a EFI_FIRMWARE_IMAGE_DESCRIPTOR
> + # ///
> + # EFI_GUID UpdateImageTypeId;
> + #
> + # ///
> + # /// Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROTOCOL.SetImage ()
> + # ///
> + # UINT8 UpdateImageIndex;
> + # UINT8 reserved_bytes[3];
> + #
> + # ///
> + # /// Size of the binary update image which immediately follows this structure
> + # ///
> + # UINT32 UpdateImageSize;
> + #
> + # ///
> + # /// Size of the VendorCode bytes which optionally immediately follow binary update image in the capsule
> + # ///
> + # UINT32 UpdateVendorCodeSize;
> + #
> + # ///
> + # /// The HardwareInstance to target with this update. If value is zero it means match all
> + # /// HardwareInstances. This field allows update software to target only a single device in
> + # /// cases where there are more than one device with the same ImageTypeId GUID.
> + # /// This header is outside the signed data of the Authentication Info structure and
> + # /// therefore can be modified without changing the Auth data.
> + # ///
> + # UINT64 UpdateHardwareInstance;
> + # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER;
> + #
> + # #define EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION 0x00000002
> +
> + _StructFormat = '<I16sB3BIIQ'
> + _StructSize = struct.calcsize (_StructFormat)
> +
> + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION = 0x00000002
> +
> + def __init__ (self):
> + self._Valid = False
> + self.Version = self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION
> + self.UpdateImageTypeId = uuid.UUID ('00000000-0000-0000-0000-000000000000')
> + self.UpdateImageIndex = 0
> + self.UpdateImageSize = 0
> + self.UpdateVendorCodeSize = 0
> + self.UpdateHardwareInstance = 0x0000000000000000
> + self.Payload = b''
> + self.VendorCodeBytes = b''
> +
> + def Encode (self):
> + self.UpdateImageSize = len (self.Payload)
> + self.UpdateVendorCodeSize = len (self.VendorCodeBytes)
> + FmpCapsuleImageHeader = struct.pack (
> + self._StructFormat,
> + self.Version,
> + self.UpdateImageTypeId.bytes_le,
> + self.UpdateImageIndex,
> + 0,0,0,
> + self.UpdateImageSize,
> + self.UpdateVendorCodeSize,
> + self.UpdateHardwareInstance
> + )
> + self._Valid = True
> + return FmpCapsuleImageHeader + self.Payload + self.VendorCodeBytes
> +
> + def Decode (self, Buffer):
> + if len (Buffer) < self._StructSize:
> + raise ValueError
> + (Version, UpdateImageTypeId, UpdateImageIndex, r0, r1, r2, UpdateImageSize, UpdateVendorCodeSize,
> UpdateHardwareInstance) = \
> + struct.unpack (
> + self._StructFormat,
> + Buffer[0:self._StructSize]
> + )
> +
> + if Version < self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION:
> + raise ValueError
> + if UpdateImageIndex < 1:
> + raise ValueError
> + if UpdateImageSize + UpdateVendorCodeSize != len (Buffer[self._StructSize:]):
> + raise ValueError
> +
> + self.Version = Version
> + self.UpdateImageTypeId = uuid.UUID (bytes_le = UpdateImageTypeId)
> + self.UpdateImageIndex = UpdateImageIndex
> + self.UpdateImageSize = UpdateImageSize
> + self.UpdateVendorCodeSize = UpdateVendorCodeSize
> + self.UpdateHardwareInstance = UpdateHardwareInstance
> + self.Payload = Buffer[self._StructSize:self._StructSize + UpdateImageSize]
> + self.VendorCodeBytes = Buffer[self._StructSize + UpdateImageSize:]
> + self._Valid = True
> + return Buffer[self._StructSize:]
> +
> + def DumpInfo (self):
> + if not self._Valid:
> + raise ValueError
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version = {Version:08X}'.format
> (Version = self.Version))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageTypeId =
> {UpdateImageTypeId}'.format (UpdateImageTypeId = str(self.UpdateImageTypeId).upper()))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageIndex =
> {UpdateImageIndex:08X}'.format (UpdateImageIndex = self.UpdateImageIndex))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImageSize =
> {UpdateImageSize:08X}'.format (UpdateImageSize = self.UpdateImageSize))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendorCodeSize =
> {UpdateVendorCodeSize:08X}'.format (UpdateVendorCodeSize = self.UpdateVendorCodeSize))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardwareInstance =
> {UpdateHardwareInstance:016X}'.format (UpdateHardwareInstance = self.UpdateHardwareInstance))
> + print ('sizeof (Payload) = {Size:08X}'.format (Size = len
> (self.Payload)))
> + print ('sizeof (VendorCodeBytes) = {Size:08X}'.format (Size = len
> (self.VendorCodeBytes)))
> +
> +class FmpCapsuleHeaderClass (object):
> + # typedef struct {
> + # UINT32 Version;
> + #
> + # ///
> + # /// The number of drivers included in the capsule and the number of corresponding
> + # /// offsets stored in ItemOffsetList array.
> + # ///
> + # UINT16 EmbeddedDriverCount;
> + #
> + # ///
> + # /// The number of payload items included in the capsule and the number of
> + # /// corresponding offsets stored in the ItemOffsetList array.
> + # ///
> + # UINT16 PayloadItemCount;
> + #
> + # ///
> + # /// Variable length array of dimension [EmbeddedDriverCount + PayloadItemCount]
> + # /// containing offsets of each of the drivers and payload items contained within the capsule
> + # ///
> + # // UINT64 ItemOffsetList[];
> + # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER;
> + #
> + # #define EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION 0x00000001
> + _StructFormat = '<IHH'
> + _StructSize = struct.calcsize (_StructFormat)
> +
> + _ItemOffsetFormat = '<Q'
> + _ItemOffsetSize = struct.calcsize (_ItemOffsetFormat)
> +
> + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION = 0x00000001
> +
> + def __init__ (self):
> + self._Valid = False
> + self.Version = self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION
> + self.EmbeddedDriverCount = 0
> + self.PayloadItemCount = 0
> + self._ItemOffsetList = []
> + self._EmbeddedDriverList = []
> + self._PayloadList = []
> + self._FmpCapsuleImageHeaderList = []
> +
> + def AddEmbeddedDriver (self, EmbeddedDriver):
> + self._EmbeddedDriverList.append (EmbeddedDriver)
> +
> + def GetEmbeddedDriver (self, Index):
> + if Index > len (self._EmbeddedDriverList):
> + raise ValueError
> + return self._EmbeddedDriverList[Index]
> +
> + def AddPayload (self, UpdateImageTypeId, Payload = b'', VendorCodeBytes = b'', HardwareInstance = 0):
> + self._PayloadList.append ((UpdateImageTypeId, Payload, VendorCodeBytes, HardwareInstance))
> +
> + def GetFmpCapsuleImageHeader (self, Index):
> + if Index >= len (self._FmpCapsuleImageHeaderList):
> + raise ValueError
> + return self._FmpCapsuleImageHeaderList[Index]
> +
> + def Encode (self):
> + self.EmbeddedDriverCount = len (self._EmbeddedDriverList)
> + self.PayloadItemCount = len (self._PayloadList)
> +
> + FmpCapsuleHeader = struct.pack (
> + self._StructFormat,
> + self.Version,
> + self.EmbeddedDriverCount,
> + self.PayloadItemCount
> + )
> +
> + FmpCapsuleData = b''
> + Offset = self._StructSize + (self.EmbeddedDriverCount + self.PayloadItemCount) * self._ItemOffsetSize
> + for EmbeddedDriver in self._EmbeddedDriverList:
> + FmpCapsuleData = FmpCapsuleData + EmbeddedDriver
> + self._ItemOffsetList.append (Offset)
> + Offset = Offset + len (EmbeddedDriver)
> + Index = 1
> + for (UpdateImageTypeId, Payload, VendorCodeBytes, HardwareInstance) in self._PayloadList:
> + FmpCapsuleImageHeader = FmpCapsuleImageHeaderClass ()
> + FmpCapsuleImageHeader.UpdateImageTypeId = UpdateImageTypeId
> + FmpCapsuleImageHeader.UpdateImageIndex = Index
> + FmpCapsuleImageHeader.Payload = Payload
> + FmpCapsuleImageHeader.VendorCodeBytes = VendorCodeBytes
> + FmpCapsuleImageHeader.UpdateHardwareInstance = HardwareInstance
> + FmpCapsuleImage = FmpCapsuleImageHeader.Encode ()
> + FmpCapsuleData = FmpCapsuleData + FmpCapsuleImage
> +
> + self._ItemOffsetList.append (Offset)
> + self._FmpCapsuleImageHeaderList.append (FmpCapsuleImageHeader)
> +
> + Offset = Offset + len (FmpCapsuleImage)
> + Index = Index + 1
> +
> + for Offset in self._ItemOffsetList:
> + FmpCapsuleHeader = FmpCapsuleHeader + struct.pack (self._ItemOffsetFormat, Offset)
> +
> + self._Valid = True
> + return FmpCapsuleHeader + FmpCapsuleData
> +
> + def Decode (self, Buffer):
> + if len (Buffer) < self._StructSize:
> + raise ValueError
> + (Version, EmbeddedDriverCount, PayloadItemCount) = \
> + struct.unpack (
> + self._StructFormat,
> + Buffer[0:self._StructSize]
> + )
> + if Version < self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION:
> + raise ValueError
> +
> + self.Version = Version
> + self.EmbeddedDriverCount = EmbeddedDriverCount
> + self.PayloadItemCount = PayloadItemCount
> + self._ItemOffsetList = []
> + self._EmbeddedDriverList = []
> + self._PayloadList = []
> + self._FmpCapsuleImageHeaderList = []
> +
> + #
> + # Parse the ItemOffsetList values
> + #
> + Offset = self._StructSize
> + for Index in range (0, EmbeddedDriverCount + PayloadItemCount):
> + ItemOffset = struct.unpack (self._ItemOffsetFormat, Buffer[Offset:Offset + self._ItemOffsetSize])[0]
> + if ItemOffset >= len (Buffer):
> + raise ValueError
> + self._ItemOffsetList.append (ItemOffset)
> + Offset = Offset + self._ItemOffsetSize
> + Result = Buffer[Offset:]
> +
> + #
> + # Parse the EmbeddedDrivers
> + #
> + for Index in range (0, EmbeddedDriverCount):
> + Offset = self._ItemOffsetList[Index]
> + if Index < (len (self._ItemOffsetList) - 1):
> + Length = self._ItemOffsetList[Index + 1] - Offset
> + else:
> + Length = len (Buffer) - Offset
> + self.AddEmbeddedDriver (Buffer[Offset:Offset + Length])
> +
> + #
> + # Parse the Payloads that are FMP Capsule Images
> + #
> + for Index in range (EmbeddedDriverCount, EmbeddedDriverCount + PayloadItemCount):
> + Offset = self._ItemOffsetList[Index]
> + if Index < (len (self._ItemOffsetList) - 1):
> + Length = self._ItemOffsetList[Index + 1] - Offset
> + else:
> + Length = len (Buffer) - Offset
> + FmpCapsuleImageHeader = FmpCapsuleImageHeaderClass ()
> + FmpCapsuleImageHeader.Decode (Buffer[Offset:Offset + Length])
> + self.AddPayload (
> + FmpCapsuleImageHeader.UpdateImageTypeId,
> + FmpCapsuleImageHeader.Payload,
> + FmpCapsuleImageHeader.VendorCodeBytes
> + )
> + self._FmpCapsuleImageHeaderList.append (FmpCapsuleImageHeader)
> +
> + self._Valid = True
> + return Result
> +
> + def DumpInfo (self):
> + if not self._Valid:
> + raise ValueError
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.Version = {Version:08X}'.format (Version =
> self.Version))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.EmbeddedDriverCount = {EmbeddedDriverCount:08X}'.format
> (EmbeddedDriverCount = self.EmbeddedDriverCount))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.PayloadItemCount = {PayloadItemCount:08X}'.format
> (PayloadItemCount = self.PayloadItemCount))
> + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.ItemOffsetList = ')
> + for Offset in self._ItemOffsetList:
> + print (' {Offset:016X}'.format (Offset = Offset))
> + for FmpCapsuleImageHeader in self._FmpCapsuleImageHeaderList:
> + FmpCapsuleImageHeader.DumpInfo ()
> diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsuleHeader.py
> b/BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsuleHeader.py
> new file mode 100644
> index 0000000000..cfe1cb6c46
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsuleHeader.py
> @@ -0,0 +1,136 @@
> +## @file
> +# Module that encodes and decodes a EFI_CAPSULE_HEADER with a payload
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +
> +'''
> +UefiCapsuleHeader
> +'''
> +
> +import struct
> +import uuid
> +
> +class UefiCapsuleHeaderClass (object):
> + # typedef struct {
> + # ///
> + # /// A GUID that defines the contents of a capsule.
> + # ///
> + # EFI_GUID CapsuleGuid;
> + # ///
> + # /// The size of the capsule header. This may be larger than the size of
> + # /// the EFI_CAPSULE_HEADER since CapsuleGuid may imply
> + # /// extended header entries
> + # ///
> + # UINT32 HeaderSize;
> + # ///
> + # /// Bit-mapped list describing the capsule attributes. The Flag values
> + # /// of 0x0000 - 0xFFFF are defined by CapsuleGuid. Flag values
> + # /// of 0x10000 - 0xFFFFFFFF are defined by this specification
> + # ///
> + # UINT32 Flags;
> + # ///
> + # /// Size in bytes of the capsule.
> + # ///
> + # UINT32 CapsuleImageSize;
> + # } EFI_CAPSULE_HEADER;
> + #
> + # #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000
> + # #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
> + # #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000
> + #
> + _StructFormat = '<16sIIII'
> + _StructSize = struct.calcsize (_StructFormat)
> +
> + EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID = uuid.UUID ('6DCBD5ED-E82D-4C44-BDA1-7194199AD92A')
> +
> + _CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000
> + _CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000
> + _CAPSULE_FLAGS_INITIATE_RESET = 0x00040000
> +
> + def __init__ (self):
> + self._Valid = False
> + self.CapsuleGuid = self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID
> + self.HeaderSize = self._StructSize
> + self.OemFlags = 0x0000
> + self.PersistAcrossReset = False
> + self.PopulateSystemTable = False
> + self.InitiateReset = False
> + self.CapsuleImageSize = self.HeaderSize
> + self.Payload = b''
> +
> + def Encode (self):
> + Flags = self.OemFlags
> + if self.PersistAcrossReset:
> + Flags = Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET
> + if self.PopulateSystemTable:
> + Flags = Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE
> + if self.InitiateReset:
> + Flags = Flags | self._CAPSULE_FLAGS_INITIATE_RESET
> +
> + self.CapsuleImageSize = self.HeaderSize + len (self.Payload)
> +
> + UefiCapsuleHeader = struct.pack (
> + self._StructFormat,
> + self.CapsuleGuid.bytes_le,
> + self.HeaderSize,
> + Flags,
> + self.CapsuleImageSize,
> + 0
> + )
> + self._Valid = True
> + return UefiCapsuleHeader + self.Payload
> +
> + def Decode (self, Buffer):
> + if len (Buffer) < self._StructSize:
> + raise ValueError
> + (CapsuleGuid, HeaderSize, Flags, CapsuleImageSize, Reserved) = \
> + struct.unpack (
> + self._StructFormat,
> + Buffer[0:self._StructSize]
> + )
> + if HeaderSize < self._StructSize:
> + raise ValueError
> + if CapsuleImageSize != len (Buffer):
> + raise ValueError
> + self.CapsuleGuid = uuid.UUID (bytes_le = CapsuleGuid)
> + self.HeaderSize = HeaderSize
> + self.OemFlags = Flags & 0xffff
> + self.PersistAcrossReset = (Flags & self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0
> + self.PopulateSystemTable = (Flags & self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0
> + self.InitiateReset = (Flags & self._CAPSULE_FLAGS_INITIATE_RESET) != 0
> + self.CapsuleImageSize = CapsuleImageSize
> + self.Payload = Buffer[self.HeaderSize:]
> +
> + self._Valid = True
> + return self.Payload
> +
> + def DumpInfo (self):
> + if not self._Valid:
> + raise ValueError
> + Flags = self.OemFlags
> + if self.PersistAcrossReset:
> + Flags = Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET
> + if self.PopulateSystemTable:
> + Flags = Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE
> + if self.InitiateReset:
> + Flags = Flags | self._CAPSULE_FLAGS_INITIATE_RESET
> + print ('EFI_CAPSULE_HEADER.CapsuleGuid = {Guid}'.format (Guid = str(self.CapsuleGuid).upper()))
> + print ('EFI_CAPSULE_HEADER.HeaderSize = {Size:08X}'.format (Size = self.HeaderSize))
> + print ('EFI_CAPSULE_HEADER.Flags = {Flags:08X}'.format (Flags = Flags))
> + print (' OEM Flags = {Flags:04X}'.format (Flags = self.OemFlags))
> + if self.PersistAcrossReset:
> + print (' CAPSULE_FLAGS_PERSIST_ACROSS_RESET')
> + if self.PopulateSystemTable:
> + print (' CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE')
> + if self.InitiateReset:
> + print (' CAPSULE_FLAGS_INITIATE_RESET')
> + print ('EFI_CAPSULE_HEADER.CapsuleImageSize = {Size:08X}'.format (Size = self.CapsuleImageSize))
> + print ('sizeof (Payload) = {Size:08X}'.format (Size = len (self.Payload)))
> diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/__init__.py
> b/BaseTools/Source/Python/Common/Uefi/Capsule/__init__.py
> new file mode 100644
> index 0000000000..d9db4aa919
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Uefi/Capsule/__init__.py
> @@ -0,0 +1,15 @@
> +## @file
> +# Python 'Common.Uefi.Capsule' package initialization file.
> +#
> +# This file is required to make Python interpreter treat the directory
> +# as containing package.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> diff --git a/BaseTools/Source/Python/Common/Uefi/__init__.py b/BaseTools/Source/Python/Common/Uefi/__init__.py
> new file mode 100644
> index 0000000000..d80219dcb3
> --- /dev/null
> +++ b/BaseTools/Source/Python/Common/Uefi/__init__.py
> @@ -0,0 +1,15 @@
> +## @file
> +# Python 'Common.Uefi' package initialization file.
> +#
> +# This file is required to make Python interpreter treat the directory
> +# as containing package.
> +#
> +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
> +# This program and the accompanying materials
> +# are licensed and made available under the terms and conditions of the BSD License
> +# which accompanies this distribution. The full text of the license may be found at
> +# http://opensource.org/licenses/bsd-license.php
> +#
> +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> --
> 2.14.2.windows.3
next prev parent reply other threads:[~2018-05-31 14:25 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-05-29 22:56 [RFC 0/1] Add Capsule Generation Tools Michael D Kinney
2018-05-29 22:56 ` [RFC 1/1] BaseTools/Capsule: " Michael D Kinney
2018-05-31 14:25 ` Gao, Liming [this message]
2018-05-31 16:36 ` Kinney, Michael D
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A89E2EF3DFEDB4C8BFDE51014F606A14E291226@SHSMSX104.ccr.corp.intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox