From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 134.134.136.24, mailfrom: bob.c.feng@intel.com) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by groups.io with SMTP; Wed, 26 Jun 2019 18:27:50 -0700 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 26 Jun 2019 18:27:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,422,1557212400"; d="scan'208";a="164553813" Received: from fmsmsx106.amr.corp.intel.com ([10.18.124.204]) by orsmga003.jf.intel.com with ESMTP; 26 Jun 2019 18:27:49 -0700 Received: from fmsmsx122.amr.corp.intel.com (10.18.125.37) by FMSMSX106.amr.corp.intel.com (10.18.124.204) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 26 Jun 2019 18:27:49 -0700 Received: from shsmsx103.ccr.corp.intel.com (10.239.4.69) by fmsmsx122.amr.corp.intel.com (10.18.125.37) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 26 Jun 2019 18:27:48 -0700 Received: from shsmsx105.ccr.corp.intel.com ([169.254.11.72]) by SHSMSX103.ccr.corp.intel.com ([169.254.4.83]) with mapi id 14.03.0439.000; Thu, 27 Jun 2019 09:27:46 +0800 From: "Bob Feng" To: "Jin, Eric" , "devel@edk2.groups.io" CC: "Gao, Liming" , "Kinney, Michael D" Subject: Re: [PATCH V2] BaseTools/Capsule: Supports multiple payloads and drivers in capsule Thread-Topic: [PATCH V2] BaseTools/Capsule: Supports multiple payloads and drivers in capsule Thread-Index: AQHVK+Tnk0/SKTwmFkuCd4Jo+/B7KKautzOw Date: Thu, 27 Jun 2019 01:27:45 +0000 Message-ID: <08650203BA1BD64D8AD9B6D5D74A85D160B3391C@SHSMSX105.ccr.corp.intel.com> References: <20190626060245.19312-1-eric.jin@intel.com> In-Reply-To: <20190626060245.19312-1-eric.jin@intel.com> Accept-Language: zh-CN, en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.239.127.40] MIME-Version: 1.0 Return-Path: bob.c.feng@intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Reviewed-by: Bob Feng =20 -----Original Message----- From: Jin, Eric=20 Sent: Wednesday, June 26, 2019 2:03 PM To: devel@edk2.groups.io Cc: Feng, Bob C ; Gao, Liming ;= Kinney, Michael D Subject: [PATCH V2] BaseTools/Capsule: Supports multiple payloads and drive= rs in capsule REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D1834 1)Add arguments "--embedded-driver" to support embedded driver in command line. 2)Add arguments "--update-image-index" to identify ImageIndex within the device in command line. 3)Add arguments "-j JSONFILE" to support multiple payloads and embedded drivers with JSON file. The update is in a backwards compatible manner, so all command line options to support single payload are still supported. But all the options associated with multiple payloads should be provided in a JSON file. Cc: Bob Feng Cc: Liming Gao Cc: Kinney Michael D Signed-off-by: Eric Jin --- V1: Initial version to support multiple payloads and drivers V2: Enhance exception handle and file operation .../Source/Python/Capsule/GenerateCapsule.py | 971 +++++++++++++----- .../Common/Uefi/Capsule/FmpAuthHeader.py | 14 +- .../Common/Uefi/Capsule/FmpCapsuleHeader.py | 12 +- 3 files changed, 731 insertions(+), 266 deletions(-) diff --git a/BaseTools/Source/Python/Capsule/GenerateCapsule.py b/BaseTools= /Source/Python/Capsule/GenerateCapsule.py index 4de3635298..6838beb682 100644 --- a/BaseTools/Source/Python/Capsule/GenerateCapsule.py +++ b/BaseTools/Source/Python/Capsule/GenerateCapsule.py @@ -1,18 +1,16 @@ ## @file # Generate a capsule. # -# This tool generates a UEFI Capsule around an FMP Capsule. The capsule p= ayload +# This tool generates a UEFI Capsule around an FMP Capsule. The capsule pa= yload # be signed using signtool or OpenSSL and if it is signed the signed conte= nt # includes an FMP Payload Header. # # This tool is intended to be used to generate UEFI Capsules to update the -# system firmware or device firmware for integrated devices. In order to +# system firmware or device firmware for integrated devices. In order to # keep the tool as simple as possible, it has the following limitations: -# * Do not support multiple payloads in a capsule. -# * Do not support optional drivers in a capsule. # * Do not support vendor code bytes in a capsule. # -# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # =20 @@ -29,6 +27,7 @@ import os import tempfile import shutil import platform +import json from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass @@ -42,7 +41,7 @@ __version__ =3D '0.9' __copyright__ =3D 'Copyright (c) 2018, Intel Corporation. All rights res= erved.' __description__ =3D 'Generate a capsule.\n' =20 -def SignPayloadSignTool (Payload, ToolPath, PfxFile): +def SignPayloadSignTool (Payload, ToolPath, PfxFile, Verbose =3D False): # # Create a temporary directory # @@ -57,9 +56,8 @@ def SignPayloadSignTool (Payload, ToolPath, PfxFile): # Create temporary payload file for signing # try: - File =3D open (TempFileName, mode=3D'wb') - File.write (Payload) - File.close () + with open (TempFileName, 'wb') as File: + File.write (Payload) except: shutil.rmtree (TempDirectoryName) raise ValueError ('GenerateCapsule: error: can not write temporary= payload file.') @@ -75,6 +73,8 @@ def SignPayloadSignTool (Payload, ToolPath, PfxFile): Command =3D Command + '/p7 {TempDir} '.format (TempDir =3D TempDirecto= ryName) Command =3D Command + '/f {PfxFile} '.format (PfxFile =3D PfxFile) Command =3D Command + TempFileName + if Verbose: + print (Command) =20 # # Sign the input file using the specified private key @@ -88,16 +88,15 @@ def SignPayloadSignTool (Payload, ToolPath, PfxFile): =20 if Process.returncode !=3D 0: shutil.rmtree (TempDirectoryName) - print (Result[1].decode(encoding=3D'utf-8', errors=3D'ignore')) + print (Result[1].decode()) raise ValueError ('GenerateCapsule: error: signtool failed.') =20 # # Read the signature from the generated output file # try: - File =3D open (TempFileName + '.p7', mode=3D'rb') - Signature =3D File.read () - File.close () + with open (TempFileName + '.p7', 'rb') as File: + Signature =3D File.read () except: shutil.rmtree (TempDirectoryName) raise ValueError ('GenerateCapsule: error: can not read signature = file.') @@ -105,11 +104,11 @@ def SignPayloadSignTool (Payload, ToolPath, PfxFile): shutil.rmtree (TempDirectoryName) return Signature =20 -def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile): +def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile, Verbose = =3D False): print ('signtool verify is not supported.') raise ValueError ('GenerateCapsule: error: signtool verify is not supp= orted.') =20 -def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPub= licCertFile, TrustedPublicCertFile): +def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPub= licCertFile, TrustedPublicCertFile, Verbose =3D False): # # Build openssl command # @@ -119,6 +118,8 @@ def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivat= eCertFile, OtherPublicCer Command =3D Command + '"{Path}" '.format (Path =3D os.path.join (ToolP= ath, 'openssl')) Command =3D Command + 'smime -sign -binary -outform DER -md sha256 ' Command =3D Command + '-signer "{Private}" -certfile "{Public}"'.forma= t (Private =3D SignerPrivateCertFile, Public =3D OtherPublicCertFile) + if Verbose: + print (Command) =20 # # Sign the input file using the specified private key and capture sign= ature from STDOUT @@ -131,12 +132,12 @@ def SignPayloadOpenSsl (Payload, ToolPath, SignerPriv= ateCertFile, OtherPublicCer raise ValueError ('GenerateCapsule: error: can not run openssl.') =20 if Process.returncode !=3D 0: - print (Result[1].decode(encoding=3D'utf-8', errors=3D'ignore')) + print (Result[1].decode()) raise ValueError ('GenerateCapsule: error: openssl failed.') =20 return Signature =20 -def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFi= le, OtherPublicCertFile, TrustedPublicCertFile): +def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFi= le, OtherPublicCertFile, TrustedPublicCertFile, Verbose =3D False): # # Create a temporary directory # @@ -151,9 +152,8 @@ def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, = SignerPrivateCertFile, Ot # Create temporary payload file for verification # try: - File =3D open (TempFileName, mode=3D'wb') - File.write (Payload) - File.close () + with open (TempFileName, 'wb') as File: + File.write (Payload) except: shutil.rmtree (TempDirectoryName) raise ValueError ('GenerateCapsule: error: can not write temporary= payload file.') @@ -167,6 +167,8 @@ def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, = SignerPrivateCertFile, Ot Command =3D Command + '"{Path}" '.format (Path =3D os.path.join (ToolP= ath, 'openssl')) Command =3D Command + 'smime -verify -inform DER ' Command =3D Command + '-content {Content} -CAfile "{Public}"'.format (= Content =3D TempFileName, Public =3D TrustedPublicCertFile) + if Verbose: + print (Command) =20 # # Verify signature @@ -180,7 +182,7 @@ def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, = SignerPrivateCertFile, Ot =20 if Process.returncode !=3D 0: shutil.rmtree (TempDirectoryName) - print (Result[1].decode(encoding=3D'utf-8', errors=3D'ignore')) + print (Result[1].decode()) raise ValueError ('GenerateCapsule: error: openssl failed.') =20 shutil.rmtree (TempDirectoryName) @@ -212,6 +214,655 @@ if __name__ =3D=3D '__main__': raise argparse.ArgumentTypeError (Message) return Value =20 + def ConvertJsonValue (Config, FieldName, Convert, Required =3D True, D= efault =3D None, Open =3D False): + if FieldName not in Config: + if Required: + print ('GenerateCapsule: error: Payload descriptor invalid= syntax. Could not find {Key} in payload descriptor.'.format(Key =3D FieldN= ame)) + sys.exit (1) + return Default + try: + Value =3D Convert (Config[FieldName]) + except: + print ('GenerateCapsule: error: {Key} in payload descriptor ha= s invalid syntax.'.format (Key =3D FieldName)) + sys.exit (1) + if Open: + try: + Value =3D open (Value, "rb") + except: + print ('GenerateCapsule: error: can not open file {File}'.= format (File =3D FieldName)) + sys.exit (1) + return Value + + def DecodeJsonFileParse (Json): + if 'Payloads' not in Json: + print ('GenerateCapsule: error "Payloads" section not found in= JSON file {File}'.format (File =3D args.JsonFile.name)) + sys.exit (1) + for Config in Json['Payloads']: + # + # Parse fields from JSON + # + PayloadFile =3D ConvertJsonValue (Config, 'Pa= yload', os.path.expandvars, Required =3D False) + Guid =3D ConvertJsonValue (Config, 'Gu= id', ValidateRegistryFormatGuid, Required =3D False) + FwVersion =3D ConvertJsonValue (Config, 'Fw= Version', ValidateUnsignedInteger, Required =3D False) + LowestSupportedVersion =3D ConvertJsonValue (Config, 'Lo= westSupportedVersion', ValidateUnsignedInteger, Required =3D False) + HardwareInstance =3D ConvertJsonValue (Config, 'Ha= rdwareInstance', ValidateUnsignedInteger, Required =3D False, Default =3D 0= ) + MonotonicCount =3D ConvertJsonValue (Config, 'Mo= notonicCount', ValidateUnsignedInteger, Required =3D False, Default =3D 0) + SignToolPfxFile =3D ConvertJsonValue (Config, 'Si= gnToolPfxFile', os.path.expandvars, Required =3D False, Default =3D None, O= pen =3D True) + OpenSslSignerPrivateCertFile =3D ConvertJsonValue (Config, 'Op= enSslSignerPrivateCertFile', os.path.expandvars, Required =3D False, Defaul= t =3D None, Open =3D True) + OpenSslOtherPublicCertFile =3D ConvertJsonValue (Config, 'Op= enSslOtherPublicCertFile', os.path.expandvars, Required =3D False, Default = =3D None, Open =3D True) + OpenSslTrustedPublicCertFile =3D ConvertJsonValue (Config, 'Op= enSslTrustedPublicCertFile', os.path.expandvars, Required =3D False, Defaul= t =3D None, Open =3D True) + SigningToolPath =3D ConvertJsonValue (Config, 'Si= gningToolPath', os.path.expandvars, Required =3D False, Default =3D None) + UpdateImageIndex =3D ConvertJsonValue (Config, 'Up= dateImageIndex', ValidateUnsignedInteger, Required =3D False, Default =3D 1= ) + + PayloadDescriptorList.append (PayloadDescriptor ( + PayloadFile, + Guid, + FwVersion, + LowestSupportedVersion, + MonotonicCount, + HardwareInstance, + UpdateImageIndex, + SignToolPfxFile, + OpenSslSignerPrivateCertFile, + OpenSslOtherPublicCertFile, + OpenSslTrustedPublicCertFile, + SigningToolPath + )) + + def EncodeJsonFileParse (Json): + if 'EmbeddedDrivers' not in Json: + print ('GenerateCapsule: warning "EmbeddedDrivers" section not= found in JSON file {File}'.format (File =3D args.JsonFile.name)) + else: + for Config in Json['EmbeddedDrivers']: + EmbeddedDriverFile =3D ConvertJsonValue(Config, 'Driv= er', os.path.expandvars, Open =3D True) + # + #Read EmbeddedDriver file + # + try: + if args.Verbose: + print ('Read EmbeddedDriver file {File}'.format (F= ile =3D EmbeddedDriverFile.name)) + Driver =3D EmbeddedDriverFile.read() + except: + print ('GenerateCapsule: error: can not read EmbeddedD= river file {File}'.format (File =3D EmbeddedDriverFile.name)) + sys.exit (1) + EmbeddedDriverDescriptorList.append (Driver) + + if 'Payloads' not in Json: + print ('GenerateCapsule: error: "Payloads" section not found i= n JSON file {File}'.format (File =3D args.JsonFile.name)) + sys.exit (1) + for Config in Json['Payloads']: + # + # Parse fields from JSON + # + PayloadFile =3D ConvertJsonValue (Config, 'Pa= yload', os.path.expandvars, Open =3D True) + Guid =3D ConvertJsonValue (Config, 'Gu= id', ValidateRegistryFormatGuid) + FwVersion =3D ConvertJsonValue (Config, 'Fw= Version', ValidateUnsignedInteger) + LowestSupportedVersion =3D ConvertJsonValue (Config, 'Lo= westSupportedVersion', ValidateUnsignedInteger) + HardwareInstance =3D ConvertJsonValue (Config, 'Ha= rdwareInstance', ValidateUnsignedInteger, Required =3D False, Default =3D 0= ) + UpdateImageIndex =3D ConvertJsonValue (Config, 'Up= dateImageIndex', ValidateUnsignedInteger, Required =3D False, Default =3D 1= ) + MonotonicCount =3D ConvertJsonValue (Config, 'Mo= notonicCount', ValidateUnsignedInteger, Required =3D False, Default =3D 0) + SignToolPfxFile =3D ConvertJsonValue (Config, 'Si= gnToolPfxFile', os.path.expandvars, Required =3D False, Default =3D None, O= pen =3D True) + OpenSslSignerPrivateCertFile =3D ConvertJsonValue (Config, 'Op= enSslSignerPrivateCertFile', os.path.expandvars, Required =3D False, Defaul= t =3D None, Open =3D True) + OpenSslOtherPublicCertFile =3D ConvertJsonValue (Config, 'Op= enSslOtherPublicCertFile', os.path.expandvars, Required =3D False, Default = =3D None, Open =3D True) + OpenSslTrustedPublicCertFile =3D ConvertJsonValue (Config, 'Op= enSslTrustedPublicCertFile', os.path.expandvars, Required =3D False, Defaul= t =3D None, Open =3D True) + SigningToolPath =3D ConvertJsonValue (Config, 'Si= gningToolPath', os.path.expandvars, Required =3D False, Default =3D None) + + # + # Read binary input file + # + try: + if args.Verbose: + print ('Read binary input file {File}'.format (File = =3D PayloadFile.name)) + Payload =3D PayloadFile.read() + PayloadFile.close () + except: + print ('GenerateCapsule: error: can not read binary input = file {File}'.format (File =3D PayloadFile.name)) + sys.exit (1) + PayloadDescriptorList.append (PayloadDescriptor ( + Payload, + Guid, + FwVersion, + LowestSupportedVersion, + MonotonicCount, + HardwareInstance, + UpdateImageIndex, + SignToolPfxFile, + OpenSslSignerPrivateCertFile, + OpenSslOtherPublicCertFile, + OpenSslTrustedPublicCertFile, + SigningToolPath + )) + + def GenerateOutputJson (PayloadJsonDescriptorList): + PayloadJson =3D { + "Payloads" : [ + { + "Guid": str(PayloadDescriptor.Guid).uppe= r(), + "FwVersion": str(PayloadDescriptor.FwVer= sion), + "LowestSupportedVersion": str(PayloadDes= criptor.LowestSupportedVersion), + "MonotonicCount": str(PayloadDescriptor.= MonotonicCount), + "Payload": PayloadDescriptor.Payload, + "HardwareInstance": str(PayloadDescripto= r.HardwareInstance), + "UpdateImageIndex": str(PayloadDescripto= r.UpdateImageIndex), + "SignToolPfxFile": str(PayloadDescriptor= .SignToolPfxFile), + "OpenSslSignerPrivateCertFile": str(Payl= oadDescriptor.OpenSslSignerPrivateCertFile), + "OpenSslOtherPublicCertFile": str(Payloa= dDescriptor.OpenSslOtherPublicCertFile), + "OpenSslTrustedPublicCertFile": str(Payl= oadDescriptor.OpenSslTrustedPublicCertFile), + "SigningToolPath": str(PayloadDescriptor= .SigningToolPath) + }for PayloadDescriptor in PayloadJsonDescrip= torList + ] + } + OutputJsonFile =3D args.OutputFile.name + '.json' + if 'Payloads' in PayloadJson: + PayloadSection =3D PayloadJson ['Payloads'] + Index =3D 0 + for PayloadField in PayloadSection: + if PayloadJsonDescriptorList[Index].SignToolPfxFile is None: + del PayloadField ['SignToolPfxFile'] + if PayloadJsonDescriptorList[Index].OpenSslSignerPrivateCertFi= le is None: + del PayloadField ['OpenSslSignerPrivateCertFile'] + if PayloadJsonDescriptorList[Index].OpenSslOtherPublicCertFile= is None: + del PayloadField ['OpenSslOtherPublicCertFile'] + if PayloadJsonDescriptorList[Index].OpenSslTrustedPublicCertFi= le is None: + del PayloadField ['OpenSslTrustedPublicCertFile'] + if PayloadJsonDescriptorList[Index].SigningToolPath is None: + del PayloadField ['SigningToolPath'] + Index =3D Index + 1 + Result =3D json.dumps (PayloadJson, indent=3D4, sort_keys=3DTrue, = separators=3D(',', ': ')) + with open (OutputJsonFile, 'w') as OutputFile: + OutputFile.write (Result) + + def CheckArgumentConflict (args): + if args.Encode: + if args.InputFile: + print ('GenerateCapsule: error: Argument InputFile conflic= ts with Argument -j') + sys.exit (1) + if args.EmbeddedDriver: + print ('GenerateCapsule: error: Argument --embedded-driver= conflicts with Argument -j') + sys.exit (1) + if args.Guid: + print ('GenerateCapsule: error: Argument --guid conflicts with= Argument -j') + sys.exit (1) + if args.FwVersion: + print ('GenerateCapsule: error: Argument --fw-version conflict= s with Argument -j') + sys.exit (1) + if args.LowestSupportedVersion: + print ('GenerateCapsule: error: Argument --lsv conflicts with = Argument -j') + sys.exit (1) + if args.MonotonicCount: + print ('GenerateCapsule: error: Argument --monotonic-count con= flicts with Argument -j') + sys.exit (1) + if args.HardwareInstance: + print ('GenerateCapsule: error: Argument --hardware-instance c= onflicts with Argument -j') + sys.exit (1) + if args.SignToolPfxFile: + print ('GenerateCapsule: error: Argument --pfx-file conflicts = with Argument -j') + sys.exit (1) + if args.OpenSslSignerPrivateCertFile: + print ('GenerateCapsule: error: Argument --signer-private-cert= conflicts with Argument -j') + sys.exit (1) + if args.OpenSslOtherPublicCertFile: + print ('GenerateCapsule: error: Argument --other-public-cert c= onflicts with Argument -j') + sys.exit (1) + if args.OpenSslTrustedPublicCertFile: + print ('GenerateCapsule: error: Argument --trusted-public-cert= conflicts with Argument -j') + sys.exit (1) + if args.SigningToolPath: + print ('GenerateCapsule: error: Argument --signing-tool-path c= onflicts with Argument -j') + sys.exit (1) + + class PayloadDescriptor (object): + def __init__(self, + Payload, + Guid, + FwVersion, + LowestSupportedVersion, + MonotonicCount =3D 0, + HardwareInstance =3D 0, + UpdateImageIndex =3D 1, + SignToolPfxFile =3D None, + OpenSslSignerPrivateCertFile =3D None, + OpenSslOtherPublicCertFile =3D None, + OpenSslTrustedPublicCertFile =3D None, + SigningToolPath =3D None + ): + self.Payload =3D Payload + self.Guid =3D Guid + self.FwVersion =3D FwVersion + self.LowestSupportedVersion =3D LowestSupportedVersion + self.MonotonicCount =3D MonotonicCount + self.HardwareInstance =3D HardwareInstance + self.UpdateImageIndex =3D UpdateImageIndex + self.SignToolPfxFile =3D SignToolPfxFile + self.OpenSslSignerPrivateCertFile =3D OpenSslSignerPrivateCert= File + self.OpenSslOtherPublicCertFile =3D OpenSslOtherPublicCertFi= le + self.OpenSslTrustedPublicCertFile =3D OpenSslTrustedPublicCert= File + self.SigningToolPath =3D SigningToolPath + + self.UseSignTool =3D self.SignToolPfxFile is not None + self.UseOpenSsl =3D (self.OpenSslSignerPrivateCertFile is not= None and + self.OpenSslOtherPublicCertFile is not Non= e and + self.OpenSslTrustedPublicCertFile is not N= one) + self.AnyOpenSsl =3D (self.OpenSslSignerPrivateCertFile is not= None or + self.OpenSslOtherPublicCertFile is not Non= e or + self.OpenSslTrustedPublicCertFile is not N= one) + + def Validate(self, args): + if self.UseSignTool and self.AnyOpenSsl: + raise argparse.ArgumentTypeError ('Providing both signtool= and OpenSSL options is not supported') + if not self.UseSignTool and not self.UseOpenSsl and self.AnyOp= enSsl: + if args.JsonFile: + raise argparse.ArgumentTypeError ('the following JSON = fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOther= PublicCertFile, OpenSslTrustedPublicCertFile') + else: + raise argparse.ArgumentTypeError ('the following optio= ns are required for OpenSSL: --signer-private-cert, --other-public-cert, --= trusted-public-cert') + if self.UseSignTool and platform.system() !=3D 'Windows': + raise argparse.ArgumentTypeError ('Use of signtool is not = supported on this operating system.') + if args.Encode: + if self.FwVersion is None or self.LowestSupportedVersion i= s None: + if args.JsonFile: + raise argparse.ArgumentTypeError ('the following J= SON fields are required: FwVersion, LowestSupportedVersion') + else: + raise argparse.ArgumentTypeError ('the following o= ptions are required: --fw-version, --lsv') + if self.FwVersion > 0xFFFFFFFF: + if args.JsonFile: + raise argparse.ArgumentTypeError ('JSON field FwVe= rsion must be an integer in range 0x0..0xffffffff') + else: + raise argparse.ArgumentTypeError ('--fw-version mu= st be an integer in range 0x0..0xffffffff') + if self.LowestSupportedVersion > 0xFFFFFFFF: + if args.JsonFile: + raise argparse.ArgumentTypeError ('JSON field Lowe= stSupportedVersion must be an integer in range 0x0..0xffffffff') + else: + raise argparse.ArgumentTypeError ('--lsv must be a= n integer in range 0x0..0xffffffff') + + if args.Encode: + if self.Guid is None: + if args.JsonFile: + raise argparse.ArgumentTypeError ('the following J= SON field is required: Guid') + else: + raise argparse.ArgumentTypeError ('the following o= ption is required: --guid') + if self.HardwareInstance > 0xFFFFFFFFFFFFFFFF: + if args.JsonFile: + raise argparse.ArgumentTypeError ('JSON field Hard= wareInstance must be an integer in range 0x0..0xffffffffffffffff') + else: + raise argparse.ArgumentTypeError ('--hardware-inst= ance must be an integer in range 0x0..0xffffffffffffffff') + if self.MonotonicCount > 0xFFFFFFFFFFFFFFFF: + if args.JsonFile: + raise argparse.ArgumentTypeError ('JSON field Mono= tonicCount must be an integer in range 0x0..0xffffffffffffffff') + else: + raise argparse.ArgumentTypeError ('--monotonic-cou= nt must be an integer in range 0x0..0xffffffffffffffff') + if self.UpdateImageIndex >0xFF: + if args.JsonFile: + raise argparse.ArgumentTypeError ('JSON field Upda= teImageIndex must be an integer in range 0x0..0xff') + else: + raise argparse.ArgumentTypeError ('--update-image-= index must be an integer in range 0x0..0xff') + + if self.UseSignTool: + self.SignToolPfxFile.close() + self.SignToolPfxFile =3D self.SignToolPfxFile.name + if self.UseOpenSsl: + self.OpenSslSignerPrivateCertFile.close() + self.OpenSslOtherPublicCertFile.close() + self.OpenSslTrustedPublicCertFile.close() + self.OpenSslSignerPrivateCertFile =3D self.OpenSslSignerPr= ivateCertFile.name + self.OpenSslOtherPublicCertFile =3D self.OpenSslOtherPub= licCertFile.name + self.OpenSslTrustedPublicCertFile =3D self.OpenSslTrustedP= ublicCertFile.name + + # + # Perform additional argument verification + # + if args.Encode: + if 'PersistAcrossReset' not in args.CapsuleFlag: + if 'InitiateReset' in args.CapsuleFlag: + raise argparse.ArgumentTypeError ('--capflag Initi= ateReset also requires --capflag PersistAcrossReset') + if args.CapsuleOemFlag > 0xFFFF: + raise argparse.ArgumentTypeError ('--capoemflag must b= e an integer between 0x0000 and 0xffff') + + return True + + + def Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffe= r): + if args.JsonFile: + CheckArgumentConflict(args) + try: + Json =3D json.loads (args.JsonFile.read ()) + except: + print ('GenerateCapsule: error: {JSONFile} loads failure. = '.format (JSONFile =3D args.JsonFile)) + sys.exit (1) + EncodeJsonFileParse(Json) + else: + for Driver in args.EmbeddedDriver: + EmbeddedDriverDescriptorList.append (Driver.read()) + PayloadDescriptorList.append (PayloadDescriptor ( + Buffer, + args.Guid, + args.FwVersion, + args.LowestSupportedVersion, + args.MonotonicCount, + args.HardwareInstance, + args.UpdateImageIndex, + args.SignToolPfxFile, + args.OpenSslSignerPrivateCertF= ile, + args.OpenSslOtherPublicCertFil= e, + args.OpenSslTrustedPublicCertF= ile, + args.SigningToolPath + )) + for SinglePayloadDescriptor in PayloadDescriptorList: + try: + SinglePayloadDescriptor.Validate (args) + except Exception as Msg: + print ('GenerateCapsule: error:' + str(Msg)) + sys.exit (1) + for SinglePayloadDescriptor in PayloadDescriptorList: + Result =3D SinglePayloadDescriptor.Payload + try: + FmpPayloadHeader.FwVersion =3D SinglePayloadD= escriptor.FwVersion + FmpPayloadHeader.LowestSupportedVersion =3D SinglePayloadD= escriptor.LowestSupportedVersion + FmpPayloadHeader.Payload =3D SinglePayloadD= escriptor.Payload + Result =3D FmpPayloadHeader.Encode () + if args.Verbose: + FmpPayloadHeader.DumpInfo () + except: + print ('GenerateCapsule: error: can not encode FMP Payload= Header') + sys.exit (1) + if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescript= or.UseSignTool: + # + # Sign image with 64-bit MonotonicCount appended to end of= image + # + try: + if SinglePayloadDescriptor.UseSignTool: + CertData =3D SignPayloadSignTool ( + Result + struct.pack (' 0: + Result =3D FmpCapsuleHeader.Decode (Result) + if args.JsonFile: + if FmpCapsuleHeader.PayloadItemCount !=3D len (Payload= DescriptorList): + CapsulePayloadNum =3D FmpCapsuleHeader.PayloadItem= Count + JsonPayloadNum =3D len (PayloadDescriptorList) + print ('GenerateCapsule: Decode error: {JsonPayloa= dNumber} payloads in JSON file {File} and {CapsulePayloadNumber} payloads i= n Capsule {CapsuleName}'.format (JsonPayloadNumber =3D JsonPayloadNum, File= =3D args.JsonFile.name, CapsulePayloadNumber =3D CapsulePayloadNum, Capsul= eName =3D args.InputFile.name)) + sys.exit (1) + for Index in range (0, FmpCapsuleHeader.PayloadItemCou= nt): + if Index < len (PayloadDescriptorList): + GUID =3D FmpCapsuleHeader.GetFmpCapsuleImageHe= ader (Index).UpdateImageTypeId + HardwareInstance =3D FmpCapsuleHeader.GetFmpCa= psuleImageHeader (Index).UpdateHardwareInstance + UpdateImageIndex =3D FmpCapsuleHeader.GetFmpCa= psuleImageHeader (Index).UpdateImageIndex + if PayloadDescriptorList[Index].Guid !=3D GUID= or PayloadDescriptorList[Index].HardwareInstance !=3D HardwareInstance: + print ('GenerateCapsule: Decode error: Gui= d or HardwareInstance pair in input JSON file {File} does not match the pay= load {PayloadIndex} in Capsule {InputCapsule}'.format (File =3D args.JsonFi= le.name, PayloadIndex =3D Index + 1, InputCapsule =3D args.InputFile.name)) + sys.exit (1) + PayloadDescriptorList[Index].Payload =3D FmpCa= psuleHeader.GetFmpCapsuleImageHeader (Index).Payload + DecodeJsonOutput =3D args.OutputFile.name + '.= Payload.{Index:d}.bin'.format (Index =3D Index + 1) + PayloadJsonDescriptorList.append (PayloadDescr= iptor ( + DecodeJson= Output, + GUID, + None, + None, + None, + HardwareIn= stance, + UpdateImag= eIndex, + PayloadDes= criptorList[Index].SignToolPfxFile, + PayloadDes= criptorList[Index].OpenSslSignerPrivateCertFile, + PayloadDes= criptorList[Index].OpenSslOtherPublicCertFile, + PayloadDes= criptorList[Index].OpenSslTrustedPublicCertFile, + PayloadDes= criptorList[Index].SigningToolPath + )) + else: + PayloadDescriptorList[0].Payload =3D FmpCapsuleHeader.= GetFmpCapsuleImageHeader (0).Payload + for Index in range (0, FmpCapsuleHeader.PayloadItemCou= nt): + if Index > 0: + PayloadDecodeFile =3D FmpCapsuleHeader.GetFmpC= apsuleImageHeader (Index).Payload + PayloadDescriptorList.append (PayloadDescripto= r (PayloadDecodeFile, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None + )) + GUID =3D FmpCapsuleHeader.GetFmpCapsuleImageHeader= (Index).UpdateImageTypeId + HardwareInstance =3D FmpCapsuleHeader.GetFmpCapsul= eImageHeader (Index).UpdateHardwareInstance + UpdateImageIndex =3D FmpCapsuleHeader.GetFmpCapsul= eImageHeader (Index).UpdateImageIndex + DecodeJsonOutput =3D args.OutputFile.name + '.Payl= oad.{Index:d}.bin'.format (Index =3D Index + 1) + PayloadJsonDescriptorList.append (PayloadDescripto= r ( + DecodeJsonOutp= ut, + GUID, + None, + None, + None, + HardwareInstan= ce, + UpdateImageInd= ex, + PayloadDescrip= torList[Index].SignToolPfxFile, + PayloadDescrip= torList[Index].OpenSslSignerPrivateCertFile, + PayloadDescrip= torList[Index].OpenSslOtherPublicCertFile, + PayloadDescrip= torList[Index].OpenSslTrustedPublicCertFile, + PayloadDescrip= torList[Index].SigningToolPath + )) + JsonIndex =3D 0 + for SinglePayloadDescriptor in PayloadDescriptorList: + if args.Verbose: + print ('=3D=3D=3D=3D=3D=3D=3D=3D') + UefiCapsuleHeader.DumpInfo () + print ('--------') + FmpCapsuleHeader.DumpInfo () + if FmpAuthHeader.IsSigned(SinglePayloadDescriptor.Payl= oad): + if not SinglePayloadDescriptor.UseOpenSsl and not = SinglePayloadDescriptor.UseSignTool: + print ('GenerateCapsule: decode warning: can n= ot verify singed payload without cert or pfx file. Index =3D {Index}'.forma= t (Index =3D JsonIndex + 1)) + SinglePayloadDescriptor.Payload =3D FmpAuthHeader.= Decode (SinglePayloadDescriptor.Payload) + PayloadJsonDescriptorList[JsonIndex].MonotonicCoun= t =3D FmpAuthHeader.MonotonicCount + if args.Verbose: + print ('--------') + FmpAuthHeader.DumpInfo () + + # + # Verify Image with 64-bit MonotonicCount appended= to end of image + # + try: + if SinglePayloadDescriptor.UseSignTool: + CertData =3D VerifyPayloadSignTool ( + FmpAuthHeader.Payload + struct.= pack (' 0: + FmpCapsuleHeader.Decode (Result) + print ('--------') + FmpCapsuleHeader.DumpInfo () + for Index in range (0, FmpCapsuleHeader.PayloadItemCount): + Result =3D FmpCapsuleHeader.GetFmpCapsuleImageHeader (= Index).Payload + try: + Result =3D FmpAuthHeader.Decode (Result) + print ('--------') + FmpAuthHeader.DumpInfo () + except: + print ('--------') + print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION') + try: + Result =3D FmpPayloadHeader.Decode (Result) + print ('--------') + FmpPayloadHeader.DumpInfo () + except: + print ('--------') + print ('No FMP_PAYLOAD_HEADER') + print ('=3D=3D=3D=3D=3D=3D=3D=3D') + except: + print ('GenerateCapsule: error: can not decode capsule') + sys.exit (1) # # Create command line argument parser object # @@ -226,7 +877,7 @@ if __name__ =3D=3D '__main__': # # Add input and output file arguments # - parser.add_argument("InputFile", type =3D argparse.FileType('rb'), + parser.add_argument("InputFile", type =3D argparse.FileType('rb'), na= rgs=3D'?', help =3D "Input binary payload filename.") parser.add_argument("-o", "--output", dest =3D 'OutputFile', type =3D = argparse.FileType('wb'), help =3D "Output filename.") @@ -243,6 +894,8 @@ if __name__ =3D=3D '__main__': # # Add optional arguments for this command # + parser.add_argument ("-j", "--json-file", dest =3D 'JsonFile', type=3D= argparse.FileType('r'), + help =3D "JSON configuration file for multiple pa= yloads and embedded drivers.") parser.add_argument ("--capflag", dest =3D 'CapsuleFlag', action=3D'ap= pend', default =3D [], choices=3D['PersistAcrossReset', 'InitiateReset']= , help =3D "Capsule flag can be PersistAcrossReset = or InitiateReset or not set") @@ -250,7 +903,7 @@ if __name__ =3D=3D '__main__': help =3D "Capsule OEM Flag is an integer between = 0x0000 and 0xffff.") =20 parser.add_argument ("--guid", dest =3D 'Guid', type =3D ValidateRegis= tryFormatGuid, - help =3D "The FMP/ESRT GUID in registry format. = Required for encode operations.") + help =3D "The FMP/ESRT GUID in registry format. = Required for single payload encode operations.") parser.add_argument ("--hardware-instance", dest =3D 'HardwareInstance= ', type =3D ValidateUnsignedInteger, default =3D 0x0000000000000000, help =3D "The 64-bit hardware instance. The defa= ult is 0x0000000000000000") =20 @@ -259,9 +912,9 @@ if __name__ =3D=3D '__main__': help =3D "64-bit monotonic count value in header.= Default is 0x0000000000000000.") =20 parser.add_argument ("--fw-version", dest =3D 'FwVersion', type =3D Va= lidateUnsignedInteger, - help =3D "The 32-bit version of the binary payloa= d (e.g. 0x11223344 or 5678). Required for encode operations that sign a pa= yload.") + help =3D "The 32-bit version of the binary payloa= d (e.g. 0x11223344 or 5678). Required for encode operations.") parser.add_argument ("--lsv", dest =3D 'LowestSupportedVersion', type = =3D ValidateUnsignedInteger, - help =3D "The 32-bit lowest supported version of = the binary payload (e.g. 0x11223344 or 5678). Required for encode operatio= ns that sign a payload.") + help =3D "The 32-bit lowest supported version of = the binary payload (e.g. 0x11223344 or 5678). Required for encode operatio= ns.") =20 parser.add_argument ("--pfx-file", dest=3D'SignToolPfxFile', type=3Dar= gparse.FileType('rb'), help=3D"signtool PFX certificate filename.") @@ -276,6 +929,9 @@ if __name__ =3D=3D '__main__': parser.add_argument ("--signing-tool-path", dest =3D 'SigningToolPath'= , help =3D "Path to signtool or OpenSSL tool. Opti= onal if path to tools are already in PATH.") =20 + parser.add_argument ("--embedded-driver", dest =3D 'EmbeddedDriver', t= ype =3D argparse.FileType('rb'), action=3D'append', default =3D [], + help =3D "Path to embedded UEFI driver to add to = capsule.") + # # Add optional arguments common to all operations # @@ -286,79 +942,29 @@ if __name__ =3D=3D '__main__': help =3D "Disable all messages except fatal error= s.") parser.add_argument ("--debug", dest =3D 'Debug', type =3D int, metava= r =3D '[0-9]', choices =3D range (0, 10), default =3D 0, help =3D "Set debug level") + parser.add_argument ("--update-image-index", dest =3D 'UpdateImageInde= x', type =3D ValidateUnsignedInteger, default =3D 0x01, help =3D "unique nu= mber identifying the firmware image within the device ") =20 # # Parse command line arguments # args =3D parser.parse_args() =20 - # - # Perform additional argument verification - # - if args.Encode: - if args.Guid is None: - parser.error ('the following option is required: --guid') - if 'PersistAcrossReset' not in args.CapsuleFlag: - if 'InitiateReset' in args.CapsuleFlag: - parser.error ('--capflag InitiateReset also requires --cap= flag PersistAcrossReset') - if args.CapsuleOemFlag > 0xFFFF: - parser.error ('--capoemflag must be an integer between 0x0000 = and 0xffff') - if args.HardwareInstance > 0xFFFFFFFFFFFFFFFF: - parser.error ('--hardware-instance must be an integer in range= 0x0..0xffffffffffffffff') - if args.MonotonicCount > 0xFFFFFFFFFFFFFFFF: - parser.error ('--monotonic-count must be an integer in range 0= x0..0xffffffffffffffff') - - UseSignTool =3D args.SignToolPfxFile is not None - UseOpenSsl =3D (args.OpenSslSignerPrivateCertFile is not None and - args.OpenSslOtherPublicCertFile is not None and - args.OpenSslTrustedPublicCertFile is not None) - AnyOpenSsl =3D (args.OpenSslSignerPrivateCertFile is not None or - args.OpenSslOtherPublicCertFile is not None or - args.OpenSslTrustedPublicCertFile is not None) - if args.Encode or args.Decode: - if args.OutputFile is None: - parser.error ('the following option is required for all encode= and decode operations: --output') - - if UseSignTool and AnyOpenSsl: - parser.error ('Providing both signtool and OpenSSL options is = not supported') - if not UseSignTool and not UseOpenSsl and AnyOpenSsl: - parser.error ('all the following options are required for Open= SSL: --signer-private-cert, --other-public-cert, --trusted-public-cert') - if UseSignTool and platform.system() !=3D 'Windows': - parser.error ('Use of signtool is not supported on this operat= ing system.') - if args.Encode and (UseSignTool or UseOpenSsl): - if args.FwVersion is None or args.LowestSupportedVersion is No= ne: - parser.error ('the following options are required: --fw-ve= rsion, --lsv') - if args.FwVersion > 0xFFFFFFFF: - parser.error ('--fw-version must be an integer in range 0x= 0..0xffffffff') - if args.LowestSupportedVersion > 0xFFFFFFFF: - parser.error ('--lsv must be an integer in range 0x0..0xff= ffffff') - - if UseSignTool: - args.SignToolPfxFile.close() - args.SignToolPfxFile =3D args.SignToolPfxFile.name - if UseOpenSsl: - args.OpenSslSignerPrivateCertFile.close() - args.OpenSslOtherPublicCertFile.close() - args.OpenSslTrustedPublicCertFile.close() - args.OpenSslSignerPrivateCertFile =3D args.OpenSslSignerPrivat= eCertFile.name - args.OpenSslOtherPublicCertFile =3D args.OpenSslOtherPublicC= ertFile.name - args.OpenSslTrustedPublicCertFile =3D args.OpenSslTrustedPubli= cCertFile.name - - if args.DumpInfo: - if args.OutputFile is not None: - parser.error ('the following option is not supported for dumpi= nfo operations: --output') - # # Read binary input file # - try: - if args.Verbose: - print ('Read binary input file {File}'.format (File =3D args.I= nputFile.name)) - Buffer =3D args.InputFile.read () - args.InputFile.close () - except: - print ('GenerateCapsule: error: can not read binary input file {Fi= le}'.format (File =3D args.InputFile.name)) - sys.exit (1) + Buffer =3D '' + if args.InputFile: + if os.path.getsize (args.InputFile.name) =3D=3D 0: + print ('GenerateCapsule: error: InputFile {File} is empty'.for= mat (File =3D args.InputFile.name)) + sys.exit (1) + try: + if args.Verbose: + print ('Read binary input file {File}'.format (File =3D ar= gs.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) =20 # # Create objects @@ -368,182 +974,27 @@ if __name__ =3D=3D '__main__': FmpAuthHeader =3D FmpAuthHeaderClass () FmpPayloadHeader =3D FmpPayloadHeaderClass () =20 - if args.Encode: - Result =3D Buffer - if UseSignTool or UseOpenSsl: - try: - FmpPayloadHeader.FwVersion =3D args.FwVersion - FmpPayloadHeader.LowestSupportedVersion =3D args.LowestSup= portedVersion - FmpPayloadHeader.Payload =3D Result - Result =3D 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 ima= ge - # - try: - if UseSignTool: - CertData =3D SignPayloadSignTool ( - Result + struct.pack (' +# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # =20 @@ -166,6 +166,18 @@ class FmpAuthHeaderClass (object): self._Valid =3D True return self.Payload =20 + def IsSigned (self, Buffer): + if len (Buffer) < self._StructSize: + return False + (MonotonicCount, dwLength, wRevision, wCertificateType, CertType) = =3D \ + struct.unpack ( + self._StructFormat, + Buffer[0:self._StructSize] + ) + if CertType !=3D self._EFI_CERT_TYPE_PKCS7_GUID.bytes_le: + return False + return True + def DumpInfo (self): if not self._Valid: raise ValueError diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.p= y b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py index c24258d047..91d24919c4 100644 --- a/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py +++ b/BaseTools/Source/Python/Common/Uefi/Capsule/FmpCapsuleHeader.py @@ -2,7 +2,7 @@ # Module that encodes and decodes a EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER= with # a payload. # -# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # =20 @@ -172,8 +172,8 @@ class FmpCapsuleHeaderClass (object): raise ValueError return self._EmbeddedDriverList[Index] =20 - def AddPayload (self, UpdateImageTypeId, Payload =3D b'', VendorCodeBy= tes =3D b'', HardwareInstance =3D 0): - self._PayloadList.append ((UpdateImageTypeId, Payload, VendorCodeB= ytes, HardwareInstance)) + def AddPayload (self, UpdateImageTypeId, Payload =3D b'', VendorCodeBy= tes =3D b'', HardwareInstance =3D 0, UpdateImageIndex =3D 1): + self._PayloadList.append ((UpdateImageTypeId, Payload, VendorCodeB= ytes, HardwareInstance, UpdateImageIndex)) =20 def GetFmpCapsuleImageHeader (self, Index): if Index >=3D len (self._FmpCapsuleImageHeaderList): @@ -198,10 +198,10 @@ class FmpCapsuleHeaderClass (object): self._ItemOffsetList.append (Offset) Offset =3D Offset + len (EmbeddedDriver) Index =3D 1 - for (UpdateImageTypeId, Payload, VendorCodeBytes, HardwareInstance= ) in self._PayloadList: + for (UpdateImageTypeId, Payload, VendorCodeBytes, HardwareInstance= , UpdateImageIndex) in self._PayloadList: FmpCapsuleImageHeader =3D FmpCapsuleImageHeaderClass () FmpCapsuleImageHeader.UpdateImageTypeId =3D UpdateImageTy= peId - FmpCapsuleImageHeader.UpdateImageIndex =3D Index + FmpCapsuleImageHeader.UpdateImageIndex =3D UpdateImageIn= dex FmpCapsuleImageHeader.Payload =3D Payload FmpCapsuleImageHeader.VendorCodeBytes =3D VendorCodeByt= es FmpCapsuleImageHeader.UpdateHardwareInstance =3D HardwareInsta= nce @@ -288,6 +288,8 @@ class FmpCapsuleHeaderClass (object): raise ValueError print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.Version = =3D {Version:08X}'.format (Version =3D self.Version)) print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.EmbeddedDriverCount= =3D {EmbeddedDriverCount:08X}'.format (EmbeddedDriverCount =3D self.Embedd= edDriverCount)) + for EmbeddedDriver in self._EmbeddedDriverList: + print (' sizeof (EmbeddedDriver) = =3D {Size:08X}'.format (Size =3D len (EmbeddedDriver))) print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.PayloadItemCount = =3D {PayloadItemCount:08X}'.format (PayloadItemCount =3D self.PayloadItemC= ount)) print ('EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER.ItemOffsetList = =3D ') for Offset in self._ItemOffsetList: --=20 2.20.1.windows.1