[edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver

Eric Jin posted 1 patch 4 years, 10 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/edk2 tags/patchew/20190528071057.1196-1-eric.jin@intel.com
There is a newer version of this series
BaseTools/Source/Python/Capsule/CatGenerator.py                | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py       | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BaseTools/Source/Python/Capsule/InfGenerator.py                | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 582 insertions(+)
[edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver
Posted by Eric Jin 4 years, 10 months ago
https://bugzilla.tianocore.org/show_bug.cgi?id=1837

The tool is designed to generate Windows Firmware Update Drivers, the
input is one drivername.cap with related parameters, the output Windows
Driver package are composed by drivername.cap, drivername.inf and
drivername.cat to update the single payload in device.

usage: GenerateWindowsDriver [-h] [--output-folder OUTPUTFOLDER]
                             [--product-fmp-guid PRODUCTFMPGUID]
                             [--capsuleversion-dotstring CAPSULEVERSION_DOTSTRING]
                             [--capsuleversion-hexstring CAPSULEVERSION_HEXSTRING]
                             [--product-fw-provider PRODUCTFWPROVIDER]
                             [--product-fw-mfg-name PRODUCTFWMFGNAME]
                             [--product-fw-desc PRODUCTFWDESC]
                             [--capsule-file-name CAPSULEFILENAME]
                             [--pfx-file PFXFILE] [--arch ARCH]
                             [--operating-system-string OPERATINGSYSTEMSTRING]

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Kinney Michael D <michael.d.kinney@intel.com>
Signed-off-by: Eric Jin <eric.jin@intel.com>
---
 BaseTools/Source/Python/Capsule/CatGenerator.py                | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py       | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/InfGenerator.py                | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 582 insertions(+)

diff --git a/BaseTools/Source/Python/Capsule/CatGenerator.py b/BaseTools/Source/Python/Capsule/CatGenerator.py
new file mode 100644
index 0000000000..737387c296
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/CatGenerator.py
@@ -0,0 +1,155 @@
+## @file
+ # Script to generate Cat files for capsule update based on supplied inf file
+ #
+ # Copyright (c) 2019, Microsoft Corporation
+ # Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+ # SPDX-License-Identifier: BSD-2-Clause-Patent
+ #
+ ##
+
+import os
+import logging
+import datetime
+import subprocess
+import threading
+
+class PropagatingThread(threading.Thread):
+    def run(self):
+        self.exc = None
+        try:
+            if hasattr(self, '_Thread__target'):
+                # Thread uses name mangling prior to Python 3.
+                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
+            else:
+                self.ret = self._target(*self._args, **self._kwargs)
+        except BaseException as e:
+            self.exc = e
+    def join(self, timeout=None):
+        super(PropagatingThread, self).join()
+        if self.exc:
+             raise self.exc
+        return self.ret
+def reader(filepath, outstream, stream):
+    f = None
+    # open file if caller provided path
+    if(filepath):
+        f = open(filepath, "w")
+    while True:
+        s = stream.readline().decode()
+        if not s:
+            stream.close()
+            break
+        if(f is not None):
+            # write to file if caller provided file
+            f.write(s)
+        if(outstream is not None):
+            # write to stream object if caller provided object
+            outstream.write(s)
+        logging.info(s.rstrip())
+    if(f is not None):
+        f.close()
+def RunCmd(cmd, parameters, capture=True, workingdir=None, outfile=None, outstream=None, environ=None):
+    cmd = cmd.strip('"\'')
+    if " " in cmd:
+        cmd = '"' + cmd + '"'
+    if parameters is not None:
+        parameters = parameters.strip()
+        cmd += " " + parameters
+    starttime = datetime.datetime.now()
+    logging.info("Cmd to run is: " + cmd) 
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Starting---------------")
+    logging.info("------------------------------------------------")
+    c = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=workingdir, shell=True, env=environ)
+    if(capture):
+        outr = PropagatingThread(target=reader, args=(outfile, outstream, c.stdout,))
+        outr.start()
+        outr.join()
+        c.wait()
+    else:
+        c.wait()
+  
+    endtime = datetime.datetime.now()
+    delta = endtime - starttime
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Finished---------------")
+    logging.info("--------- Running Time (mm:ss): {0[0]:02}:{0[1]:02} ----------".format(divmod(delta.seconds, 60)))
+    logging.info("------------------------------------------------")
+    return c.returncode
+
+class CatGenerator(object):
+    SUPPORTED_OS = {'win10': '10',
+                    '10': '10',
+                    '10_au': '10_AU',
+                    '10_rs2': '10_RS2',
+                    '10_rs3': '10_RS3',
+                    '10_rs4': '10_RS4',
+                    'server10': 'Server10',
+                    'server2016': 'Server2016',
+                    'serverrs2': 'ServerRS2',
+                    'serverrs3': 'ServerRS3',
+                    'serverrs4': 'ServerRS4'
+                    }
+
+    def __init__(self, arch, os):
+        self.Arch = arch
+        self.OperatingSystem = os
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        value = value.lower()
+        if(value == "x64") or (value == "amd64"):  # support amd64 value so INF and CAT tools can use same arch value
+            self._arch = "X64"
+        elif(value == "arm"):
+            self._arch = "ARM"
+        elif(value == "arm64") or (value == "aarch64"):  # support UEFI defined aarch64 value as well
+            self._arch = "ARM64"
+        else:
+            logging.critical("Unsupported Architecture: %s", value)
+            raise ValueError("Unsupported Architecture")
+
+    @property
+    def OperatingSystem(self):
+        return self._operatingsystem
+
+    @OperatingSystem.setter
+    def OperatingSystem(self, value):
+        key = value.lower()
+        if(key not in CatGenerator.SUPPORTED_OS.keys()):
+            logging.critical("Unsupported Operating System: %s", key)
+            raise ValueError("Unsupported Operating System")
+        self._operatingsystem = CatGenerator.SUPPORTED_OS[key]
+
+    def MakeCat(self, OutputCatFile, PathToInf2CatTool=None):
+        # Find Inf2Cat tool
+        if(PathToInf2CatTool is None):
+            PathToInf2CatTool = os.path.join(os.getenv("ProgramFiles(x86)"), "Windows Kits", "10",
+                                             "bin", "x86", "Inf2Cat.exe")
+            if not os.path.exists(PathToInf2CatTool):
+                logging.debug("Windows Kit 10 not Found....trying 8.1")
+                # Try 8.1 kit
+                PathToInf2CatTool.replace("10", "8.1")
+
+        # check if exists
+        if not os.path.exists(PathToInf2CatTool):
+            raise Exception("Can't find Inf2Cat on this machine.  Please install the Windows 10 WDK - "
+                            "https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit")
+
+        # Adjust for spaces in the path (when calling the command).
+        if " " in PathToInf2CatTool:
+            PathToInf2CatTool = '"' + PathToInf2CatTool + '"'
+
+        OutputFolder = os.path.dirname(OutputCatFile)
+        # Make Cat file
+        cmd = "/driver:. /os:" + self.OperatingSystem + "_" + self.Arch + " /verbose"
+        ret = RunCmd(PathToInf2CatTool, cmd, workingdir=OutputFolder)
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(not os.path.isfile(OutputCatFile)):
+            raise Exception("CAT file (%s) not created" % OutputCatFile)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
new file mode 100644
index 0000000000..1d543b3fca
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
@@ -0,0 +1,115 @@
+## @file
+# Generate a capsule windows driver.
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+'''
+GenerateWindowsDriver
+'''
+
+import sys
+import argparse
+import uuid
+import struct
+import subprocess
+import os
+import tempfile
+import shutil
+import platform
+import re
+import logging
+from WindowsCapsuleSupportHelper import WindowsCapsuleSupportHelper
+
+#
+# Globals for help information
+#
+__prog__        = 'GenerateWindowsDriver'
+__version__     = '0.0'
+__copyright__   = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
+__description__ = 'Generate Capsule Windows Driver.\n'
+
+
+if __name__ == '__main__':
+    def convert_arg_line_to_args(arg_line):
+        for arg in arg_line.split():
+            if not arg.strip():
+                continue
+            yield arg
+
+    parser = argparse.ArgumentParser (
+                        prog = __prog__,
+                        description = __description__ + __copyright__,
+                        conflict_handler = 'resolve',
+                        fromfile_prefix_chars = '@'
+                        )
+    parser.convert_arg_line_to_args = convert_arg_line_to_args
+    parser.add_argument("--output-folder", dest = 'OutputFolder', help = "firmware resource update driver package output folder.")
+    parser.add_argument("--product-fmp-guid", dest = 'ProductFmpGuid', help = "firmware GUID of resource update driver package")
+    parser.add_argument("--capsuleversion-dotstring", dest = 'CapsuleVersion_DotString', help = "firmware version with date on which update driver package is authored")
+    parser.add_argument("--capsuleversion-hexstring", dest = 'CapsuleVersion_HexString', help = "firmware version in Hex of update driver package")
+    parser.add_argument("--product-fw-provider", dest = 'ProductFwProvider', help = "vendor/provider of entire firmware resource update driver package")
+    parser.add_argument("--product-fw-mfg-name", dest = 'ProductFwMfgName', help = "manufacturer/vendor of firmware resource update driver package")
+    parser.add_argument("--product-fw-desc", dest = "ProductFwDesc", help = "description about resource update driver")
+    parser.add_argument("--capsule-file-name", dest = 'CapsuleFileName', help ="firmware resource image file")
+    parser.add_argument("--pfx-file", dest = 'PfxFile', help = "pfx file path used to sign resource update driver")
+    parser.add_argument("--arch", dest = 'Arch', help = "supported architecture:arm/x64/amd64/arm64/aarch64", default = 'amd64')
+    parser.add_argument("--operating-system-string", dest = 'OperatingSystemString', help = "supported operating system:win10/10/10_au/10_rs2/10_rs3/10_rs4/server10/server2016/serverrs2/serverrs3/serverrs4", default = "win10")
+
+    def ArgCheck(args):
+        Version = args.CapsuleVersion_DotString.split('.')
+
+        if len(Version) != 4:
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name invalid.")
+        for sub in Version:
+            if  int(sub) > 65536:
+                logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+                raise ValueError("Name exceed limit 65536.")
+
+        if not (re.compile(r'[0-9.]*$')).match(args.CapsuleVersion_DotString):
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name has invalid chars.")
+
+    def CapsuleGuidCheck(InputFile, Guid):
+        TempCapDecode = 'TempCapDecode.txt'
+        Command = 'python GenerateCapsule.py "' + InputFile + '" --dump-info >' + TempCapDecode
+        os.system (Command)
+        with open(TempCapDecode, 'rb') as f:
+            for line in f:
+                if re.search(b'UpdateImageTypeId', line, re.M|re.I):
+                    CapGuid = str(line).split('= ')[1][:-5]
+                    break
+            f.close()
+            os.remove(TempCapDecode)
+            if (Guid != CapGuid):
+                print('GenerateWindowsDriver error: Different Guid from Capsule')
+                sys.exit(1)
+
+    args = parser.parse_args()
+
+    InputFile = os.path.join(args.OutputFolder, '') + args.CapsuleFileName
+
+    ProductName = args.CapsuleFileName.strip('.cap')
+    WindowsDriver = WindowsCapsuleSupportHelper ()
+
+    ArgCheck(args)
+    CapsuleGuidCheck(InputFile, args.ProductFmpGuid)
+
+    WindowsDriver.PackageWindowsCapsuleFiles (
+                                                   args.OutputFolder,
+                                                   ProductName,
+                                                   args.ProductFmpGuid,
+                                                   args.CapsuleVersion_DotString,
+                                                   args.CapsuleVersion_HexString,
+                                                   args.ProductFwProvider,
+                                                   args.ProductFwMfgName,
+                                                   args.ProductFwDesc,
+                                                   args.CapsuleFileName,
+                                                   args.PfxFile,
+                                                   None,
+                                                   None,
+                                                   args.Arch,
+                                                   args.OperatingSystemString
+                                                   )
diff --git a/BaseTools/Source/Python/Capsule/InfGenerator.py b/BaseTools/Source/Python/Capsule/InfGenerator.py
new file mode 100644
index 0000000000..508bbed0ef
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/InfGenerator.py
@@ -0,0 +1,210 @@
+## @file
+ # Script to generate inf files for capsule update based on INF TEMPLATE and
+ # supplied information (Name, Version, ESRT Guid, Rollback, etc.)
+ #
+ # Copyright (c) 2019, Microsoft Corporation
+ # Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+ # SPDX-License-Identifier: BSD-2-Clause-Patent
+ ##
+
+import os
+import logging
+import datetime
+import re
+import uuid
+
+
+#####
+#
+#####
+class InfGenerator(object):
+
+    ### INF Template ###
+    TEMPLATE = r""";
+; {Name}.inf
+; {DriverVersion}
+; Copyright (C) 2019 Microsoft Corporation.  All Rights Reserved.
+;
+[Version]
+Signature="$WINDOWS NT$"
+Class=Firmware
+ClassGuid={{f2e7dd72-6468-4e36-b6f1-6488f42c1b52}}
+Provider=%Provider%
+DriverVer={Date},{DriverVersion}
+PnpLockdown=1
+CatalogFile={Name}.cat
+[Manufacturer]
+%MfgName% = Firmware,NT{Arch}
+[Firmware.NT{Arch}]
+%FirmwareDesc% = Firmware_Install,UEFI\RES_{{{EsrtGuid}}}
+[Firmware_Install.NT]
+CopyFiles = Firmware_CopyFiles
+{Rollback}
+[Firmware_CopyFiles]
+{FirmwareBinFile}
+[Firmware_Install.NT.Hw]
+AddReg = Firmware_AddReg
+[Firmware_AddReg]
+HKR,,FirmwareId,,{{{EsrtGuid}}}
+HKR,,FirmwareVersion,%REG_DWORD%,{VersionHexString}
+HKR,,FirmwareFilename,,{FirmwareBinFile}
+[SourceDisksNames]
+1 = %DiskName%
+[SourceDisksFiles]
+{FirmwareBinFile} = 1
+[DestinationDirs]
+DefaultDestDir = %DIRID_WINDOWS%,Firmware ; %SystemRoot%\Firmware
+[Strings]
+; localizable
+Provider     = "{Provider}"
+MfgName      = "{MfgName}"
+FirmwareDesc = "{Description}"
+DiskName     = "Firmware Update"
+; non-localizable
+DIRID_WINDOWS = 10
+REG_DWORD     = 0x00010001
+"""
+
+    ROLLBACKTEMPLATE = r"""AddReg    = Firmware_DowngradePolicy_Addreg
+  ;override firmware resource update policy to allow downgrade to lower version
+  [Firmware_DowngradePolicy_Addreg]
+  HKLM,SYSTEM\CurrentControlSet\Control\FirmwareResources\{{{EsrtGuid}}},Policy,%REG_DWORD%,1
+  """
+
+    SUPPORTED_ARCH = {'amd64': 'amd64',
+                      'x64': 'amd64',
+                      'arm': 'arm',
+                      'arm64': 'ARM64',
+                      'aarch64': 'ARM64'
+                      }
+
+    def __init__(self, name_string, provider, esrt_guid, arch, description_string, version_string, version_hex):
+        self.Name = name_string
+        self.Provider = provider
+        self.EsrtGuid = esrt_guid
+        self.Arch = arch
+        self.Description = description_string
+        self.VersionString = version_string
+        self.VersionHex = version_hex
+        self._manufacturer = None  # default for optional feature
+        self._date = datetime.date.today()
+
+    @property
+    def Name(self):
+        return self._name
+
+    @Name.setter
+    def Name(self, value):
+        # test here for invalid chars
+        if not (re.compile(r'[\w-]*$')).match(value):
+            logging.critical("Name invalid: '%s'", value)
+            raise ValueError("Name has invalid chars.")
+        self._name = value
+
+    @property
+    def Provider(self):
+        return self._provider
+
+    @Provider.setter
+    def Provider(self, value):
+        self._provider = value
+
+    @property
+    def Manufacturer(self):
+        if(self._manufacturer is None):
+            return self.Provider
+
+        return self._manufacturer
+
+    @Manufacturer.setter
+    def Manufacturer(self, value):
+        self._manufacturer = value
+
+    @property
+    def Description(self):
+        return self._description
+
+    @Description.setter
+    def Description(self, value):
+        self._description = value
+
+    @property
+    def EsrtGuid(self):
+        return self._esrtguid
+
+    @EsrtGuid.setter
+    def EsrtGuid(self, value):
+        uuid.UUID(value)  # if this works it is valid...otherwise throws exception
+        # todo - make sure it is formatted exactly right
+        self._esrtguid = value
+
+    @property
+    def VersionString(self):
+        return self._versionstring
+
+    @VersionString.setter
+    def VersionString(self, value):
+        c = value.count(".")
+        if(c < 1) or (c > 3):
+            logging.critical("Version string in invalid format.")
+            raise ValueError("VersionString must be in format of xx.xx -> xx.xx.xx.xx")
+        self._versionstring = value
+
+    @property
+    def VersionHex(self):
+        return "0x%08X" % self._versionhex
+
+    @VersionHex.setter
+    def VersionHex(self, value):
+        a = int(value, 0)
+        if(a > 0xFFFFFFFF):
+            logging.critical("VersionHex invalid: '%s'", value)
+            raise ValueError("VersionHex must fit within 32bit value range for unsigned integer")
+        self._versionhex = a
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        key = value.lower()
+        if(key not in InfGenerator.SUPPORTED_ARCH.keys()):
+            logging.critical("Arch invalid: '%s'", value)
+            raise ValueError("Unsupported Architecture")
+        self._arch = InfGenerator.SUPPORTED_ARCH[key]
+
+    @property
+    def Date(self):
+        return self._date.strftime("%m/%d/%Y")
+
+    @Date.setter
+    def Date(self, value):
+        if(not isinstance(value, datetime.date)):
+            raise ValueError("Date must be a datetime.date object")
+        self._date = value
+
+    def MakeInf(self, OutputInfFilePath, FirmwareBinFileName, Rollback=False):
+        RollbackString = ""
+        if(Rollback):
+            RollbackString = InfGenerator.ROLLBACKTEMPLATE.format(EsrtGuid=self.EsrtGuid)
+
+        binfilename = os.path.basename(FirmwareBinFileName)
+
+        Content = InfGenerator.TEMPLATE.format(
+            Name=self.Name,
+            Date=self.Date,
+            Arch=self.Arch,
+            DriverVersion=self.VersionString,
+            EsrtGuid=self.EsrtGuid,
+            FirmwareBinFile=binfilename,
+            VersionHexString=self.VersionHex,
+            Provider=self.Provider,
+            MfgName=self.Manufacturer,
+            Description=self.Description,
+            Rollback=RollbackString)
+
+        with open(OutputInfFilePath, "w") as f:
+            f.write(Content)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
new file mode 100644
index 0000000000..69992e2c08
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
@@ -0,0 +1,102 @@
+##
+# UefiBuild Plugin that supports Window Capsule files based on the
+# Windows Firmware Update Platform spec.
+# Creates INF, Cat, and then signs it
+#
+#
+# Copyright (c) 2018, Microsoft Corporation
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+import sys
+import re
+import datetime
+import os
+import logging
+from CatGenerator import CatGenerator
+from InfGenerator import InfGenerator
+from CatGenerator import RunCmd
+
+def CatalogSignWithSignTool(SignToolPath, ToSignFilePath, PfxFilePath, PfxPass=None):
+    # check signtool path
+    if not os.path.exists(SignToolPath):
+        logging.error("Path to signtool invalid.  %s" % SignToolPath)
+        return -1
+
+    # Adjust for spaces in the path (when calling the command).
+    if " " in SignToolPath:
+        SignToolPath = '"' + SignToolPath + '"'
+
+    OutputDir = os.path.dirname(ToSignFilePath)
+    # Signtool docs https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe
+    # todo: link to catalog signing documentation
+    params = "sign /a /fd SHA256 /f " + PfxFilePath
+    if PfxPass is not None:
+        # add password if set
+        params = params + ' /p ' + PfxPass
+    params = params + ' /debug /v "' + ToSignFilePath + '" '
+    ret = RunCmd(SignToolPath, params, workingdir=OutputDir)
+    if(ret != 0):
+        logging.error("Signtool failed %d" % ret)
+    return ret
+
+class WindowsCapsuleSupportHelper(object):
+    @staticmethod
+    def _LocateLatestWindowsKits():
+        result = None
+        
+        # Start with a base path and use it to start locating the ideal directory.
+        base_path = os.path.join(os.getenv("ProgramFiles(x86)"), "Windows Kits")
+        
+        # Check for Win 10 kits first.
+        base_10_path = os.path.join(base_path, "10", "bin")
+        if os.path.isdir(base_10_path):
+            # If you can find one of the new kit paths, use it.
+            # Walk backwards to test the most recent kit first.
+            for sub_path in reversed(os.listdir(base_10_path)):
+                if sub_path.startswith("10.") and os.path.isdir(os.path.join(base_10_path, sub_path, "x64")):
+                    result = os.path.join(base_10_path, sub_path, "x64")
+                    break
+
+            # Otherwise, fall back to the legacy path.
+            if not result and os.path.isdir(os.path.join(base_10_path, "x64")):
+                result = os.path.join(base_10_path, "x64")
+        # If not, fall back to Win 8.1.
+        elif os.path.isdir(os.path.join(base_path, "8.1", "bin", "x64")):
+            result = os.path.join(base_path, "8.1", "bin", "x64")
+        return result
+
+    def RegisterHelpers(self, obj):
+        fp = os.path.abspath(__file__)
+        obj.Register("PackageWindowsCapsuleFiles", WindowsCapsuleSupportHelper.PackageWindowsCapsuleFiles, fp)
+
+
+    @staticmethod
+    def PackageWindowsCapsuleFiles(OutputFolder, ProductName, ProductFmpGuid, CapsuleVersion_DotString,CapsuleVersion_HexString, ProductFwProvider, ProductFwMfgName, ProductFwDesc, CapsuleFileName, PfxFile=None, PfxPass=None, Rollback=False, Arch='amd64', OperatingSystem_String='Win10'):
+        logging.debug("CapsulePackage: Create Windows Capsule Files")
+        #Make INF
+        InfFilePath = os.path.join(OutputFolder, ProductName + ".inf")
+        InfTool = InfGenerator(ProductName, ProductFwProvider, ProductFmpGuid, Arch, ProductFwDesc, CapsuleVersion_DotString, CapsuleVersion_HexString)
+        InfTool.Manufacturer = ProductFwMfgName #optional
+        ret = InfTool.MakeInf(InfFilePath, CapsuleFileName, Rollback)
+        if(ret != 0):
+            raise Exception("CreateWindowsInf Failed with errorcode %d" % ret)
+        #Make CAT
+        CatFilePath = os.path.realpath(os.path.join(OutputFolder, ProductName + ".cat"))
+        CatTool = CatGenerator(Arch, OperatingSystem_String)
+        ret = CatTool.MakeCat(CatFilePath)
+        
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(PfxFile is not None):
+            #Find Signtool
+            WinKitsPath = WindowsCapsuleSupportHelper._LocateLatestWindowsKits()
+            SignToolPath = os.path.join(WinKitsPath, "signtool.exe")
+            if not os.path.exists(SignToolPath):
+                raise Exception("Can't find signtool on this machine.")
+            #dev sign the cat file
+            ret = CatalogSignWithSignTool(SignToolPath, CatFilePath, PfxFile, PfxPass)
+            if(ret != 0):
+                raise Exception("Signing Cat file Failed with errorcode %d" % ret)
+        return ret
-- 
2.20.1.windows.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#41454): https://edk2.groups.io/g/devel/message/41454
Mute This Topic: https://groups.io/mt/31819358/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-

Re: [edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver
Posted by Bob Feng 4 years, 10 months ago
Hi Eric,

Please use "with" statement for the open() calls.

For example,

Replace:
    Fd = open("file","w")
    Do something
    Fd.close

To:
    With open("file","w") as fd:
         Do something

Thanks,
Bob

-----Original Message-----
From: devel@edk2.groups.io [mailto:devel@edk2.groups.io] On Behalf Of Eric Jin
Sent: Tuesday, May 28, 2019 3:11 PM
To: devel@edk2.groups.io
Cc: Feng, Bob C <bob.c.feng@intel.com>; Gao, Liming <liming.gao@intel.com>; Kinney, Michael D <michael.d.kinney@intel.com>
Subject: [edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver

https://bugzilla.tianocore.org/show_bug.cgi?id=1837

The tool is designed to generate Windows Firmware Update Drivers, the input is one drivername.cap with related parameters, the output Windows Driver package are composed by drivername.cap, drivername.inf and drivername.cat to update the single payload in device.

usage: GenerateWindowsDriver [-h] [--output-folder OUTPUTFOLDER]
                             [--product-fmp-guid PRODUCTFMPGUID]
                             [--capsuleversion-dotstring CAPSULEVERSION_DOTSTRING]
                             [--capsuleversion-hexstring CAPSULEVERSION_HEXSTRING]
                             [--product-fw-provider PRODUCTFWPROVIDER]
                             [--product-fw-mfg-name PRODUCTFWMFGNAME]
                             [--product-fw-desc PRODUCTFWDESC]
                             [--capsule-file-name CAPSULEFILENAME]
                             [--pfx-file PFXFILE] [--arch ARCH]
                             [--operating-system-string OPERATINGSYSTEMSTRING]

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Kinney Michael D <michael.d.kinney@intel.com>
Signed-off-by: Eric Jin <eric.jin@intel.com>
---
 BaseTools/Source/Python/Capsule/CatGenerator.py                | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py       | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/InfGenerator.py                | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 582 insertions(+)

diff --git a/BaseTools/Source/Python/Capsule/CatGenerator.py b/BaseTools/Source/Python/Capsule/CatGenerator.py
new file mode 100644
index 0000000000..737387c296
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/CatGenerator.py
@@ -0,0 +1,155 @@
+## @file
+ # Script to generate Cat files for capsule update based on supplied 
+inf file  #  # Copyright (c) 2019, Microsoft Corporation  # Copyright 
+(c) 2019, Intel Corporation. All rights reserved.<BR>  # 
+SPDX-License-Identifier: BSD-2-Clause-Patent  #  ##
+
+import os
+import logging
+import datetime
+import subprocess
+import threading
+
+class PropagatingThread(threading.Thread):
+    def run(self):
+        self.exc = None
+        try:
+            if hasattr(self, '_Thread__target'):
+                # Thread uses name mangling prior to Python 3.
+                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
+            else:
+                self.ret = self._target(*self._args, **self._kwargs)
+        except BaseException as e:
+            self.exc = e
+    def join(self, timeout=None):
+        super(PropagatingThread, self).join()
+        if self.exc:
+             raise self.exc
+        return self.ret
+def reader(filepath, outstream, stream):
+    f = None
+    # open file if caller provided path
+    if(filepath):
+        f = open(filepath, "w")
+    while True:
+        s = stream.readline().decode()
+        if not s:
+            stream.close()
+            break
+        if(f is not None):
+            # write to file if caller provided file
+            f.write(s)
+        if(outstream is not None):
+            # write to stream object if caller provided object
+            outstream.write(s)
+        logging.info(s.rstrip())
+    if(f is not None):
+        f.close()
+def RunCmd(cmd, parameters, capture=True, workingdir=None, outfile=None, outstream=None, environ=None):
+    cmd = cmd.strip('"\'')
+    if " " in cmd:
+        cmd = '"' + cmd + '"'
+    if parameters is not None:
+        parameters = parameters.strip()
+        cmd += " " + parameters
+    starttime = datetime.datetime.now()
+    logging.info("Cmd to run is: " + cmd) 
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Starting---------------")
+    logging.info("------------------------------------------------")
+    c = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=workingdir, shell=True, env=environ)
+    if(capture):
+        outr = PropagatingThread(target=reader, args=(outfile, outstream, c.stdout,))
+        outr.start()
+        outr.join()
+        c.wait()
+    else:
+        c.wait()
+
+    endtime = datetime.datetime.now()
+    delta = endtime - starttime
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Finished---------------")
+    logging.info("--------- Running Time (mm:ss): {0[0]:02}:{0[1]:02} ----------".format(divmod(delta.seconds, 60)))
+    logging.info("------------------------------------------------")
+    return c.returncode
+
+class CatGenerator(object):
+    SUPPORTED_OS = {'win10': '10',
+                    '10': '10',
+                    '10_au': '10_AU',
+                    '10_rs2': '10_RS2',
+                    '10_rs3': '10_RS3',
+                    '10_rs4': '10_RS4',
+                    'server10': 'Server10',
+                    'server2016': 'Server2016',
+                    'serverrs2': 'ServerRS2',
+                    'serverrs3': 'ServerRS3',
+                    'serverrs4': 'ServerRS4'
+                    }
+
+    def __init__(self, arch, os):
+        self.Arch = arch
+        self.OperatingSystem = os
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        value = value.lower()
+        if(value == "x64") or (value == "amd64"):  # support amd64 value so INF and CAT tools can use same arch value
+            self._arch = "X64"
+        elif(value == "arm"):
+            self._arch = "ARM"
+        elif(value == "arm64") or (value == "aarch64"):  # support UEFI defined aarch64 value as well
+            self._arch = "ARM64"
+        else:
+            logging.critical("Unsupported Architecture: %s", value)
+            raise ValueError("Unsupported Architecture")
+
+    @property
+    def OperatingSystem(self):
+        return self._operatingsystem
+
+    @OperatingSystem.setter
+    def OperatingSystem(self, value):
+        key = value.lower()
+        if(key not in CatGenerator.SUPPORTED_OS.keys()):
+            logging.critical("Unsupported Operating System: %s", key)
+            raise ValueError("Unsupported Operating System")
+        self._operatingsystem = CatGenerator.SUPPORTED_OS[key]
+
+    def MakeCat(self, OutputCatFile, PathToInf2CatTool=None):
+        # Find Inf2Cat tool
+        if(PathToInf2CatTool is None):
+            PathToInf2CatTool = os.path.join(os.getenv("ProgramFiles(x86)"), "Windows Kits", "10",
+                                             "bin", "x86", "Inf2Cat.exe")
+            if not os.path.exists(PathToInf2CatTool):
+                logging.debug("Windows Kit 10 not Found....trying 8.1")
+                # Try 8.1 kit
+                PathToInf2CatTool.replace("10", "8.1")
+
+        # check if exists
+        if not os.path.exists(PathToInf2CatTool):
+            raise Exception("Can't find Inf2Cat on this machine.  Please install the Windows 10 WDK - "
+
+ "https://developer.microsoft.com/en-us/windows/hardware/windows-driver
+ -kit")
+
+        # Adjust for spaces in the path (when calling the command).
+        if " " in PathToInf2CatTool:
+            PathToInf2CatTool = '"' + PathToInf2CatTool + '"'
+
+        OutputFolder = os.path.dirname(OutputCatFile)
+        # Make Cat file
+        cmd = "/driver:. /os:" + self.OperatingSystem + "_" + self.Arch + " /verbose"
+        ret = RunCmd(PathToInf2CatTool, cmd, workingdir=OutputFolder)
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(not os.path.isfile(OutputCatFile)):
+            raise Exception("CAT file (%s) not created" % 
+ OutputCatFile)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
new file mode 100644
index 0000000000..1d543b3fca
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
@@ -0,0 +1,115 @@
+## @file
+# Generate a capsule windows driver.
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> # 
+SPDX-License-Identifier: BSD-2-Clause-Patent #
+
+'''
+GenerateWindowsDriver
+'''
+
+import sys
+import argparse
+import uuid
+import struct
+import subprocess
+import os
+import tempfile
+import shutil
+import platform
+import re
+import logging
+from WindowsCapsuleSupportHelper import WindowsCapsuleSupportHelper
+
+#
+# Globals for help information
+#
+__prog__        = 'GenerateWindowsDriver'
+__version__     = '0.0'
+__copyright__   = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
+__description__ = 'Generate Capsule Windows Driver.\n'
+
+
+if __name__ == '__main__':
+    def convert_arg_line_to_args(arg_line):
+        for arg in arg_line.split():
+            if not arg.strip():
+                continue
+            yield arg
+
+    parser = argparse.ArgumentParser (
+                        prog = __prog__,
+                        description = __description__ + __copyright__,
+                        conflict_handler = 'resolve',
+                        fromfile_prefix_chars = '@'
+                        )
+    parser.convert_arg_line_to_args = convert_arg_line_to_args
+    parser.add_argument("--output-folder", dest = 'OutputFolder', help = "firmware resource update driver package output folder.")
+    parser.add_argument("--product-fmp-guid", dest = 'ProductFmpGuid', help = "firmware GUID of resource update driver package")
+    parser.add_argument("--capsuleversion-dotstring", dest = 'CapsuleVersion_DotString', help = "firmware version with date on which update driver package is authored")
+    parser.add_argument("--capsuleversion-hexstring", dest = 'CapsuleVersion_HexString', help = "firmware version in Hex of update driver package")
+    parser.add_argument("--product-fw-provider", dest = 'ProductFwProvider', help = "vendor/provider of entire firmware resource update driver package")
+    parser.add_argument("--product-fw-mfg-name", dest = 'ProductFwMfgName', help = "manufacturer/vendor of firmware resource update driver package")
+    parser.add_argument("--product-fw-desc", dest = "ProductFwDesc", help = "description about resource update driver")
+    parser.add_argument("--capsule-file-name", dest = 'CapsuleFileName', help ="firmware resource image file")
+    parser.add_argument("--pfx-file", dest = 'PfxFile', help = "pfx file path used to sign resource update driver")
+    parser.add_argument("--arch", dest = 'Arch', help = "supported architecture:arm/x64/amd64/arm64/aarch64", default = 'amd64')
+    parser.add_argument("--operating-system-string", dest = 
+ 'OperatingSystemString', help = "supported operating 
+ system:win10/10/10_au/10_rs2/10_rs3/10_rs4/server10/server2016/serverr
+ s2/serverrs3/serverrs4", default = "win10")
+
+    def ArgCheck(args):
+        Version = args.CapsuleVersion_DotString.split('.')
+
+        if len(Version) != 4:
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name invalid.")
+        for sub in Version:
+            if  int(sub) > 65536:
+                logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+                raise ValueError("Name exceed limit 65536.")
+
+        if not (re.compile(r'[0-9.]*$')).match(args.CapsuleVersion_DotString):
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name has invalid chars.")
+
+    def CapsuleGuidCheck(InputFile, Guid):
+        TempCapDecode = 'TempCapDecode.txt'
+        Command = 'python GenerateCapsule.py "' + InputFile + '" --dump-info >' + TempCapDecode
+        os.system (Command)
+        with open(TempCapDecode, 'rb') as f:
+            for line in f:
+                if re.search(b'UpdateImageTypeId', line, re.M|re.I):
+                    CapGuid = str(line).split('= ')[1][:-5]
+                    break
+            f.close()
+            os.remove(TempCapDecode)
+            if (Guid != CapGuid):
+                print('GenerateWindowsDriver error: Different Guid from Capsule')
+                sys.exit(1)
+
+    args = parser.parse_args()
+
+    InputFile = os.path.join(args.OutputFolder, '') + 
+ args.CapsuleFileName
+
+    ProductName = args.CapsuleFileName.strip('.cap')
+    WindowsDriver = WindowsCapsuleSupportHelper ()
+
+    ArgCheck(args)
+    CapsuleGuidCheck(InputFile, args.ProductFmpGuid)
+
+    WindowsDriver.PackageWindowsCapsuleFiles (
+                                                   args.OutputFolder,
+                                                   ProductName,
+                                                   args.ProductFmpGuid,
+                                                   args.CapsuleVersion_DotString,
+                                                   args.CapsuleVersion_HexString,
+                                                   args.ProductFwProvider,
+                                                   args.ProductFwMfgName,
+                                                   args.ProductFwDesc,
+                                                   args.CapsuleFileName,
+                                                   args.PfxFile,
+                                                   None,
+                                                   None,
+                                                   args.Arch,
+                                                   args.OperatingSystemString
+                                                   )
diff --git a/BaseTools/Source/Python/Capsule/InfGenerator.py b/BaseTools/Source/Python/Capsule/InfGenerator.py
new file mode 100644
index 0000000000..508bbed0ef
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/InfGenerator.py
@@ -0,0 +1,210 @@
+## @file
+ # Script to generate inf files for capsule update based on INF 
+TEMPLATE and  # supplied information (Name, Version, ESRT Guid, 
+Rollback, etc.)  #  # Copyright (c) 2019, Microsoft Corporation  # 
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>  # 
+SPDX-License-Identifier: BSD-2-Clause-Patent  ##
+
+import os
+import logging
+import datetime
+import re
+import uuid
+
+
+#####
+#
+#####
+class InfGenerator(object):
+
+    ### INF Template ###
+    TEMPLATE = r""";
+; {Name}.inf
+; {DriverVersion}
+; Copyright (C) 2019 Microsoft Corporation.  All Rights Reserved.
+;
+[Version]
+Signature="$WINDOWS NT$"
+Class=Firmware
+ClassGuid={{f2e7dd72-6468-4e36-b6f1-6488f42c1b52}}
+Provider=%Provider%
+DriverVer={Date},{DriverVersion}
+PnpLockdown=1
+CatalogFile={Name}.cat
+[Manufacturer]
+%MfgName% = Firmware,NT{Arch}
+[Firmware.NT{Arch}]
+%FirmwareDesc% = Firmware_Install,UEFI\RES_{{{EsrtGuid}}}
+[Firmware_Install.NT]
+CopyFiles = Firmware_CopyFiles
+{Rollback}
+[Firmware_CopyFiles]
+{FirmwareBinFile}
+[Firmware_Install.NT.Hw]
+AddReg = Firmware_AddReg
+[Firmware_AddReg]
+HKR,,FirmwareId,,{{{EsrtGuid}}}
+HKR,,FirmwareVersion,%REG_DWORD%,{VersionHexString}
+HKR,,FirmwareFilename,,{FirmwareBinFile}
+[SourceDisksNames]
+1 = %DiskName%
+[SourceDisksFiles]
+{FirmwareBinFile} = 1
+[DestinationDirs]
+DefaultDestDir = %DIRID_WINDOWS%,Firmware ; %SystemRoot%\Firmware 
+[Strings] ; localizable
+Provider     = "{Provider}"
+MfgName      = "{MfgName}"
+FirmwareDesc = "{Description}"
+DiskName     = "Firmware Update"
+; non-localizable
+DIRID_WINDOWS = 10
+REG_DWORD     = 0x00010001
+"""
+
+    ROLLBACKTEMPLATE = r"""AddReg    = Firmware_DowngradePolicy_Addreg
+  ;override firmware resource update policy to allow downgrade to lower 
+ version  [Firmware_DowngradePolicy_Addreg]
+
+ HKLM,SYSTEM\CurrentControlSet\Control\FirmwareResources\{{{EsrtGuid}}}
+ ,Policy,%REG_DWORD%,1
+  """
+
+    SUPPORTED_ARCH = {'amd64': 'amd64',
+                      'x64': 'amd64',
+                      'arm': 'arm',
+                      'arm64': 'ARM64',
+                      'aarch64': 'ARM64'
+                      }
+
+    def __init__(self, name_string, provider, esrt_guid, arch, description_string, version_string, version_hex):
+        self.Name = name_string
+        self.Provider = provider
+        self.EsrtGuid = esrt_guid
+        self.Arch = arch
+        self.Description = description_string
+        self.VersionString = version_string
+        self.VersionHex = version_hex
+        self._manufacturer = None  # default for optional feature
+        self._date = datetime.date.today()
+
+    @property
+    def Name(self):
+        return self._name
+
+    @Name.setter
+    def Name(self, value):
+        # test here for invalid chars
+        if not (re.compile(r'[\w-]*$')).match(value):
+            logging.critical("Name invalid: '%s'", value)
+            raise ValueError("Name has invalid chars.")
+        self._name = value
+
+    @property
+    def Provider(self):
+        return self._provider
+
+    @Provider.setter
+    def Provider(self, value):
+        self._provider = value
+
+    @property
+    def Manufacturer(self):
+        if(self._manufacturer is None):
+            return self.Provider
+
+        return self._manufacturer
+
+    @Manufacturer.setter
+    def Manufacturer(self, value):
+        self._manufacturer = value
+
+    @property
+    def Description(self):
+        return self._description
+
+    @Description.setter
+    def Description(self, value):
+        self._description = value
+
+    @property
+    def EsrtGuid(self):
+        return self._esrtguid
+
+    @EsrtGuid.setter
+    def EsrtGuid(self, value):
+        uuid.UUID(value)  # if this works it is valid...otherwise throws exception
+        # todo - make sure it is formatted exactly right
+        self._esrtguid = value
+
+    @property
+    def VersionString(self):
+        return self._versionstring
+
+    @VersionString.setter
+    def VersionString(self, value):
+        c = value.count(".")
+        if(c < 1) or (c > 3):
+            logging.critical("Version string in invalid format.")
+            raise ValueError("VersionString must be in format of xx.xx -> xx.xx.xx.xx")
+        self._versionstring = value
+
+    @property
+    def VersionHex(self):
+        return "0x%08X" % self._versionhex
+
+    @VersionHex.setter
+    def VersionHex(self, value):
+        a = int(value, 0)
+        if(a > 0xFFFFFFFF):
+            logging.critical("VersionHex invalid: '%s'", value)
+            raise ValueError("VersionHex must fit within 32bit value range for unsigned integer")
+        self._versionhex = a
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        key = value.lower()
+        if(key not in InfGenerator.SUPPORTED_ARCH.keys()):
+            logging.critical("Arch invalid: '%s'", value)
+            raise ValueError("Unsupported Architecture")
+        self._arch = InfGenerator.SUPPORTED_ARCH[key]
+
+    @property
+    def Date(self):
+        return self._date.strftime("%m/%d/%Y")
+
+    @Date.setter
+    def Date(self, value):
+        if(not isinstance(value, datetime.date)):
+            raise ValueError("Date must be a datetime.date object")
+        self._date = value
+
+    def MakeInf(self, OutputInfFilePath, FirmwareBinFileName, Rollback=False):
+        RollbackString = ""
+        if(Rollback):
+            RollbackString = 
+ InfGenerator.ROLLBACKTEMPLATE.format(EsrtGuid=self.EsrtGuid)
+
+        binfilename = os.path.basename(FirmwareBinFileName)
+
+        Content = InfGenerator.TEMPLATE.format(
+            Name=self.Name,
+            Date=self.Date,
+            Arch=self.Arch,
+            DriverVersion=self.VersionString,
+            EsrtGuid=self.EsrtGuid,
+            FirmwareBinFile=binfilename,
+            VersionHexString=self.VersionHex,
+            Provider=self.Provider,
+            MfgName=self.Manufacturer,
+            Description=self.Description,
+            Rollback=RollbackString)
+
+        with open(OutputInfFilePath, "w") as f:
+            f.write(Content)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
new file mode 100644
index 0000000000..69992e2c08
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
@@ -0,0 +1,102 @@
+##
+# UefiBuild Plugin that supports Window Capsule files based on the # 
+Windows Firmware Update Platform spec.
+# Creates INF, Cat, and then signs it
+#
+#
+# Copyright (c) 2018, Microsoft Corporation # Copyright (c) 2019, Intel 
+Corporation. All rights reserved.<BR> # SPDX-License-Identifier: 
+BSD-2-Clause-Patent ##
+
+import sys
+import re
+import datetime
+import os
+import logging
+from CatGenerator import CatGenerator
+from InfGenerator import InfGenerator
+from CatGenerator import RunCmd
+
+def CatalogSignWithSignTool(SignToolPath, ToSignFilePath, PfxFilePath, PfxPass=None):
+    # check signtool path
+    if not os.path.exists(SignToolPath):
+        logging.error("Path to signtool invalid.  %s" % SignToolPath)
+        return -1
+
+    # Adjust for spaces in the path (when calling the command).
+    if " " in SignToolPath:
+        SignToolPath = '"' + SignToolPath + '"'
+
+    OutputDir = os.path.dirname(ToSignFilePath)
+    # Signtool docs https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe
+    # todo: link to catalog signing documentation
+    params = "sign /a /fd SHA256 /f " + PfxFilePath
+    if PfxPass is not None:
+        # add password if set
+        params = params + ' /p ' + PfxPass
+    params = params + ' /debug /v "' + ToSignFilePath + '" '
+    ret = RunCmd(SignToolPath, params, workingdir=OutputDir)
+    if(ret != 0):
+        logging.error("Signtool failed %d" % ret)
+    return ret
+
+class WindowsCapsuleSupportHelper(object):
+    @staticmethod
+    def _LocateLatestWindowsKits():
+        result = None
+
+        # Start with a base path and use it to start locating the ideal directory.
+        base_path = os.path.join(os.getenv("ProgramFiles(x86)"), 
+ "Windows Kits")
+
+        # Check for Win 10 kits first.
+        base_10_path = os.path.join(base_path, "10", "bin")
+        if os.path.isdir(base_10_path):
+            # If you can find one of the new kit paths, use it.
+            # Walk backwards to test the most recent kit first.
+            for sub_path in reversed(os.listdir(base_10_path)):
+                if sub_path.startswith("10.") and os.path.isdir(os.path.join(base_10_path, sub_path, "x64")):
+                    result = os.path.join(base_10_path, sub_path, "x64")
+                    break
+
+            # Otherwise, fall back to the legacy path.
+            if not result and os.path.isdir(os.path.join(base_10_path, "x64")):
+                result = os.path.join(base_10_path, "x64")
+        # If not, fall back to Win 8.1.
+        elif os.path.isdir(os.path.join(base_path, "8.1", "bin", "x64")):
+            result = os.path.join(base_path, "8.1", "bin", "x64")
+        return result
+
+    def RegisterHelpers(self, obj):
+        fp = os.path.abspath(__file__)
+        obj.Register("PackageWindowsCapsuleFiles", 
+ WindowsCapsuleSupportHelper.PackageWindowsCapsuleFiles, fp)
+
+
+    @staticmethod
+    def PackageWindowsCapsuleFiles(OutputFolder, ProductName, ProductFmpGuid, CapsuleVersion_DotString,CapsuleVersion_HexString, ProductFwProvider, ProductFwMfgName, ProductFwDesc, CapsuleFileName, PfxFile=None, PfxPass=None, Rollback=False, Arch='amd64', OperatingSystem_String='Win10'):
+        logging.debug("CapsulePackage: Create Windows Capsule Files")
+        #Make INF
+        InfFilePath = os.path.join(OutputFolder, ProductName + ".inf")
+        InfTool = InfGenerator(ProductName, ProductFwProvider, ProductFmpGuid, Arch, ProductFwDesc, CapsuleVersion_DotString, CapsuleVersion_HexString)
+        InfTool.Manufacturer = ProductFwMfgName #optional
+        ret = InfTool.MakeInf(InfFilePath, CapsuleFileName, Rollback)
+        if(ret != 0):
+            raise Exception("CreateWindowsInf Failed with errorcode %d" % ret)
+        #Make CAT
+        CatFilePath = os.path.realpath(os.path.join(OutputFolder, ProductName + ".cat"))
+        CatTool = CatGenerator(Arch, OperatingSystem_String)
+        ret = CatTool.MakeCat(CatFilePath)
+
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(PfxFile is not None):
+            #Find Signtool
+            WinKitsPath = WindowsCapsuleSupportHelper._LocateLatestWindowsKits()
+            SignToolPath = os.path.join(WinKitsPath, "signtool.exe")
+            if not os.path.exists(SignToolPath):
+                raise Exception("Can't find signtool on this machine.")
+            #dev sign the cat file
+            ret = CatalogSignWithSignTool(SignToolPath, CatFilePath, PfxFile, PfxPass)
+            if(ret != 0):
+                raise Exception("Signing Cat file Failed with errorcode %d" % ret)
+        return ret
--
2.20.1.windows.1





-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#41574): https://edk2.groups.io/g/devel/message/41574
Mute This Topic: https://groups.io/mt/31819358/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-

Re: [edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver
Posted by Eric Jin 4 years, 9 months ago
Bob,

Thank for comment. V2 has been sent out to fix this.

Best Regards
Eric

-----Original Message-----
From: Feng, Bob C 
Sent: Wednesday, May 29, 2019 2:47 PM
To: devel@edk2.groups.io; Jin, Eric <eric.jin@intel.com>
Cc: Gao, Liming <liming.gao@intel.com>; Kinney, Michael D <michael.d.kinney@intel.com>
Subject: RE: [edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver

Hi Eric,

Please use "with" statement for the open() calls.

For example,

Replace:
    Fd = open("file","w")
    Do something
    Fd.close

To:
    With open("file","w") as fd:
         Do something

Thanks,
Bob

-----Original Message-----
From: devel@edk2.groups.io [mailto:devel@edk2.groups.io] On Behalf Of Eric Jin
Sent: Tuesday, May 28, 2019 3:11 PM
To: devel@edk2.groups.io
Cc: Feng, Bob C <bob.c.feng@intel.com>; Gao, Liming <liming.gao@intel.com>; Kinney, Michael D <michael.d.kinney@intel.com>
Subject: [edk2-devel] [PATCH] BaseTools/Capsule: Tool to Generate Windows Firmware Update Driver

https://bugzilla.tianocore.org/show_bug.cgi?id=1837

The tool is designed to generate Windows Firmware Update Drivers, the input is one drivername.cap with related parameters, the output Windows Driver package are composed by drivername.cap, drivername.inf and drivername.cat to update the single payload in device.

usage: GenerateWindowsDriver [-h] [--output-folder OUTPUTFOLDER]
                             [--product-fmp-guid PRODUCTFMPGUID]
                             [--capsuleversion-dotstring CAPSULEVERSION_DOTSTRING]
                             [--capsuleversion-hexstring CAPSULEVERSION_HEXSTRING]
                             [--product-fw-provider PRODUCTFWPROVIDER]
                             [--product-fw-mfg-name PRODUCTFWMFGNAME]
                             [--product-fw-desc PRODUCTFWDESC]
                             [--capsule-file-name CAPSULEFILENAME]
                             [--pfx-file PFXFILE] [--arch ARCH]
                             [--operating-system-string OPERATINGSYSTEMSTRING]

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Kinney Michael D <michael.d.kinney@intel.com>
Signed-off-by: Eric Jin <eric.jin@intel.com>
---
 BaseTools/Source/Python/Capsule/CatGenerator.py                | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py       | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/InfGenerator.py                | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 582 insertions(+)

diff --git a/BaseTools/Source/Python/Capsule/CatGenerator.py b/BaseTools/Source/Python/Capsule/CatGenerator.py
new file mode 100644
index 0000000000..737387c296
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/CatGenerator.py
@@ -0,0 +1,155 @@
+## @file
+ # Script to generate Cat files for capsule update based on supplied 
+inf file  #  # Copyright (c) 2019, Microsoft Corporation  # Copyright
+(c) 2019, Intel Corporation. All rights reserved.<BR>  #
+SPDX-License-Identifier: BSD-2-Clause-Patent  #  ##
+
+import os
+import logging
+import datetime
+import subprocess
+import threading
+
+class PropagatingThread(threading.Thread):
+    def run(self):
+        self.exc = None
+        try:
+            if hasattr(self, '_Thread__target'):
+                # Thread uses name mangling prior to Python 3.
+                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
+            else:
+                self.ret = self._target(*self._args, **self._kwargs)
+        except BaseException as e:
+            self.exc = e
+    def join(self, timeout=None):
+        super(PropagatingThread, self).join()
+        if self.exc:
+             raise self.exc
+        return self.ret
+def reader(filepath, outstream, stream):
+    f = None
+    # open file if caller provided path
+    if(filepath):
+        f = open(filepath, "w")
+    while True:
+        s = stream.readline().decode()
+        if not s:
+            stream.close()
+            break
+        if(f is not None):
+            # write to file if caller provided file
+            f.write(s)
+        if(outstream is not None):
+            # write to stream object if caller provided object
+            outstream.write(s)
+        logging.info(s.rstrip())
+    if(f is not None):
+        f.close()
+def RunCmd(cmd, parameters, capture=True, workingdir=None, outfile=None, outstream=None, environ=None):
+    cmd = cmd.strip('"\'')
+    if " " in cmd:
+        cmd = '"' + cmd + '"'
+    if parameters is not None:
+        parameters = parameters.strip()
+        cmd += " " + parameters
+    starttime = datetime.datetime.now()
+    logging.info("Cmd to run is: " + cmd) 
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Starting---------------")
+    logging.info("------------------------------------------------")
+    c = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=workingdir, shell=True, env=environ)
+    if(capture):
+        outr = PropagatingThread(target=reader, args=(outfile, outstream, c.stdout,))
+        outr.start()
+        outr.join()
+        c.wait()
+    else:
+        c.wait()
+
+    endtime = datetime.datetime.now()
+    delta = endtime - starttime
+    logging.info("------------------------------------------------")
+    logging.info("--------------Cmd Output Finished---------------")
+    logging.info("--------- Running Time (mm:ss): {0[0]:02}:{0[1]:02} ----------".format(divmod(delta.seconds, 60)))
+    logging.info("------------------------------------------------")
+    return c.returncode
+
+class CatGenerator(object):
+    SUPPORTED_OS = {'win10': '10',
+                    '10': '10',
+                    '10_au': '10_AU',
+                    '10_rs2': '10_RS2',
+                    '10_rs3': '10_RS3',
+                    '10_rs4': '10_RS4',
+                    'server10': 'Server10',
+                    'server2016': 'Server2016',
+                    'serverrs2': 'ServerRS2',
+                    'serverrs3': 'ServerRS3',
+                    'serverrs4': 'ServerRS4'
+                    }
+
+    def __init__(self, arch, os):
+        self.Arch = arch
+        self.OperatingSystem = os
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        value = value.lower()
+        if(value == "x64") or (value == "amd64"):  # support amd64 value so INF and CAT tools can use same arch value
+            self._arch = "X64"
+        elif(value == "arm"):
+            self._arch = "ARM"
+        elif(value == "arm64") or (value == "aarch64"):  # support UEFI defined aarch64 value as well
+            self._arch = "ARM64"
+        else:
+            logging.critical("Unsupported Architecture: %s", value)
+            raise ValueError("Unsupported Architecture")
+
+    @property
+    def OperatingSystem(self):
+        return self._operatingsystem
+
+    @OperatingSystem.setter
+    def OperatingSystem(self, value):
+        key = value.lower()
+        if(key not in CatGenerator.SUPPORTED_OS.keys()):
+            logging.critical("Unsupported Operating System: %s", key)
+            raise ValueError("Unsupported Operating System")
+        self._operatingsystem = CatGenerator.SUPPORTED_OS[key]
+
+    def MakeCat(self, OutputCatFile, PathToInf2CatTool=None):
+        # Find Inf2Cat tool
+        if(PathToInf2CatTool is None):
+            PathToInf2CatTool = os.path.join(os.getenv("ProgramFiles(x86)"), "Windows Kits", "10",
+                                             "bin", "x86", "Inf2Cat.exe")
+            if not os.path.exists(PathToInf2CatTool):
+                logging.debug("Windows Kit 10 not Found....trying 8.1")
+                # Try 8.1 kit
+                PathToInf2CatTool.replace("10", "8.1")
+
+        # check if exists
+        if not os.path.exists(PathToInf2CatTool):
+            raise Exception("Can't find Inf2Cat on this machine.  Please install the Windows 10 WDK - "
+
+ "https://developer.microsoft.com/en-us/windows/hardware/windows-driver
+ -kit")
+
+        # Adjust for spaces in the path (when calling the command).
+        if " " in PathToInf2CatTool:
+            PathToInf2CatTool = '"' + PathToInf2CatTool + '"'
+
+        OutputFolder = os.path.dirname(OutputCatFile)
+        # Make Cat file
+        cmd = "/driver:. /os:" + self.OperatingSystem + "_" + self.Arch + " /verbose"
+        ret = RunCmd(PathToInf2CatTool, cmd, workingdir=OutputFolder)
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(not os.path.isfile(OutputCatFile)):
+            raise Exception("CAT file (%s) not created" %
+ OutputCatFile)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
new file mode 100644
index 0000000000..1d543b3fca
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/GenerateWindowsDriver.py
@@ -0,0 +1,115 @@
+## @file
+# Generate a capsule windows driver.
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> #
+SPDX-License-Identifier: BSD-2-Clause-Patent #
+
+'''
+GenerateWindowsDriver
+'''
+
+import sys
+import argparse
+import uuid
+import struct
+import subprocess
+import os
+import tempfile
+import shutil
+import platform
+import re
+import logging
+from WindowsCapsuleSupportHelper import WindowsCapsuleSupportHelper
+
+#
+# Globals for help information
+#
+__prog__        = 'GenerateWindowsDriver'
+__version__     = '0.0'
+__copyright__   = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
+__description__ = 'Generate Capsule Windows Driver.\n'
+
+
+if __name__ == '__main__':
+    def convert_arg_line_to_args(arg_line):
+        for arg in arg_line.split():
+            if not arg.strip():
+                continue
+            yield arg
+
+    parser = argparse.ArgumentParser (
+                        prog = __prog__,
+                        description = __description__ + __copyright__,
+                        conflict_handler = 'resolve',
+                        fromfile_prefix_chars = '@'
+                        )
+    parser.convert_arg_line_to_args = convert_arg_line_to_args
+    parser.add_argument("--output-folder", dest = 'OutputFolder', help = "firmware resource update driver package output folder.")
+    parser.add_argument("--product-fmp-guid", dest = 'ProductFmpGuid', help = "firmware GUID of resource update driver package")
+    parser.add_argument("--capsuleversion-dotstring", dest = 'CapsuleVersion_DotString', help = "firmware version with date on which update driver package is authored")
+    parser.add_argument("--capsuleversion-hexstring", dest = 'CapsuleVersion_HexString', help = "firmware version in Hex of update driver package")
+    parser.add_argument("--product-fw-provider", dest = 'ProductFwProvider', help = "vendor/provider of entire firmware resource update driver package")
+    parser.add_argument("--product-fw-mfg-name", dest = 'ProductFwMfgName', help = "manufacturer/vendor of firmware resource update driver package")
+    parser.add_argument("--product-fw-desc", dest = "ProductFwDesc", help = "description about resource update driver")
+    parser.add_argument("--capsule-file-name", dest = 'CapsuleFileName', help ="firmware resource image file")
+    parser.add_argument("--pfx-file", dest = 'PfxFile', help = "pfx file path used to sign resource update driver")
+    parser.add_argument("--arch", dest = 'Arch', help = "supported architecture:arm/x64/amd64/arm64/aarch64", default = 'amd64')
+    parser.add_argument("--operating-system-string", dest = 
+ 'OperatingSystemString', help = "supported operating 
+ system:win10/10/10_au/10_rs2/10_rs3/10_rs4/server10/server2016/serverr
+ s2/serverrs3/serverrs4", default = "win10")
+
+    def ArgCheck(args):
+        Version = args.CapsuleVersion_DotString.split('.')
+
+        if len(Version) != 4:
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name invalid.")
+        for sub in Version:
+            if  int(sub) > 65536:
+                logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+                raise ValueError("Name exceed limit 65536.")
+
+        if not (re.compile(r'[0-9.]*$')).match(args.CapsuleVersion_DotString):
+            logging.critical("Name invalid: '%s'", args.CapsuleVersion_DotString)
+            raise ValueError("Name has invalid chars.")
+
+    def CapsuleGuidCheck(InputFile, Guid):
+        TempCapDecode = 'TempCapDecode.txt'
+        Command = 'python GenerateCapsule.py "' + InputFile + '" --dump-info >' + TempCapDecode
+        os.system (Command)
+        with open(TempCapDecode, 'rb') as f:
+            for line in f:
+                if re.search(b'UpdateImageTypeId', line, re.M|re.I):
+                    CapGuid = str(line).split('= ')[1][:-5]
+                    break
+            f.close()
+            os.remove(TempCapDecode)
+            if (Guid != CapGuid):
+                print('GenerateWindowsDriver error: Different Guid from Capsule')
+                sys.exit(1)
+
+    args = parser.parse_args()
+
+    InputFile = os.path.join(args.OutputFolder, '') + 
+ args.CapsuleFileName
+
+    ProductName = args.CapsuleFileName.strip('.cap')
+    WindowsDriver = WindowsCapsuleSupportHelper ()
+
+    ArgCheck(args)
+    CapsuleGuidCheck(InputFile, args.ProductFmpGuid)
+
+    WindowsDriver.PackageWindowsCapsuleFiles (
+                                                   args.OutputFolder,
+                                                   ProductName,
+                                                   args.ProductFmpGuid,
+                                                   args.CapsuleVersion_DotString,
+                                                   args.CapsuleVersion_HexString,
+                                                   args.ProductFwProvider,
+                                                   args.ProductFwMfgName,
+                                                   args.ProductFwDesc,
+                                                   args.CapsuleFileName,
+                                                   args.PfxFile,
+                                                   None,
+                                                   None,
+                                                   args.Arch,
+                                                   args.OperatingSystemString
+                                                   )
diff --git a/BaseTools/Source/Python/Capsule/InfGenerator.py b/BaseTools/Source/Python/Capsule/InfGenerator.py
new file mode 100644
index 0000000000..508bbed0ef
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/InfGenerator.py
@@ -0,0 +1,210 @@
+## @file
+ # Script to generate inf files for capsule update based on INF 
+TEMPLATE and  # supplied information (Name, Version, ESRT Guid, 
+Rollback, etc.)  #  # Copyright (c) 2019, Microsoft Corporation  # 
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>  #
+SPDX-License-Identifier: BSD-2-Clause-Patent  ##
+
+import os
+import logging
+import datetime
+import re
+import uuid
+
+
+#####
+#
+#####
+class InfGenerator(object):
+
+    ### INF Template ###
+    TEMPLATE = r""";
+; {Name}.inf
+; {DriverVersion}
+; Copyright (C) 2019 Microsoft Corporation.  All Rights Reserved.
+;
+[Version]
+Signature="$WINDOWS NT$"
+Class=Firmware
+ClassGuid={{f2e7dd72-6468-4e36-b6f1-6488f42c1b52}}
+Provider=%Provider%
+DriverVer={Date},{DriverVersion}
+PnpLockdown=1
+CatalogFile={Name}.cat
+[Manufacturer]
+%MfgName% = Firmware,NT{Arch}
+[Firmware.NT{Arch}]
+%FirmwareDesc% = Firmware_Install,UEFI\RES_{{{EsrtGuid}}}
+[Firmware_Install.NT]
+CopyFiles = Firmware_CopyFiles
+{Rollback}
+[Firmware_CopyFiles]
+{FirmwareBinFile}
+[Firmware_Install.NT.Hw]
+AddReg = Firmware_AddReg
+[Firmware_AddReg]
+HKR,,FirmwareId,,{{{EsrtGuid}}}
+HKR,,FirmwareVersion,%REG_DWORD%,{VersionHexString}
+HKR,,FirmwareFilename,,{FirmwareBinFile}
+[SourceDisksNames]
+1 = %DiskName%
+[SourceDisksFiles]
+{FirmwareBinFile} = 1
+[DestinationDirs]
+DefaultDestDir = %DIRID_WINDOWS%,Firmware ; %SystemRoot%\Firmware 
+[Strings] ; localizable
+Provider     = "{Provider}"
+MfgName      = "{MfgName}"
+FirmwareDesc = "{Description}"
+DiskName     = "Firmware Update"
+; non-localizable
+DIRID_WINDOWS = 10
+REG_DWORD     = 0x00010001
+"""
+
+    ROLLBACKTEMPLATE = r"""AddReg    = Firmware_DowngradePolicy_Addreg
+  ;override firmware resource update policy to allow downgrade to lower 
+ version  [Firmware_DowngradePolicy_Addreg]
+
+ HKLM,SYSTEM\CurrentControlSet\Control\FirmwareResources\{{{EsrtGuid}}}
+ ,Policy,%REG_DWORD%,1
+  """
+
+    SUPPORTED_ARCH = {'amd64': 'amd64',
+                      'x64': 'amd64',
+                      'arm': 'arm',
+                      'arm64': 'ARM64',
+                      'aarch64': 'ARM64'
+                      }
+
+    def __init__(self, name_string, provider, esrt_guid, arch, description_string, version_string, version_hex):
+        self.Name = name_string
+        self.Provider = provider
+        self.EsrtGuid = esrt_guid
+        self.Arch = arch
+        self.Description = description_string
+        self.VersionString = version_string
+        self.VersionHex = version_hex
+        self._manufacturer = None  # default for optional feature
+        self._date = datetime.date.today()
+
+    @property
+    def Name(self):
+        return self._name
+
+    @Name.setter
+    def Name(self, value):
+        # test here for invalid chars
+        if not (re.compile(r'[\w-]*$')).match(value):
+            logging.critical("Name invalid: '%s'", value)
+            raise ValueError("Name has invalid chars.")
+        self._name = value
+
+    @property
+    def Provider(self):
+        return self._provider
+
+    @Provider.setter
+    def Provider(self, value):
+        self._provider = value
+
+    @property
+    def Manufacturer(self):
+        if(self._manufacturer is None):
+            return self.Provider
+
+        return self._manufacturer
+
+    @Manufacturer.setter
+    def Manufacturer(self, value):
+        self._manufacturer = value
+
+    @property
+    def Description(self):
+        return self._description
+
+    @Description.setter
+    def Description(self, value):
+        self._description = value
+
+    @property
+    def EsrtGuid(self):
+        return self._esrtguid
+
+    @EsrtGuid.setter
+    def EsrtGuid(self, value):
+        uuid.UUID(value)  # if this works it is valid...otherwise throws exception
+        # todo - make sure it is formatted exactly right
+        self._esrtguid = value
+
+    @property
+    def VersionString(self):
+        return self._versionstring
+
+    @VersionString.setter
+    def VersionString(self, value):
+        c = value.count(".")
+        if(c < 1) or (c > 3):
+            logging.critical("Version string in invalid format.")
+            raise ValueError("VersionString must be in format of xx.xx -> xx.xx.xx.xx")
+        self._versionstring = value
+
+    @property
+    def VersionHex(self):
+        return "0x%08X" % self._versionhex
+
+    @VersionHex.setter
+    def VersionHex(self, value):
+        a = int(value, 0)
+        if(a > 0xFFFFFFFF):
+            logging.critical("VersionHex invalid: '%s'", value)
+            raise ValueError("VersionHex must fit within 32bit value range for unsigned integer")
+        self._versionhex = a
+
+    @property
+    def Arch(self):
+        return self._arch
+
+    @Arch.setter
+    def Arch(self, value):
+        key = value.lower()
+        if(key not in InfGenerator.SUPPORTED_ARCH.keys()):
+            logging.critical("Arch invalid: '%s'", value)
+            raise ValueError("Unsupported Architecture")
+        self._arch = InfGenerator.SUPPORTED_ARCH[key]
+
+    @property
+    def Date(self):
+        return self._date.strftime("%m/%d/%Y")
+
+    @Date.setter
+    def Date(self, value):
+        if(not isinstance(value, datetime.date)):
+            raise ValueError("Date must be a datetime.date object")
+        self._date = value
+
+    def MakeInf(self, OutputInfFilePath, FirmwareBinFileName, Rollback=False):
+        RollbackString = ""
+        if(Rollback):
+            RollbackString =
+ InfGenerator.ROLLBACKTEMPLATE.format(EsrtGuid=self.EsrtGuid)
+
+        binfilename = os.path.basename(FirmwareBinFileName)
+
+        Content = InfGenerator.TEMPLATE.format(
+            Name=self.Name,
+            Date=self.Date,
+            Arch=self.Arch,
+            DriverVersion=self.VersionString,
+            EsrtGuid=self.EsrtGuid,
+            FirmwareBinFile=binfilename,
+            VersionHexString=self.VersionHex,
+            Provider=self.Provider,
+            MfgName=self.Manufacturer,
+            Description=self.Description,
+            Rollback=RollbackString)
+
+        with open(OutputInfFilePath, "w") as f:
+            f.write(Content)
+
+        return 0
diff --git a/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
new file mode 100644
index 0000000000..69992e2c08
--- /dev/null
+++ b/BaseTools/Source/Python/Capsule/WindowsCapsuleSupportHelper.py
@@ -0,0 +1,102 @@
+##
+# UefiBuild Plugin that supports Window Capsule files based on the # 
+Windows Firmware Update Platform spec.
+# Creates INF, Cat, and then signs it
+#
+#
+# Copyright (c) 2018, Microsoft Corporation # Copyright (c) 2019, Intel 
+Corporation. All rights reserved.<BR> # SPDX-License-Identifier:
+BSD-2-Clause-Patent ##
+
+import sys
+import re
+import datetime
+import os
+import logging
+from CatGenerator import CatGenerator
+from InfGenerator import InfGenerator
+from CatGenerator import RunCmd
+
+def CatalogSignWithSignTool(SignToolPath, ToSignFilePath, PfxFilePath, PfxPass=None):
+    # check signtool path
+    if not os.path.exists(SignToolPath):
+        logging.error("Path to signtool invalid.  %s" % SignToolPath)
+        return -1
+
+    # Adjust for spaces in the path (when calling the command).
+    if " " in SignToolPath:
+        SignToolPath = '"' + SignToolPath + '"'
+
+    OutputDir = os.path.dirname(ToSignFilePath)
+    # Signtool docs https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe
+    # todo: link to catalog signing documentation
+    params = "sign /a /fd SHA256 /f " + PfxFilePath
+    if PfxPass is not None:
+        # add password if set
+        params = params + ' /p ' + PfxPass
+    params = params + ' /debug /v "' + ToSignFilePath + '" '
+    ret = RunCmd(SignToolPath, params, workingdir=OutputDir)
+    if(ret != 0):
+        logging.error("Signtool failed %d" % ret)
+    return ret
+
+class WindowsCapsuleSupportHelper(object):
+    @staticmethod
+    def _LocateLatestWindowsKits():
+        result = None
+
+        # Start with a base path and use it to start locating the ideal directory.
+        base_path = os.path.join(os.getenv("ProgramFiles(x86)"),
+ "Windows Kits")
+
+        # Check for Win 10 kits first.
+        base_10_path = os.path.join(base_path, "10", "bin")
+        if os.path.isdir(base_10_path):
+            # If you can find one of the new kit paths, use it.
+            # Walk backwards to test the most recent kit first.
+            for sub_path in reversed(os.listdir(base_10_path)):
+                if sub_path.startswith("10.") and os.path.isdir(os.path.join(base_10_path, sub_path, "x64")):
+                    result = os.path.join(base_10_path, sub_path, "x64")
+                    break
+
+            # Otherwise, fall back to the legacy path.
+            if not result and os.path.isdir(os.path.join(base_10_path, "x64")):
+                result = os.path.join(base_10_path, "x64")
+        # If not, fall back to Win 8.1.
+        elif os.path.isdir(os.path.join(base_path, "8.1", "bin", "x64")):
+            result = os.path.join(base_path, "8.1", "bin", "x64")
+        return result
+
+    def RegisterHelpers(self, obj):
+        fp = os.path.abspath(__file__)
+        obj.Register("PackageWindowsCapsuleFiles",
+ WindowsCapsuleSupportHelper.PackageWindowsCapsuleFiles, fp)
+
+
+    @staticmethod
+    def PackageWindowsCapsuleFiles(OutputFolder, ProductName, ProductFmpGuid, CapsuleVersion_DotString,CapsuleVersion_HexString, ProductFwProvider, ProductFwMfgName, ProductFwDesc, CapsuleFileName, PfxFile=None, PfxPass=None, Rollback=False, Arch='amd64', OperatingSystem_String='Win10'):
+        logging.debug("CapsulePackage: Create Windows Capsule Files")
+        #Make INF
+        InfFilePath = os.path.join(OutputFolder, ProductName + ".inf")
+        InfTool = InfGenerator(ProductName, ProductFwProvider, ProductFmpGuid, Arch, ProductFwDesc, CapsuleVersion_DotString, CapsuleVersion_HexString)
+        InfTool.Manufacturer = ProductFwMfgName #optional
+        ret = InfTool.MakeInf(InfFilePath, CapsuleFileName, Rollback)
+        if(ret != 0):
+            raise Exception("CreateWindowsInf Failed with errorcode %d" % ret)
+        #Make CAT
+        CatFilePath = os.path.realpath(os.path.join(OutputFolder, ProductName + ".cat"))
+        CatTool = CatGenerator(Arch, OperatingSystem_String)
+        ret = CatTool.MakeCat(CatFilePath)
+
+        if(ret != 0):
+            raise Exception("Creating Cat file Failed with errorcode %d" % ret)
+        if(PfxFile is not None):
+            #Find Signtool
+            WinKitsPath = WindowsCapsuleSupportHelper._LocateLatestWindowsKits()
+            SignToolPath = os.path.join(WinKitsPath, "signtool.exe")
+            if not os.path.exists(SignToolPath):
+                raise Exception("Can't find signtool on this machine.")
+            #dev sign the cat file
+            ret = CatalogSignWithSignTool(SignToolPath, CatFilePath, PfxFile, PfxPass)
+            if(ret != 0):
+                raise Exception("Signing Cat file Failed with errorcode %d" % ret)
+        return ret
--
2.20.1.windows.1





-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#41790): https://edk2.groups.io/g/devel/message/41790
Mute This Topic: https://groups.io/mt/31819358/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-