From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.43; helo=mga05.intel.com; envelope-from=liming.gao@intel.com; receiver=edk2-devel@lists.01.org Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 1BC63209603CA for ; Thu, 31 May 2018 07:25:39 -0700 (PDT) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 31 May 2018 07:25:39 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.49,463,1520924400"; d="scan'208";a="43654443" Received: from fmsmsx106.amr.corp.intel.com ([10.18.124.204]) by fmsmga007.fm.intel.com with ESMTP; 31 May 2018 07:25:39 -0700 Received: from fmsmsx121.amr.corp.intel.com (10.18.125.36) by FMSMSX106.amr.corp.intel.com (10.18.124.204) with Microsoft SMTP Server (TLS) id 14.3.319.2; Thu, 31 May 2018 07:25:38 -0700 Received: from shsmsx103.ccr.corp.intel.com (10.239.4.69) by fmsmsx121.amr.corp.intel.com (10.18.125.36) with Microsoft SMTP Server (TLS) id 14.3.319.2; Thu, 31 May 2018 07:25:38 -0700 Received: from shsmsx104.ccr.corp.intel.com ([169.254.5.87]) by SHSMSX103.ccr.corp.intel.com ([169.254.4.51]) with mapi id 14.03.0319.002; Thu, 31 May 2018 22:25:36 +0800 From: "Gao, Liming" To: "Kinney, Michael D" , "edk2-devel@lists.01.org" CC: Sean Brogan , "Yao, Jiewen" , "Zhu, Yonghong" Thread-Topic: [RFC 1/1] BaseTools/Capsule: Add Capsule Generation Tools Thread-Index: AQHT96BPDFnP8+PrB0iksyD7TkOUi6RJ5PTw Date: Thu, 31 May 2018 14:25:34 +0000 Message-ID: <4A89E2EF3DFEDB4C8BFDE51014F606A14E291226@SHSMSX104.ccr.corp.intel.com> References: <20180529225635.22536-1-michael.d.kinney@intel.com> <20180529225635.22536-2-michael.d.kinney@intel.com> In-Reply-To: <20180529225635.22536-2-michael.d.kinney@intel.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ctpclassification: CTP_NT x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiZDQ3ZmYwNjItMWNhNC00ODk0LTkyMWYtNjA3YzA0YjRjZjc1IiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoiUzNZWitYYVFSYmt6XC9oU09jWG13cElKSm9yY1JUYklFaXFsd1lIeUhjS1p0R1wvRjhVK1R6QkkrbnZHSFcwSmV2In0= dlp-product: dlpe-windows dlp-version: 11.0.200.100 dlp-reaction: no-action x-originating-ip: [10.239.127.40] MIME-Version: 1.0 Subject: Re: [RFC 1/1] BaseTools/Capsule: Add Capsule Generation Tools X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 31 May 2018 14:25:40 -0000 Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Mike: Which C tool is converted? Does it support all capsule generation functio= nality in GenFv tool?=20 =20 And, GenFds has the logic to generate FMP capsule. Can they share same lo= gic?=20 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 ; Sean Brogan ; Yao, Jiewen > ; Zhu, Yonghong ; Gao, Limi= ng > Subject: [RFC 1/1] BaseTools/Capsule: Add Capsule Generation Tools >=20 > From: "Kinney, Michael D" >=20 > https://bugzilla.tianocore.org/show_bug.cgi?id=3D945 >=20 > Based on content from the following branch >=20 > https://github.com/Microsoft/MS_UEFI/tree/share/beta/CapsuleTools >=20 > * 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 >=20 > 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 LOWESTSUPPORTEDVERSIO= N] > [--pfx-file SIGNTOOLPFXFILE] > [--signer-private-cert OPENSSLSIGNERPRIVATECERTFIL= E] > [--other-public-cert OPENSSLOTHERPUBLICCERTFILE] > [--trusted-public-cert OPENSSLTRUSTEDPUBLICCERTFIL= E] > [--signing-tool-path SIGNINGTOOLPATH] [-v] [-q] > [--debug [0-9]] > InputFile >=20 > Generate a capsule. Copyright (c) 2018, Intel Corporation. All rights > reserved. >=20 > positional arguments: > InputFile Input binary payload filename. >=20 > 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 fo= r > 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 i= s > 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 pa= th to > tools are already in PATH. > -v, --verbose Increase output messages > -q, --quiet Reduce output messages > --debug [0-9] Set debug level >=20 > Cc: Sean Brogan > Cc: Jiewen Yao > Cc: Yonghong Zhu > Cc: Liming Gao > Contributed-under: TianoCore Contribution Agreement 1.1 > Signed-off-by: Michael D Kinney > --- > 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/FmpPayloa= dHeader.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/FmpAuthHe= ader.py > create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsul= eHeader.py > create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsu= leHeader.py > create mode 100644 BaseTools/Source/Python/Common/Uefi/Capsule/__init__.= py > create mode 100644 BaseTools/Source/Python/Common/Uefi/__init__.py >=20 > 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=3Dpython2 > +fi > + > +full_cmd=3D${BASH_SOURCE:-$0} # see http://mywiki.wooledge.org/BashFAQ/0= 28 for a discussion of why $0 is not a good choice here > +dir=3D$(dirname "$full_cmd") > +cmd=3D${full_cmd##*/} > + > +export PYTHONPATH=3D"$dir/../../Source/Python${PYTHONPATH:+:"$PYTHONPATH= "}" > +exec "${python_exe:-python}" "$dir/../../Source/Python/Capsule/$cmd.py" = "$@" > diff --git a/BaseTools/BinWrappers/WindowsLike/GenerateCapsule.bat b/Base= Tools/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\Genera= teCapsule.py %* > diff --git a/BaseTools/Source/Python/Capsule/GenerateCapsule.py b/BaseToo= ls/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.
> +# 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 I= MPLIED. > +# > + > +''' > +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__ =3D 'GenerateCapsule' > +__copyright__ =3D 'Copyright (c) 2018, Intel Corporation. All rights r= eserved.' > +__description__ =3D 'Generate a capsule.\n' > + > +def SignPayloadSignTool (Payload, ToolPath, PfxFile): > + # > + # Create a temporary directory > + # > + TempDirectoryName =3D tempfile.mkdtemp() > + > + # > + # Generate temp file name for the payload contents > + # > + TempFileName =3D os.path.join (TempDirectoryName, 'Payload.bin') > + > + # > + # Create temporary payload file for signing > + # > + try: > + File =3D open (TempFileName, mode=3D'wb') > + File.write (Payload) > + File.close () > + except: > + shutil.rmtree (TempDirectoryName) > + raise ValueError ('GenerateCapsule: error: can not write tempora= ry payload file.') > + > + # > + # Build signtool command > + # > + if ToolPath is None: > + ToolPath =3D '' > + Command =3D '' > + Command =3D Command + '"{Path}" '.format (Path =3D os.path.join (Too= lPath, 'signtool.exe')) > + Command =3D Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7c= o 1.2.840.113549.1.7.2 ' > + Command =3D Command + '/p7 {TempDir} '.format (TempDir =3D TempDirec= toryName) > + Command =3D Command + '/f {PfxFile} '.format (PfxFile =3D PfxFile) > + Command =3D Command + TempFileName > + > + # > + # Sign the input file using the specified private key > + # > + try: > + Process =3D subprocess.Popen (Command, stdin =3D subprocess.PIPE= , stdout =3D subprocess.PIPE, stderr =3D subprocess.PIPE, shell =3D > True) > + Result =3D Process.communicate('') > + except: > + shutil.rmtree (TempDirectoryName) > + raise ValueError ('GenerateCapsule: error: can not run signtool.= ') > + > + if Process.returncode !=3D 0: > + shutil.rmtree (TempDirectoryName) > + print (Result[1].decode()) > + raise ValueError ('GenerateCapsule: error: signtool failed.') > + > + # > + # Read the signature from the generated output file > + # > + try: > + File =3D open (TempFileName + '.p7', mode=3D'rb') > + Signature =3D File.read () > + File.close () > + except: > + shutil.rmtree (TempDirectoryName) > + raise ValueError ('GenerateCapsule: error: can not read signatur= e 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 su= pported.') > + > +def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherP= ublicCertFile, TrustedPublicCertFile): > + # > + # Build openssl command > + # > + if ToolPath is None: > + ToolPath =3D '' > + Command =3D '' > + Command =3D Command + '"{Path}" '.format (Path =3D os.path.join (Too= lPath, 'openssl')) > + Command =3D Command + 'smime -sign -binary -outform DER -md sha256 ' > + Command =3D Command + '-signer "{Private}" -certfile "{Public}"'.for= mat (Private =3D SignerPrivateCertFile, Public =3D > OtherPublicCertFile) > + > + # > + # Sign the input file using the specified private key and capture si= gnature from STDOUT > + # > + try: > + Process =3D subprocess.Popen (Command, stdin =3D subprocess.PIPE= , stdout =3D subprocess.PIPE, stderr =3D subprocess.PIPE, shell =3D > True) > + Result =3D Process.communicate(input =3D Payload) > + Signature =3D Result[0] > + except: > + raise ValueError ('GenerateCapsule: error: can not run openssl.'= ) > + > + if Process.returncode !=3D 0: > + print (Result[1].decode()) > + raise ValueError ('GenerateCapsule: error: openssl failed.') > + > + return Signature > + > +def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCert= File, OtherPublicCertFile, TrustedPublicCertFile): > + # > + # Create a temporary directory > + # > + TempDirectoryName =3D tempfile.mkdtemp() > + > + # > + # Generate temp file name for the payload contents > + # > + TempFileName =3D os.path.join (TempDirectoryName, 'Payload.bin') > + > + # > + # Create temporary payload file for verification > + # > + try: > + File =3D open (TempFileName, mode=3D'wb') > + File.write (Payload) > + File.close () > + except: > + shutil.rmtree (TempDirectoryName) > + raise ValueError ('GenerateCapsule: error: can not write tempora= ry payload file.') > + > + # > + # Build openssl command > + # > + if ToolPath is None: > + ToolPath =3D '' > + Command =3D '' > + Command =3D Command + '"{Path}" '.format (Path =3D os.path.join (Too= lPath, 'openssl')) > + Command =3D Command + 'smime -verify -inform DER ' > + Command =3D Command + '-content {Content} -CAfile "{Public}"'.format= (Content =3D TempFileName, Public =3D TrustedPublicCertFile) > + > + # > + # Verify signature > + # > + try: > + Process =3D subprocess.Popen (Command, stdin =3D subprocess.PIPE= , stdout =3D subprocess.PIPE, stderr =3D subprocess.PIPE, shell =3D > True) > + Result =3D Process.communicate(input =3D CertData) > + except: > + shutil.rmtree (TempDirectoryName) > + raise ValueError ('GenerateCapsule: error: can not run openssl.'= ) > + > + if Process.returncode !=3D 0: > + shutil.rmtree (TempDirectoryName) > + print (Result[1].decode()) > + raise ValueError ('GenerateCapsule: error: openssl failed.') > + > + shutil.rmtree (TempDirectoryName) > + return Payload > + > +if __name__ =3D=3D '__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 =3D int (Argument, 0) > + except: > + Message =3D '{Argument} is not a valid integer value.'.forma= t (Argument =3D Argument) > + raise argparse.ArgumentTypeError (Message) > + if Value < 0: > + Message =3D '{Argument} is a negative value.'.format (Argume= nt =3D Argument) > + raise argparse.ArgumentTypeError (Message) > + return Value > + > + def ValidateRegistryFormatGuid (Argument): > + try: > + Value =3D uuid.UUID (Argument) > + except: > + Message =3D '{Argument} is not a valid registry format GUID = value.'.format (Argument =3D Argument) > + raise argparse.ArgumentTypeError (Message) > + return Value > + > + # > + # Create command line argument parser object > + # > + parser =3D argparse.ArgumentParser ( > + prog =3D __prog__, > + description =3D __description__ + __copyright__, > + conflict_handler =3D 'resolve', > + fromfile_prefix_chars =3D '@' > + ) > + parser.convert_arg_line_to_args =3D convert_arg_line_to_args > + > + # > + # Add input and output file arguments > + # > + parser.add_argument("InputFile", type =3D argparse.FileType('rb'), > + help =3D "Input binary payload filename.") > + parser.add_argument("-o", "--output", dest =3D 'OutputFile', type = =3D argparse.FileType('wb'), > + help =3D "Output filename.") > + # > + # Add group for -e and -d flags that are mutually exclusive and requ= ired > + # > + group =3D parser.add_mutually_exclusive_group (required =3D True) > + group.add_argument ("-e", "--encode", dest =3D 'Encode', action =3D = "store_true", > + help =3D "Encode file") > + group.add_argument ("-d", "--decode", dest =3D 'Decode', action =3D = "store_true", > + help =3D "Decode file") > + group.add_argument ("--dump-info", dest =3D 'DumpInfo', action =3D "= store_true", > + help =3D "Display FMP Payload Header information= ") > + # > + # Add optional arguments for this command > + # > + parser.add_argument ("--capflag", dest =3D 'CapsuleFlag', action=3D'= append', default =3D [], > + choices=3D['PersistAcrossReset', 'PopulateSyste= mTable', 'InitiateReset'], > + help =3D "Capsule flag can be PersistAcrossRese= t, or PopulateSystemTable or InitiateReset or not set") > + parser.add_argument ("--capoemflag", dest =3D 'CapsuleOemFlag', type= =3D ValidateUnsignedInteger, default =3D 0x0000, > + help =3D "Capsule OEM Flag is an integer betwee= n 0x0000 and 0xffff.") > + > + parser.add_argument ("--guid", dest =3D 'Guid', type =3D ValidateReg= istryFormatGuid, > + help =3D "The FMP/ESRT GUID in registry format.= Required for encode operations.") > + parser.add_argument ("--hardware-instance", dest =3D 'HardwareInstan= ce', type =3D ValidateUnsignedInteger, default =3D > 0x0000000000000000, > + help =3D "The 64-bit hardware instance. The de= fault is 0x0000000000000000") > + > + > + parser.add_argument ("--monotonic-count", dest =3D 'MonotonicCount',= type =3D ValidateUnsignedInteger, default =3D > 0x0000000000000000, > + help =3D "64-bit monotonic count value in heade= r. Default is 0x0000000000000000.") > + > + parser.add_argument ("--version", dest =3D 'FwVersion', type =3D Val= idateUnsignedInteger, > + help =3D "The 32-bit version of the binary payl= oad (e.g. 0x11223344 or 5678).") > + parser.add_argument ("--lsv", dest =3D 'LowestSupportedVersion', typ= e =3D ValidateUnsignedInteger, > + help =3D "The 32-bit lowest supported version o= f the binary payload (e.g. 0x11223344 or 5678).") > + > + parser.add_argument ("--pfx-file", dest=3D'SignToolPfxFile', type=3D= argparse.FileType('rb'), > + help=3D"signtool PFX certificate filename.") > + > + parser.add_argument ("--signer-private-cert", dest=3D'OpenSslSignerP= rivateCertFile', type=3Dargparse.FileType('rb'), > + help=3D"OpenSSL signer private certificate file= name.") > + parser.add_argument ("--other-public-cert", dest=3D'OpenSslOtherPubl= icCertFile', type=3Dargparse.FileType('rb'), > + help=3D"OpenSSL other public certificate filena= me.") > + parser.add_argument ("--trusted-public-cert", dest=3D'OpenSslTrusted= PublicCertFile', type=3Dargparse.FileType('rb'), > + help=3D"OpenSSL trusted public certificate file= name.") > + > + parser.add_argument ("--signing-tool-path", dest =3D 'SigningToolPat= h', > + help =3D "Path to signtool or Open SLL tool. O= ptional if path to tools are already in PATH.") > + > + # > + # Add optional arguments common to all commands > + # > + parser.add_argument("-v", "--verbose", dest =3D 'Verbose', action = =3D "store_true", > + help =3D "Increase output messages") > + parser.add_argument("-q", "--quiet", dest =3D 'Quiet', action =3D "s= tore_true", > + help =3D "Reduce output messages") > + parser.add_argument("--debug", dest =3D 'Debug', type =3D int, metav= ar =3D '[0-9]', choices =3D range(0,10), default =3D 0, > + help =3D "Set debug level") > + > + # > + # Parse command line arguments > + # > + args =3D 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 comm= ands except --dump-info: --output') > + > + if args.Encode and (args.Guid is None or args.FwVersion is None or a= rgs.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.OpenSslSignerPrivateCer= tFile is None and args.OpenSslOtherPublicCertFile is None > and args.OpenSslTrustedPublicCertFile is None: > + parser.error ('certificate file arguments are required: --pf= x-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 i= s not supported') > + if args.OpenSslOtherPublicCertFile is not None: > + parser.error ('Providing both signtool and OpenSSL options i= s not supported') > + if args.OpenSslTrustedPublicCertFile is not None: > + parser.error ('Providing both signtool and OpenSSL options i= s not supported') > + args.SignToolPfxFile.close() > + args.SignToolPfxFile =3D args.SignToolPfxFile.name > + > + if not args.DumpInfo: > + if args.SignToolPfxFile is None: > + if args.OpenSslSignerPrivateCertFile is None: > + parser.error ('the following arguments are required: --s= igner-private-cert') > + if args.OpenSslOtherPublicCertFile is None: > + parser.error ('the following arguments are required: --o= ther-public-cert') > + if args.OpenSslTrustedPublicCertFile is None: > + parser.error ('the following arguments are required: --t= rusted-public-cert') > + args.OpenSslSignerPrivateCertFile.close() > + args.OpenSslOtherPublicCertFile.close() > + args.OpenSslTrustedPublicCertFile.close() > + args.OpenSslSignerPrivateCertFile =3D args.OpenSslSignerPriv= ateCertFile.name > + args.OpenSslOtherPublicCertFile =3D args.OpenSslOtherPubli= cCertFile.name > + args.OpenSslTrustedPublicCertFile =3D args.OpenSslTrustedPub= licCertFile.name > + > + # > + # Read binary input file > + # > + try: > + if args.Verbose: > + print ('Read binary input file {File}'.format (File =3D args= .InputFile.name)) > + Buffer =3D args.InputFile.read () > + args.InputFile.close () > + except: > + print ('GenerateCapsule: error: can not read binary input file {= File}'.format (File =3D args.InputFile.name)) > + sys.exit (1) > + > + # > + # Create objects > + # > + UefiCapsuleHeader =3D UefiCapsuleHeaderClass () > + FmpCapsuleHeader =3D FmpCapsuleHeaderClass () > + FmpAuthHeader =3D FmpAuthHeaderClass () > + FmpPayloadHeader =3D FmpPayloadHeaderClass () > + > + if args.Encode: > + try: > + FmpPayloadHeader.FwVersion =3D args.FwVersion > + FmpPayloadHeader.LowestSupportedVersion =3D args.LowestSuppo= rtedVersion > + FmpPayloadHeader.Payload =3D Buffer > + Result =3D FmpPayloadHeader.Encode () > + if args.Verbose: > + FmpPayloadHeader.DumpInfo () > + except: > + print ('GenerateCapsule: error: can not encode FMP Payload H= eader') > + sys.exit (1) > + > + # > + # Sign image with 64-bit MonotonicCount appended to end of image > + # > + try: > + if args.SignToolPfxFile is not None: > + CertData =3D SignPayloadSignTool ( > + Result + struct.pack (' + args.SigningToolPath, > + args.SignToolPfxFile > + ) > + else: > + CertData =3D SignPayloadOpenSsl ( > + Result + struct.pack (' + args.SigningToolPath, > + args.OpenSslSignerPrivateCertFile, > + args.OpenSslOtherPublicCertFile, > + args.OpenSslTrustedPublicCertFile > + ) > + except: > + print ('GenerateCapsule: error: can not sign payload') > + raise > + sys.exit (1) > + > + try: > + FmpAuthHeader.MonotonicCount =3D args.MonotonicCount > + FmpAuthHeader.CertData =3D CertData > + FmpAuthHeader.Payload =3D Result > + Result =3D FmpAuthHeader.Encode () > + if args.Verbose: > + FmpAuthHeader.DumpInfo () > + except: > + print ('GenerateCapsule: error: can not encode FMP Auth Head= er') > + sys.exit (1) > + > + try: > + FmpCapsuleHeader.AddPayload (args.Guid, Result, HardwareInst= ance =3D args.HardwareInstance) > + Result =3D FmpCapsuleHeader.Encode () > + if args.Verbose: > + FmpCapsuleHeader.DumpInfo () > + except: > + print ('GenerateCapsule: error: can not encode FMP Capsule H= eader') > + sys.exit (1) > + > + try: > + UefiCapsuleHeader.OemFlags =3D args.CapsuleOemFla= g > + UefiCapsuleHeader.PersistAcrossReset =3D 'PersistAcrossRese= t' in args.CapsuleFlag > + UefiCapsuleHeader.PopulateSystemTable =3D 'PopulateSystemTab= le' in args.CapsuleFlag > + UefiCapsuleHeader.InitiateReset =3D 'InitiateReset' = in args.CapsuleFlag > + UefiCapsuleHeader.Payload =3D Result > + Result =3D 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 =3D UefiCapsuleHeader.Decode (Buffer) > + FmpCapsuleHeader.Decode (Result) > + Result =3D FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Pay= load > + Result =3D FmpAuthHeader.Decode (Result) > + > + # > + # Verify Image with 64-bit MonotonicCount appended to end of= image > + # > + try: > + if args.SignToolPfxFile is not None: > + CertData =3D VerifyPayloadSignTool ( > + FmpAuthHeader.Payload + struct.pack (' + FmpAuthHeader.CertData, > + args.SigningToolPath, > + args.SignToolPfxFile > + ) > + else: > + CertData =3D VerifyPayloadOpenSsl ( > + FmpAuthHeader.Payload + struct.pack (' + FmpAuthHeader.CertData, > + args.SigningToolPath, > + args.OpenSslSignerPrivateCertFile, > + args.OpenSslOtherPublicCertFile, > + args.OpenSslTrustedPublicCertFile > + ) > + except ValueError: > + print ('GenerateCapsule: warning: can not verify payload= .') > + > + Result =3D FmpPayloadHeader.Decode (Result) > + if args.Verbose: > + print ('=3D=3D=3D=3D=3D=3D=3D=3D') > + UefiCapsuleHeader.DumpInfo () > + print ('--------') > + FmpCapsuleHeader.DumpInfo () > + print ('--------') > + FmpAuthHeader.DumpInfo () > + print ('--------') > + FmpPayloadHeader.DumpInfo () > + print ('=3D=3D=3D=3D=3D=3D=3D=3D') > + except: > + print ('GenerateCapsule: error: can not decode capsule') > + raise > + sys.exit (1) > + > + elif args.DumpInfo: > + try: > + Result =3D UefiCapsuleHeader.Decode (Buffer) > + FmpCapsuleHeader.Decode (Result) > + Result =3D FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Pay= load > + Result =3D FmpAuthHeader.Decode (Result) > + Result =3D FmpPayloadHeader.Decode (Result) > + > + print ('=3D=3D=3D=3D=3D=3D=3D=3D') > + UefiCapsuleHeader.DumpInfo () > + print ('--------') > + FmpCapsuleHeader.DumpInfo () > + print ('--------') > + FmpAuthHeader.DumpInfo () > + print ('--------') > + FmpPayloadHeader.DumpInfo () > + print ('=3D=3D=3D=3D=3D=3D=3D=3D') > + 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 = =3D args.OutputFile.name)) > + args.OutputFile.write (Result) > + args.OutputFile.close () > + except: > + print ('GenerateCapsule: error: can not write binary output = file {File}'.format (File =3D 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.
> +# 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 I= MPLIED. > +# > + > +''' > +FmpPayloadHeader > +''' > + > +import struct > + > +def _SIGNATURE_32 (A, B, C, D): > + return struct.unpack ('=3DI',bytearray (A + B + C + D, 'ascii'))[0] > + > +def _SIGNATURE_32_TO_STRING (Signature): > + return struct.pack (" + > +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 =3D ' + _StructSize =3D struct.calcsize (_StructFormat) > + > + _FMP_PAYLOAD_HEADER_SIGNATURE =3D _SIGNATURE_32 ('M', 'S', 'S', '1') > + > + def __init__ (self): > + self._Valid =3D False > + self.Signature =3D self._FMP_PAYLOAD_HEADER_SIGNATU= RE > + self.HeaderSize =3D self._StructSize > + self.FwVersion =3D 0x00000000 > + self.LowestSupportedVersion =3D 0x00000000 > + self.Payload =3D b'' > + > + def Encode (self): > + FmpPayloadHeader =3D struct.pack ( > + self._StructFormat, > + self.Signature, > + self.HeaderSize, > + self.FwVersion, > + self.LowestSupportedVersion > + ) > + self._Valid =3D True > + return FmpPayloadHeader + self.Payload > + > + def Decode (self, Buffer): > + if len (Buffer) < self._StructSize: > + raise ValueError > + (Signature, HeaderSize, FwVersion, LowestSupportedVersion) =3D \ > + struct.unpack ( > + self._StructFormat, > + Buffer[0:self._StructSize] > + ) > + if Signature !=3D self._FMP_PAYLOAD_HEADER_SIGNATURE: > + raise ValueError > + if HeaderSize < self._StructSize: > + raise ValueError > + self.Signature =3D Signature > + self.HeaderSize =3D HeaderSize > + self.FwVersion =3D FwVersion > + self.LowestSupportedVersion =3D LowestSupportedVersion > + self.Payload =3D Buffer[self.HeaderSize:] > + > + self._Valid =3D True > + return self.Payload > + > + def DumpInfo (self): > + if not self._Valid: > + raise ValueError > + print ('FMP_PAYLOAD_HEADER.Signature =3D {Signature= :08X} ({SignatureString})'.format (Signature =3D > self.Signature, SignatureString =3D _SIGNATURE_32_TO_STRING (self.Signatu= re))) > + print ('FMP_PAYLOAD_HEADER.HeaderSize =3D {HeaderSiz= e:08X}'.format (HeaderSize =3D self.HeaderSize)) > + print ('FMP_PAYLOAD_HEADER.FwVersion =3D {FwVersion= :08X}'.format (FwVersion =3D self.FwVersion)) > + print ('FMP_PAYLOAD_HEADER.LowestSupportedVersion =3D {LowestSup= portedVersion:08X}'.format > (LowestSupportedVersion =3D self.LowestSupportedVersion)) > + print ('sizeof (Payload) =3D {Size:08X}= '.format (Size =3D 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.
> +# 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 I= MPLIED. > +# > 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.
> +# 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 I= MPLIED. > +# > 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 wi= th > +# certificate data and payload data. > +# > +# Copyright (c) 2018, Intel Corporation. All rights reserved.
> +# 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 I= MPLIED. > +# > + > +''' > +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 e= nsure 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 th= e private key that is > + # /// associated with a public key that has been provisioned via t= he 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 signatu= re > + # /// > + # 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 structur= e. > + # /// > + # UINT8 CertData[1]; > + # } WIN_CERTIFICATE_UEFI_GUID; > + # > + # /// > + # /// The WIN_CERTIFICATE structure is part of the PE/COFF specifica= tion. > + # /// > + # 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 v= ersion 1.5 [RFC2315] > + # /// SignedData value. > + # /// > + # #define EFI_CERT_TYPE_PKCS7_GUID \ > + # { \ > + # 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x5= 6, 0x65, 0xa7} \ > + # } > + > + _StructFormat =3D ' + _StructSize =3D struct.calcsize (_StructFormat) > + > + _MonotonicCountFormat =3D ' + _MonotonicCountSize =3D struct.calcsize (_MonotonicCountFormat) > + > + _StructAuthInfoFormat =3D ' + _StructAuthInfoSize =3D struct.calcsize (_StructAuthInfoFormat) > + > + _WIN_CERT_REVISION =3D 0x0200 > + _WIN_CERT_TYPE_EFI_GUID =3D 0x0EF1 > + _EFI_CERT_TYPE_PKCS7_GUID =3D uuid.UUID ('4aafd29d-68df-49ee-8aa9-34= 7d375665a7') > + > + def __init__ (self): > + self._Valid =3D False > + self.MonotonicCount =3D 0 > + self.dwLength =3D self._StructAuthInfoSize > + self.wRevision =3D self._WIN_CERT_REVISION > + self.wCertificateType =3D self._WIN_CERT_TYPE_EFI_GUID > + self.CertType =3D self._EFI_CERT_TYPE_PKCS7_GUID > + self.CertData =3D b'' > + self.Payload =3D b'' > + > + > + def Encode (self): > + if self.wRevision !=3D self._WIN_CERT_REVISION: > + raise ValueError > + if self.wCertificateType !=3D self._WIN_CERT_TYPE_EFI_GUID: > + raise ValueError > + if self.CertType !=3D self._EFI_CERT_TYPE_PKCS7_GUID: > + raise ValueError > + self.dwLength =3D self._StructAuthInfoSize + len (self.CertData) > + > + FmpAuthHeader =3D struct.pack ( > + self._StructFormat, > + self.MonotonicCount, > + self.dwLength, > + self.wRevision, > + self.wCertificateType, > + self.CertType.bytes_le > + ) > + self._Valid =3D True > + > + return FmpAuthHeader + self.CertData + self.Payload > + > + def Decode (self, Buffer): > + if len (Buffer) < self._StructSize: > + raise ValueError > + (MonotonicCount, dwLength, wRevision, wCertificateType, CertType= ) =3D \ > + struct.unpack ( > + self._StructFormat, > + Buffer[0:self._StructSize] > + ) > + if dwLength < self._StructAuthInfoSize: > + raise ValueError > + if wRevision !=3D self._WIN_CERT_REVISION: > + raise ValueError > + if wCertificateType !=3D self._WIN_CERT_TYPE_EFI_GUID: > + raise ValueError > + if CertType !=3D self._EFI_CERT_TYPE_PKCS7_GUID.bytes_le: > + raise ValueError > + self.MonotonicCount =3D MonotonicCount > + self.dwLength =3D dwLength > + self.wRevision =3D wRevision > + self.wCertificateType =3D wCertificateType > + self.CertType =3D uuid.UUID (bytes =3D CertType) > + self.CertData =3D Buffer[self._StructSize:self._Monotoni= cCountSize + self.dwLength] > + self.Payload =3D Buffer[self._MonotonicCountSize + self= .dwLength:] > + self._Valid =3D True > + return self.Payload > + > + def DumpInfo (self): > + if not self._Valid: > + raise ValueError > + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.MonotonicCount = =3D {MonotonicCount:016X}'.format > (MonotonicCount =3D self.MonotonicCount)) > + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.dwLength = =3D {dwLength:08X}'.format (dwLength =3D > self.dwLength)) > + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wRevision= =3D {wRevision:04X}'.format (wRevision =3D > self.wRevision)) > + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.Hdr.wCertific= ateType =3D {wCertificateType:04X}'.format > (wCertificateType =3D self.wCertificateType)) > + print ('EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertType = =3D {Guid}'.format (Guid =3D > str(self.CertType).upper())) > + print ('sizeof (EFI_FIRMWARE_IMAGE_AUTHENTICATION.AuthInfo.CertD= ata) =3D {Size:08X}'.format (Size =3D len > (self.CertData))) > + print ('sizeof (Payload) = =3D {Size:08X}'.format (Size =3D 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_HEAD= ER with > +# a payload. > +# > +# Copyright (c) 2018, Intel Corporation. All rights reserved.
> +# 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 I= MPLIED. > +# > + > +''' > +FmpCapsuleHeader > +''' > + > +import struct > +import uuid > + > +class FmpCapsuleImageHeaderClass (object): > + # typedef struct { > + # UINT32 Version; > + # > + # /// > + # /// Used to identify device firmware targeted by this update. Th= is guid is matched by > + # /// system firmware against ImageTypeId field within a EFI_FIRMW= ARE_IMAGE_DESCRIPTOR > + # /// > + # EFI_GUID UpdateImageTypeId; > + # > + # /// > + # /// Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROT= OCOL.SetImage () > + # /// > + # UINT8 UpdateImageIndex; > + # UINT8 reserved_bytes[3]; > + # > + # /// > + # /// Size of the binary update image which immediately follows th= is structure > + # /// > + # UINT32 UpdateImageSize; > + # > + # /// > + # /// Size of the VendorCode bytes which optionally immediately fo= llow 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 targ= et only a single device in > + # /// cases where there are more than one device with the same Ima= geTypeId 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 =3D ' + _StructSize =3D struct.calcsize (_StructFormat) > + > + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION =3D 0x0000= 0002 > + > + def __init__ (self): > + self._Valid =3D False > + self.Version =3D self.EFI_FIRMWARE_MANAGEMENT_CAP= SULE_IMAGE_HEADER_INIT_VERSION > + self.UpdateImageTypeId =3D uuid.UUID ('00000000-0000-0000-0= 000-000000000000') > + self.UpdateImageIndex =3D 0 > + self.UpdateImageSize =3D 0 > + self.UpdateVendorCodeSize =3D 0 > + self.UpdateHardwareInstance =3D 0x0000000000000000 > + self.Payload =3D b'' > + self.VendorCodeBytes =3D b'' > + > + def Encode (self): > + self.UpdateImageSize =3D len (self.Payload) > + self.UpdateVendorCodeSize =3D len (self.VendorCodeBytes) > + FmpCapsuleImageHeader =3D struct.pack ( > + self._StructFormat, > + self.Version, > + self.UpdateImageTypeId.bytes_le= , > + self.UpdateImageIndex, > + 0,0,0, > + self.UpdateImageSize, > + self.UpdateVendorCodeSize, > + self.UpdateHardwareInstance > + ) > + self._Valid =3D True > + return FmpCapsuleImageHeader + self.Payload + self.VendorCodeByt= es > + > + def Decode (self, Buffer): > + if len (Buffer) < self._StructSize: > + raise ValueError > + (Version, UpdateImageTypeId, UpdateImageIndex, r0, r1, r2, Updat= eImageSize, UpdateVendorCodeSize, > UpdateHardwareInstance) =3D \ > + struct.unpack ( > + self._StructFormat, > + Buffer[0:self._StructSize] > + ) > + > + if Version < self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_I= NIT_VERSION: > + raise ValueError > + if UpdateImageIndex < 1: > + raise ValueError > + if UpdateImageSize + UpdateVendorCodeSize !=3D len (Buffer[self.= _StructSize:]): > + raise ValueError > + > + self.Version =3D Version > + self.UpdateImageTypeId =3D uuid.UUID (bytes_le =3D UpdateIm= ageTypeId) > + self.UpdateImageIndex =3D UpdateImageIndex > + self.UpdateImageSize =3D UpdateImageSize > + self.UpdateVendorCodeSize =3D UpdateVendorCodeSize > + self.UpdateHardwareInstance =3D UpdateHardwareInstance > + self.Payload =3D Buffer[self._StructSize:self._St= ructSize + UpdateImageSize] > + self.VendorCodeBytes =3D Buffer[self._StructSize + Update= ImageSize:] > + self._Valid =3D True > + return Buffer[self._StructSize:] > + > + def DumpInfo (self): > + if not self._Valid: > + raise ValueError > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.Version = =3D {Version:08X}'.format > (Version =3D self.Version)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImage= TypeId =3D > {UpdateImageTypeId}'.format (UpdateImageTypeId =3D str(self.UpdateImageTy= peId).upper())) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImage= Index =3D > {UpdateImageIndex:08X}'.format (UpdateImageIndex =3D self.UpdateImageInde= x)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateImage= Size =3D > {UpdateImageSize:08X}'.format (UpdateImageSize =3D self.UpdateImageSize)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateVendo= rCodeSize =3D > {UpdateVendorCodeSize:08X}'.format (UpdateVendorCodeSize =3D self.UpdateV= endorCodeSize)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.UpdateHardw= areInstance =3D > {UpdateHardwareInstance:016X}'.format (UpdateHardwareInstance =3D self.Up= dateHardwareInstance)) > + print ('sizeof (Payload) = =3D {Size:08X}'.format (Size =3D len > (self.Payload))) > + print ('sizeof (VendorCodeBytes) = =3D {Size:08X}'.format (Size =3D 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 + Pa= yloadItemCount] > + # /// 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 =3D ' + _StructSize =3D struct.calcsize (_StructFormat) > + > + _ItemOffsetFormat =3D ' + _ItemOffsetSize =3D struct.calcsize (_ItemOffsetFormat) > + > + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION =3D 0x00000001 > + > + def __init__ (self): > + self._Valid =3D False > + self.Version =3D self.EFI_FIRMWARE_MANAGEMENT= _CAPSULE_HEADER_INIT_VERSION > + self.EmbeddedDriverCount =3D 0 > + self.PayloadItemCount =3D 0 > + self._ItemOffsetList =3D [] > + self._EmbeddedDriverList =3D [] > + self._PayloadList =3D [] > + self._FmpCapsuleImageHeaderList =3D [] > + > + 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 =3D b'', VendorCode= Bytes =3D b'', HardwareInstance =3D 0): > + self._PayloadList.append ((UpdateImageTypeId, Payload, VendorCod= eBytes, HardwareInstance)) > + > + def GetFmpCapsuleImageHeader (self, Index): > + if Index >=3D len (self._FmpCapsuleImageHeaderList): > + raise ValueError > + return self._FmpCapsuleImageHeaderList[Index] > + > + def Encode (self): > + self.EmbeddedDriverCount =3D len (self._EmbeddedDriverList) > + self.PayloadItemCount =3D len (self._PayloadList) > + > + FmpCapsuleHeader =3D struct.pack ( > + self._StructFormat, > + self.Version, > + self.EmbeddedDriverCount, > + self.PayloadItemCount > + ) > + > + FmpCapsuleData =3D b'' > + Offset =3D self._StructSize + (self.EmbeddedDriverCount + self.P= ayloadItemCount) * self._ItemOffsetSize > + for EmbeddedDriver in self._EmbeddedDriverList: > + FmpCapsuleData =3D FmpCapsuleData + EmbeddedDriver > + self._ItemOffsetList.append (Offset) > + Offset =3D Offset + len (EmbeddedDriver) > + Index =3D 1 > + for (UpdateImageTypeId, Payload, VendorCodeBytes, HardwareInstan= ce) in self._PayloadList: > + FmpCapsuleImageHeader =3D FmpCapsuleImageHeaderClass () > + FmpCapsuleImageHeader.UpdateImageTypeId =3D UpdateImage= TypeId > + FmpCapsuleImageHeader.UpdateImageIndex =3D Index > + FmpCapsuleImageHeader.Payload =3D Payload > + FmpCapsuleImageHeader.VendorCodeBytes =3D VendorCodeB= ytes > + FmpCapsuleImageHeader.UpdateHardwareInstance =3D HardwareIns= tance > + FmpCapsuleImage =3D FmpCapsuleImageHeader.Encode () > + FmpCapsuleData =3D FmpCapsuleData + FmpCapsuleImage > + > + self._ItemOffsetList.append (Offset) > + self._FmpCapsuleImageHeaderList.append (FmpCapsuleImageHeade= r) > + > + Offset =3D Offset + len (FmpCapsuleImage) > + Index =3D Index + 1 > + > + for Offset in self._ItemOffsetList: > + FmpCapsuleHeader =3D FmpCapsuleHeader + struct.pack (self._Ite= mOffsetFormat, Offset) > + > + self._Valid =3D True > + return FmpCapsuleHeader + FmpCapsuleData > + > + def Decode (self, Buffer): > + if len (Buffer) < self._StructSize: > + raise ValueError > + (Version, EmbeddedDriverCount, PayloadItemCount) =3D \ > + struct.unpack ( > + self._StructFormat, > + Buffer[0:self._StructSize] > + ) > + if Version < self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VE= RSION: > + raise ValueError > + > + self.Version =3D Version > + self.EmbeddedDriverCount =3D EmbeddedDriverCount > + self.PayloadItemCount =3D PayloadItemCount > + self._ItemOffsetList =3D [] > + self._EmbeddedDriverList =3D [] > + self._PayloadList =3D [] > + self._FmpCapsuleImageHeaderList =3D [] > + > + # > + # Parse the ItemOffsetList values > + # > + Offset =3D self._StructSize > + for Index in range (0, EmbeddedDriverCount + PayloadItemCount): > + ItemOffset =3D struct.unpack (self._ItemOffsetFormat, Buffer= [Offset:Offset + self._ItemOffsetSize])[0] > + if ItemOffset >=3D len (Buffer): > + raise ValueError > + self._ItemOffsetList.append (ItemOffset) > + Offset =3D Offset + self._ItemOffsetSize > + Result =3D Buffer[Offset:] > + > + # > + # Parse the EmbeddedDrivers > + # > + for Index in range (0, EmbeddedDriverCount): > + Offset =3D self._ItemOffsetList[Index] > + if Index < (len (self._ItemOffsetList) - 1): > + Length =3D self._ItemOffsetList[Index + 1] - Offset > + else: > + Length =3D len (Buffer) - Offset > + self.AddEmbeddedDriver (Buffer[Offset:Offset + Length]) > + > + # > + # Parse the Payloads that are FMP Capsule Images > + # > + for Index in range (EmbeddedDriverCount, EmbeddedDriverCount + P= ayloadItemCount): > + Offset =3D self._ItemOffsetList[Index] > + if Index < (len (self._ItemOffsetList) - 1): > + Length =3D self._ItemOffsetList[Index + 1] - Offset > + else: > + Length =3D len (Buffer) - Offset > + FmpCapsuleImageHeader =3D FmpCapsuleImageHeaderClass () > + FmpCapsuleImageHeader.Decode (Buffer[Offset:Offset + Length]= ) > + self.AddPayload ( > + FmpCapsuleImageHeader.UpdateImageTypeId, > + FmpCapsuleImageHeader.Payload, > + FmpCapsuleImageHeader.VendorCodeBytes > + ) > + self._FmpCapsuleImageHeaderList.append (FmpCapsuleImageHeade= r) > + > + self._Valid =3D True > + return Result > + > + def DumpInfo (self): > + if not self._Valid: > + raise ValueError > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.Version = =3D {Version:08X}'.format (Version =3D > self.Version)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.EmbeddedDriverCou= nt =3D {EmbeddedDriverCount:08X}'.format > (EmbeddedDriverCount =3D self.EmbeddedDriverCount)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.PayloadItemCount = =3D {PayloadItemCount:08X}'.format > (PayloadItemCount =3D self.PayloadItemCount)) > + print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.ItemOffsetList = =3D ') > + for Offset in self._ItemOffsetList: > + print (' {Offset:016X}'.format (Offset =3D Offset)) > + for FmpCapsuleImageHeader in self._FmpCapsuleImageHeaderList: > + FmpCapsuleImageHeader.DumpInfo () > diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/UefiCapsuleHeade= r.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.
> +# 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 I= MPLIED. > +# > + > +''' > +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 =3D '<16sIIII' > + _StructSize =3D struct.calcsize (_StructFormat) > + > + EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID =3D uuid.UUID ('6DCBD5ED-E82= D-4C44-BDA1-7194199AD92A') > + > + _CAPSULE_FLAGS_PERSIST_ACROSS_RESET =3D 0x00010000 > + _CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE =3D 0x00020000 > + _CAPSULE_FLAGS_INITIATE_RESET =3D 0x00040000 > + > + def __init__ (self): > + self._Valid =3D False > + self.CapsuleGuid =3D self.EFI_FIRMWARE_MANAGEMENT_CAPSUL= E_ID_GUID > + self.HeaderSize =3D self._StructSize > + self.OemFlags =3D 0x0000 > + self.PersistAcrossReset =3D False > + self.PopulateSystemTable =3D False > + self.InitiateReset =3D False > + self.CapsuleImageSize =3D self.HeaderSize > + self.Payload =3D b'' > + > + def Encode (self): > + Flags =3D self.OemFlags > + if self.PersistAcrossReset: > + Flags =3D Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET > + if self.PopulateSystemTable: > + Flags =3D Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE > + if self.InitiateReset: > + Flags =3D Flags | self._CAPSULE_FLAGS_INITIATE_RESET > + > + self.CapsuleImageSize =3D self.HeaderSize + len (self.Payload) > + > + UefiCapsuleHeader =3D struct.pack ( > + self._StructFormat, > + self.CapsuleGuid.bytes_le, > + self.HeaderSize, > + Flags, > + self.CapsuleImageSize, > + 0 > + ) > + self._Valid =3D True > + return UefiCapsuleHeader + self.Payload > + > + def Decode (self, Buffer): > + if len (Buffer) < self._StructSize: > + raise ValueError > + (CapsuleGuid, HeaderSize, Flags, CapsuleImageSize, Reserved) =3D= \ > + struct.unpack ( > + self._StructFormat, > + Buffer[0:self._StructSize] > + ) > + if HeaderSize < self._StructSize: > + raise ValueError > + if CapsuleImageSize !=3D len (Buffer): > + raise ValueError > + self.CapsuleGuid =3D uuid.UUID (bytes_le =3D CapsuleGuid= ) > + self.HeaderSize =3D HeaderSize > + self.OemFlags =3D Flags & 0xffff > + self.PersistAcrossReset =3D (Flags & self._CAPSULE_FLAGS_PERSIS= T_ACROSS_RESET) !=3D 0 > + self.PopulateSystemTable =3D (Flags & self._CAPSULE_FLAGS_POPULA= TE_SYSTEM_TABLE) !=3D 0 > + self.InitiateReset =3D (Flags & self._CAPSULE_FLAGS_INITIA= TE_RESET) !=3D 0 > + self.CapsuleImageSize =3D CapsuleImageSize > + self.Payload =3D Buffer[self.HeaderSize:] > + > + self._Valid =3D True > + return self.Payload > + > + def DumpInfo (self): > + if not self._Valid: > + raise ValueError > + Flags =3D self.OemFlags > + if self.PersistAcrossReset: > + Flags =3D Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET > + if self.PopulateSystemTable: > + Flags =3D Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE > + if self.InitiateReset: > + Flags =3D Flags | self._CAPSULE_FLAGS_INITIATE_RESET > + print ('EFI_CAPSULE_HEADER.CapsuleGuid =3D {Guid}'.format (= Guid =3D str(self.CapsuleGuid).upper())) > + print ('EFI_CAPSULE_HEADER.HeaderSize =3D {Size:08X}'.form= at (Size =3D self.HeaderSize)) > + print ('EFI_CAPSULE_HEADER.Flags =3D {Flags:08X}'.for= mat (Flags =3D Flags)) > + print (' OEM Flags =3D {Flags:04X}'.for= mat (Flags =3D 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 =3D {Size:08X}'.form= at (Size =3D self.CapsuleImageSize)) > + print ('sizeof (Payload) =3D {Size:08X}'.form= at (Size =3D 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.
> +# 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 I= MPLIED. > +# > 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.
> +# 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 I= MPLIED. > +# > -- > 2.14.2.windows.3