diff --git a/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper.py b/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper.py new file mode 100644 index 0000000000..538bc3a084 --- /dev/null +++ b/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper.py @@ -0,0 +1,62 @@ +## +# UefiBuild Plugin that supports Window Capsule files based on the +# Windows Firmware Update Platform spec. +# Creates INF, Cat, and then signs it +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +import sys +import re +import datetime +import os +import logging +from edk2toolext.environment.plugintypes.uefi_helper_plugin import IUefiHelperPlugin +from edk2toollib.windows.capsule.cat_generator import * +from edk2toollib.windows.capsule.inf_generator import * +from edk2toollib.utility_functions import CatalogSignWithSignTool +from edk2toollib.windows.locate_tools import FindToolInWinSdk + + +class WindowsCapsuleSupportHelper(IUefiHelperPlugin): + + 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 + SignToolPath = FindToolInWinSdk("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 diff --git a/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper_plug_in.json b/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper_plug_in.json new file mode 100644 index 0000000000..9968e68f2f --- /dev/null +++ b/BaseTools/Plugin/WindowsCapsuleSupportHelper/WindowsCapsuleSupportHelper_plug_in.json @@ -0,0 +1,5 @@ +{ + "scope": "global", + "name": "Windows Capsule Support Helper Functions", + "module": "WindowsCapsuleSupportHelper" +} diff --git a/BaseTools/Scripts/GenFmpImageAuth.py b/BaseTools/Scripts/GenFmpImageAuth.py new file mode 100644 index 0000000000..5cbc5dd318 --- /dev/null +++ b/BaseTools/Scripts/GenFmpImageAuth.py @@ -0,0 +1,231 @@ +## +## Script to Generate a UEFI 2.4B FMP compliant Image Auth Header wrapped +## around the payload file. +## +## For dev purposes this script takes a payload file and signs it and encapsulates it +## in the correct headers. This file is then ready to be put into a FMP capsule. +## +## For production use this script has a production flag and a DetachedSignature parameter +## which allows the signing to be done offline. +## +## General process: +## Phase 1: Create payload file by combining payload and monotonic count +## Phase 2: Sign it using signtool +## Phase 3: Wrap payload in headers to create final FMP Image header/payload +## +## +## Copyright (c) Microsoft Corporation. +## SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +import os, sys +from optparse import OptionParser +import logging +import datetime +import struct +import subprocess +import uuid +from edk2toollib.utility_functions import RunCmd +from edk2toollib.utility_functions import DetachedSignWithSignTool + + +gPhase3PackageOnly = False + +# +#main script function +# +def main(): + parser = OptionParser() + #Output debug log + parser.add_option("-l", dest="OutputLog", help="Create an output log file: ie -l out.txt", default=None) + parser.add_option("-o", "--OutputFile", dest="OutputFile", help="Result/Output file", default=None) + parser.add_option("-p", "--payload", dest="Payload", help="Input unsigned payload file", default=None) + parser.add_option("--production", dest="ProductionSign", action="store_true", help="Production Sign Process (no dev signing)", default=False) + parser.add_option("-m", dest="MonotonicCount", help="Monotonic Count Value", default=0) + parser.add_option("-s", dest="DetachedSignature", help="Detached Signature file (production signed phase 3 step only)", default=None) + parser.add_option("--pfxfile", dest="PfxPath", help="Path to PFX file for dev signing", default=None) + parser.add_option("--pfxpass", dest="PfxPass", help="Optional - PFX password for dev signing with PFX cert", default=None) + parser.add_option("--eku", dest="Eku", help="Option -specify EKU value to pass to signtool if required", default=None) + parser.add_option("--SignTool", dest="SignToolPath", help="Path to signtool.exe") + #Turn on dubug level logging + parser.add_option("--debug", action="store_true", dest="debug", help="turn on debug logging level for file log", default=False) + parser.add_option("--dirty", action="store_true", dest="dirty", help="turn on dirty flag to keep intermediate files. Default is to delete them.", default=False) + + (options, args) = parser.parse_args() + + #setup file based logging if outputReport specified + if(options.OutputLog): + if(len(options.OutputLog) < 2): + logging.critical("the output log file parameter is invalid") + return -2 + else: + #setup file based logging + filelogger = logging.FileHandler(filename=options.OutputLog, mode='w') + if(options.debug): + filelogger.setLevel(logging.DEBUG) + else: + filelogger.setLevel(logging.INFO) + + filelogger.setFormatter(formatter) + logging.getLogger('').addHandler(filelogger) + + logging.info("Log Started: " + datetime.datetime.strftime(datetime.datetime.now(), "%A, %B %d, %Y %I:%M%p" )) + + #check for valid files + if not options.Payload: + logging.critical("No Payload file specified") + return -1 + + if not os.path.isfile(options.Payload): + logging.critical("Invalid Path to payload file") + return -2 + + if not options.DetachedSignature: + logging.debug("No Detached Signature File.") + else: + logging.debug("Parameter for detached signature file specified. " + options.DetachedSignature) + logging.debug("Entering Phase2-PackageOnly Mode") + global gPhase3PackageOnly + gPhase3PackageOnly = True + + if not options.OutputFile: + logging.debug("No output file specified. Using default. AuthPayload.FmImageAuth") + options.OutputFile = "AuthPayload.FmImageAuth" + + if(not gPhase3PackageOnly and not options.ProductionSign): + #must have a pfx file + if not options.PfxPath: + logging.critical("No Pfx File given.") + return -7 + if not os.path.isfile(options.PfxPath): + logging.critical("Invalid PFX Path. File doesn't exist. " + options.PfxPath) + return -6 + + logging.debug("Using PFX file: " + str(options.PfxPath)) + + + logging.debug("Production Mode: " + str(options.ProductionSign)) + logging.debug("Monotonic Count: " + str(options.MonotonicCount)) + logging.debug("Output File: " + str(options.OutputFile)) + logging.debug("Dirty Mode: " + str(options.dirty)) + + FileToSign = os.path.join("payload.Temp.ToBeSigned") + + + + #if not doing phase2 only then we need to do presign stuff + if not gPhase3PackageOnly: + #Since we are not in phase3packageonly mode we know no DetachedSignature file speficied. Set to the default output. + OutputDir = os.path.dirname(os.path.abspath(options.OutputFile)) + logging.debug("Temp files will be written to: " + str(OutputDir)) + + #change the path to temp location + FileToSign = os.path.join(OutputDir, FileToSign) + options.DetachedSignature = FileToSign + ".p7" + + #Create a temp file with payload + monotonic count + f = open(FileToSign, "wb") + pf = open(options.Payload, "rb") + f.write(pf.read()) + mc = struct.pack("Q", int(options.MonotonicCount)) + f.write(mc) + pf.close() + f.close() + + + #if not doing production signing then sign it + if not options.ProductionSign: + #check sign tool + if(os.path.exists(options.SignToolPath)): + logging.debug("Signtool.exe found at location: " + options.SignToolPath) + else: + logging.critical("Can't find signtool at location: " + options.SignToolPath) + return -5 + + ret = DetachedSignWithSignTool( + options.SignToolPath, + FileToSign, + options.DetachedSignature, + options.PfxPath, + PfxPass=options.PfxPass, + Eku=options.Eku + ) + + if ret != 0: + logging.critical("DetachedSignWithSignTool Failed: " + str(ret)) + return ret + + if not options.dirty: + logging.debug("Delete temp file: " + str(FileToSign)) + os.remove(FileToSign) + + + else: + logging.critical("File To Production Sign Created: " + FileToSign) + return 0 + + #package the final output (phase 3) + wcugSize = os.path.getsize(options.DetachedSignature) + logging.debug("PKCS7 Signed Data is size: " + str(wcugSize)) + wcugSize = wcugSize + 4 + 2 + 2 + 16 # matches the hdr + guid below + + # + #Header layout and structures defined in UEFI 2.4 Errata B. + # + + #EFI_FIRMWARE_IMAGE_AUTH + #UINT64 Monotonic Count <--count value used when signing it + #WIN_CERTIFICATE_UEFI_GUID AuthInfo + #WIN_CERTIFICATE Hdr + #UINT32 dwLength <--Length of cert header + #UINT16 wRevision <--Revision level of win cert current 0x0200 + #UINT16 wCertType <--WIN_CERT_TYPE_EFI_GUID 0x0EF1 + #EFI_GUID CertType <--gEfiCertPkcs7Guid = { 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7 }} + #UINT8[] PKCS7 SignedData <--DetachedSignature from signtool + #UINT8[] Payload <--Payload file + + #struct format for the header + header = struct.pack("QLHH", int(options.MonotonicCount), int(wcugSize), int("200", 16), int("0EF1", 16)) + pkcsguid = uuid.UUID('{4aafd29d-68df-49ee-8aa9-347d375665a7}') + + f = open(options.OutputFile, "wb") + f.write(header) + f.write(pkcsguid.bytes_le) + sd = open(options.DetachedSignature, "rb") + f.write(sd.read()) + sd.close() + p = open(options.Payload, "rb") + f.write(p.read()) + p.close() + f.close() + logging.critical("Final FMP compliant Authenticated Payload Image File created:\n " + os.path.abspath(str(options.OutputFile))) + + #if user wants temp files deleted and didn't pass in the p7 file....then delete it now + if not options.dirty: + if not gPhase3PackageOnly: + logging.debug("Delete temp file: " + str(options.DetachedSignature)) + os.remove(options.DetachedSignature) + + + return 0 + + +if __name__ == '__main__': + #setup main console as logger + logger = logging.getLogger('') + logger.setLevel(logging.DEBUG) + formatter = logging.Formatter("%(levelname)s - %(message)s") + console = logging.StreamHandler() + console.setLevel(logging.CRITICAL) + console.setFormatter(formatter) + logger.addHandler(console) + + #call main worker function + retcode = main() + + if retcode != 0: + logging.critical("Failed. Return Code: %i" % retcode) + #end logging + logging.shutdown() + sys.exit(retcode) diff --git a/BaseTools/Scripts/WindowsCapsuleFileGen.py b/BaseTools/Scripts/WindowsCapsuleFileGen.py new file mode 100644 index 0000000000..56a3c4408f --- /dev/null +++ b/BaseTools/Scripts/WindowsCapsuleFileGen.py @@ -0,0 +1,129 @@ +## +# Tool to create a Windows Capsule files that complies with +# the Windows Firmware Update Platform specification. +# +# Gen INF, CAT, and then dev sign the CAT if PFX supplied. +# +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +import os +import sys +import logging +import argparse +import datetime + +from edk2toollib.windows.capsule.cat_generator import * +from edk2toollib.windows.capsule.inf_generator import * +from edk2toollib.utility_functions import CatalogSignWithSignTool + +def main(): + parser = argparse.ArgumentParser(description='Generate Windows Firmware Update Platform Files for Capsules') + parser.add_argument("name", help="Firmware Name. No spaces") + parser.add_argument("provider", help="Firmware provider listed in INF") + parser.add_argument("description", help="Firmware description listed in INF") + parser.add_argument("version_string", help="Version String in form of XX.XX.XX[.XX]") + parser.add_argument("version_hex", help="Version String in Hex 0xAABBCCDD must be representable within 32bit") + parser.add_argument("esrt_guid", help="guid string in registry format (########-####-####-####-############) for this ESRT entry") + parser.add_argument("firmware_bin_file_path", help="full path to firmware bin / capsule file") + parser.add_argument('arch', choices=InfGenerator.SUPPORTED_ARCH, help="Architecture targeted by INF and CAT") + parser.add_argument('operating_sytem', choices=CatGenerator.SUPPORTED_OS, help="operating system targeted by INF and CAT") + parser.add_argument("--mfgname", help="Manufacturer name listed in INF") + parser.add_argument("--rollback", action="store_true", dest="rollback", help="build a rollback capsule", default=False) + parser.add_argument("--pfx_file", help="Full Path to PFX file. If not set then signing will not be performed.") + parser.add_argument("--pfx_pass", help="Password for PFX file. Optional based on PFX file") + + + #Turn on dubug level logging + parser.add_argument("--debug", action="store_true", dest="debug", help="turn on debug logging level for file log", default=False) + #Output debug log + parser.add_argument("-l", dest="OutputLog", help="Create an output debug log file: ie -l out.txt", default=None) + + args = parser.parse_args() + + #setup file based logging if outputReport specified + if(args.OutputLog): + if(len(args.OutputLog) < 2): + logging.critical("the output log file parameter is invalid") + return -2 + else: + #setup file based logging + filelogger = logging.FileHandler(filename=args.OutputLog, mode='w') + if(args.debug): + filelogger.setLevel(logging.DEBUG) + else: + filelogger.setLevel(logging.INFO) + + filelogger.setFormatter(formatter) + logging.getLogger('').addHandler(filelogger) + + logging.info("Log Started: " + datetime.datetime.strftime(datetime.datetime.now(), "%A, %B %d, %Y %I:%M%p" )) + OutputFolder = os.path.dirname(args.firmware_bin_file_path) + FirmwareFile = os.path.basename(args.firmware_bin_file_path) + + logging.debug("Make INF") + #Make INF + InfFilePath = os.path.join(OutputFolder, args.name + ".inf") + InfTool = InfGenerator(args.name, args.provider, args.esrt_guid, args.arch, args.description, args.version_string, args.version_hex) + if(args.mfgname is not None): + InfTool.Manufacturer = args.mfgname #optional + ret = InfTool.MakeInf(InfFilePath, FirmwareFile, args.rollback) + if(ret != 0): + logging.critical("CreateWindowsInf Failed with errorcode %d" % ret) + return ret + + #Make CAT + CatFilePath = os.path.realpath(os.path.join(OutputFolder, args.name + ".cat")) + CatTool = CatGenerator(args.arch, args.operating_sytem) + ret = CatTool.MakeCat(CatFilePath) + + if(ret != 0): + logging.critical("Creating Cat file Failed with errorcode %d" % ret) + return ret + + if(args.pfx_file is not None): + logging.debug("PFX file set. Going to do signing") + #Find Signtool + SignToolPath = os.path.join(os.getenv("ProgramFiles(x86)"), "Windows Kits", "8.1", "bin", "x64", "signtool.exe") + if not os.path.exists(SignToolPath): + logging.debug("Failed to find 8.1 version of signtool. Trying 10") + SignToolPath = SignToolPath.replace('8.1', '10') + + if not os.path.exists(SignToolPath): + logging.critical("Can't find signtool on this machine.") + return -3 + #dev sign the cat file + ret = CatalogSignWithSignTool(SignToolPath, CatFilePath, args.pfx_file, args.pfx_pass) + if(ret != 0): + logging.critical("Signing Cat file Failed with errorcode %d" % ret) + return ret + else: + logging.info("No PFX. Not signing") + + return ret + + +#-------------------------------- +# Control starts here +# +#-------------------------------- +if __name__ == '__main__': + #setup main console as logger + logger = logging.getLogger('') + logger.setLevel(logging.DEBUG) + formatter = logging.Formatter("%(levelname)s - %(message)s") + console = logging.StreamHandler() + console.setLevel(logging.CRITICAL) + console.setFormatter(formatter) + logger.addHandler(console) + + #call main worker function + retcode = main() + + if retcode != 0: + logging.critical("Failed. Return Code: %i" % retcode) + #end logging + logging.shutdown() + sys.exit(retcode) diff --git a/BaseTools/Scripts/basetools_scripts_bin_path_env.json b/BaseTools/Scripts/basetools_scripts_bin_path_env.json new file mode 100644 index 0000000000..553ee36fc6 --- /dev/null +++ b/BaseTools/Scripts/basetools_scripts_bin_path_env.json @@ -0,0 +1,4 @@ +{ + "scope": "global", + "flags": ["set_path"] +} diff --git a/MdeModulePkg/Include/Library/CapsuleLib.h b/MdeModulePkg/Include/Library/CapsuleLib.h index 92904ebfb6..e0b42225cf 100644 --- a/MdeModulePkg/Include/Library/CapsuleLib.h +++ b/MdeModulePkg/Include/Library/CapsuleLib.h @@ -50,6 +50,27 @@ ProcessCapsuleImage ( IN EFI_CAPSULE_HEADER *CapsuleHeader ); +// MU_CHANGE - Add a routine to allow firmware-specific handling for capsules +// that need to persist across reset. + +/** + The firmware-specific implementation that stages the capsule image + for processing after reset if it recognized the format of this capsule + image. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleHeader Pointer to the UEFI capsule image to be processed. + + @retval EFI_SUCESS Capsule Image processed successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. +**/ +EFI_STATUS +EFIAPI +StageCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ); + /** This routine is called to process capsules. diff --git a/MdeModulePkg/Include/Library/CapsulePersistLib.h b/MdeModulePkg/Include/Library/CapsulePersistLib.h new file mode 100644 index 0000000000..3f0d5743ac --- /dev/null +++ b/MdeModulePkg/Include/Library/CapsulePersistLib.h @@ -0,0 +1,53 @@ +/** @file -- CapsulePersistLib.h +A public library interface for persisting Capsules across reset. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CAPSULE_PERSIST_LIB_H_ +#define CAPSULE_PERSIST_LIB_H_ + +/** + Persist a Capsule across reset. + + @param[in] CapsuleHeader EFI_CAPSULE_HEADER pointing to Capsule Image to persist. + + @retval EFI_SUCCESS Capsule was successfully persisted. + @retval EFI_DEVICE_ERROR Something went wrong while trying to persist the capsule. + +**/ +EFI_STATUS +EFIAPI +PersistCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ); + +/** + Returns a pointer to a buffer of capsules. + + If no persisted capsules present, CapsuleArray is not modified, and CapsuleArraySize will be set to zero. + + Removes the persistent capsules from whatever the medium of persistence is. + Note: if return is something other than EFI_SUCESS or EFI_BUFFER_TOO_SMALL, removal of all persistent + capsules from persistence is not guaranteed. + + + @param[out] CapsuleArray Pointer to a buffer to hold the capsules. + @param[out] CapsuleArraySize On input, size of CapsuleArray allocation. + On output, size of actual buffer of capsules. + + @retval EFI_SUCCESS Capsules were de-perisisted, and ouptut data is valid. + @retval EFI_BUFFER_TOO_SMALL CapsuleArray buffer is too small to hold all the data. + @retval EFI_DEVICE_ERROR Something went wrong while trying to retrive the capsule. + +**/ +EFI_STATUS +EFIAPI +GetPersistedCapsules ( + OUT EFI_CAPSULE_HEADER *CapsuleArray, + OUT UINTN *CapsuleArraySize + ); + +#endif // CAPSULE_PERSIST_LIB_H_ diff --git a/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.c b/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.c new file mode 100644 index 0000000000..70bac35adb --- /dev/null +++ b/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.c @@ -0,0 +1,57 @@ +/** @file -- CapsulePersistLibNull.c +A null implementation of the CapsulePersistLib + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +/** + Persist a Capsule across reset. + + @param[in] CapsuleHeader EFI_CAPSULE_HEADER pointing to Capsule Image to persist. + + @retval EFI_SUCCESS Capsule was successfully persisted. + @retval EFI_DEVICE_ERROR Something went wrong while trying to persist the blob. + +**/ +EFI_STATUS +EFIAPI +PersistCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + return EFI_SUCCESS; +} + +/** + Returns a pointer to a buffer of capsules. + + If no persisted capsules present, CapsuleArray is not modified, and CapsuleArraySize will be set to zero. + + Removes the persistent capsules from whatever the medium of persistence is. + Note: if return is something other than EFI_SUCESS or EFI_BUFFER_TOO_SMALL, removal of all persistent + capsules from persistence is not guaranteed. + + + @param[out] CapsuleArray Pointer to a buffer to hold the capsules. + @param[out] CapsuleArraySize On input, size of CapsuleArray allocation. + On output, size of actual buffer of capsules. + + @retval EFI_SUCCESS Capsules were de-perisisted, and ouptut data is valid. + @retval EFI_BUFFER_TOO_SMALL CapsuleArray buffer is too small to hold all the data. + @retval EFI_DEVICE_ERROR Something went wrong while trying to retrive the capsule. + +**/ +EFI_STATUS +EFIAPI +GetPersistedCapsules ( + OUT EFI_CAPSULE_HEADER *CapsuleArray, + OUT UINTN *CapsuleArraySize + ) +{ + *CapsuleArraySize = 0; + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.inf b/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.inf new file mode 100644 index 0000000000..16c03ea4ab --- /dev/null +++ b/MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.inf @@ -0,0 +1,36 @@ +## @file CapsulePersistLibNull.inf +# A null implementation of the CapsulePersistLib +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = CapsulePersistLibNull + FILE_GUID = 96AAE710-21AB-4881-9D92-8AD19479BB36 + VERSION_STRING = 1.0 + MODULE_TYPE = DXE_RUNTIME_DRIVER + LIBRARY_CLASS = CapsulePersistLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + + +[Sources] + CapsulePersistLibNull.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c index 2433c76a8c..47b4e95196 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c @@ -27,6 +27,7 @@ #include #include #include +#include // MU_CHANGE - Support ConnectAll after loading. #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include // MU_CHANGE - Enable Capsule Persist Lib. #include #include @@ -251,6 +253,16 @@ ValidateFmpCapsule ( FmpCapsuleHeaderSize = sizeof (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof (UINT64)*ItemNum; + // MU_CHANGE [BEGIN] + // Currently we do not support Embedded Drivers. + // This opens up concerns about validating the driver as we can't trust secure boot chain (pk) + if (FmpCapsuleHeader->EmbeddedDriverCount != 0) { + DEBUG ((DEBUG_ERROR, "%a - FMP Capsule contains an embedded driver. This is not supported by this implementation\n", __FUNCTION__)); + return EFI_UNSUPPORTED; + } + + // MU_CHANGE [END] + // Check ItemOffsetList for (Index = 0; Index < ItemNum; Index++) { if (ItemOffsetList[Index] >= FmpCapsuleSize) { @@ -1219,6 +1231,16 @@ ProcessFmpCapsuleImage ( BOOLEAN NotReady; BOOLEAN Abort; + // MU_CHANGE [BEGIN] + // Validate the capsule (perhaps again) before processing in case some one calls + // ProcessFmpCapsuleImage() before or without calling ValidateFmpCapsule() + Status = ValidateFmpCapsule (CapsuleHeader, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + // MU_CHANGE [END] + if (!IsFmpCapsuleGuid (&CapsuleHeader->CapsuleGuid)) { return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), CapFileName, ResetRequired); } @@ -1245,6 +1267,14 @@ ProcessFmpCapsuleImage ( return EFI_SUCCESS; } + // MU_CHANGE [BEGIN] + // ConnectAll to ensure + // All the communication protocol required by driver in capsule installed + // All FMP protocols are installed + // + EfiBootManagerConnectAll (); + // MU_CHANGE [END] + // // 1. Try to load & start all the drivers within capsule // @@ -1270,6 +1300,15 @@ ProcessFmpCapsuleImage ( } } + // MU_CHANGE [BEGIN] + // Connnect all again to connect drivers within capsule + // + if (FmpCapsuleHeader->EmbeddedDriverCount > 0) { + EfiBootManagerConnectAll (); + } + + // MU_CHANGE [END] + // // 2. Route payload to right FMP instance // @@ -1615,6 +1654,30 @@ ProcessCapsuleImage ( return ProcessThisCapsuleImage (CapsuleHeader, NULL, NULL); } +/** MU_CHANGE - START + The firmware implements to process the capsule image. + + @param CapsuleHeader Points to a capsule header. + + @retval EFI_SUCESS Process Capsule Image successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. + @retval EFI_DEVICE_ERROR Something went wrong staging the capsule +**/ +EFI_STATUS +EFIAPI +StageCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) { + return EFI_UNSUPPORTED; + } + + return PersistCapsule (CapsuleHeader); +} + +// MU_CHANGE - END + /** Callback function executed when the EndOfDxe event group is signaled. diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf index 095e062a6e..16d4129203 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf @@ -4,6 +4,7 @@ # Capsule library instance for DXE_DRIVER module types. # # Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.
+# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -52,6 +53,8 @@ FileHandleLib UefiBootManagerLib VariablePolicyHelperLib + ResetUtilityLib ## MU_CHANGE - Use the enhanced reset subtype. + CapsulePersistLib ## MU_CHANGE - Enable Capsule Persist Lib. [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax ## CONSUMES @@ -97,6 +100,7 @@ ## SOMETIMES_PRODUCES ## Variable:L"BootNext" gEfiGlobalVariableGuid gEdkiiCapsuleOnDiskNameGuid ## SOMETIMES_CONSUMES ## GUID + gCapsuleUpdateCompleteResetGuid ## MU_CHANGE - Use the enhanced reset subtype. [Depex] gEfiVariableWriteArchProtocolGuid diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c index 2a38a3d95b..3115eaaf29 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c @@ -30,6 +30,8 @@ #include #include #include +#include // MU_CHANGE - Use the enhanced reset subtype. +#include // MU_CHANGE - Enable Capsule Persist Lib. #include @@ -125,10 +127,11 @@ ValidateCapsuleNameCapsuleIntegrity ( extern BOOLEAN mDxeCapsuleLibEndOfDxe; BOOLEAN mNeedReset = FALSE; -VOID **mCapsulePtr; -CHAR16 **mCapsuleNamePtr; -EFI_STATUS *mCapsuleStatusArray; -UINT32 mCapsuleTotalNumber; +VOID **mCapsulePtr; +CHAR16 **mCapsuleNamePtr; +EFI_STATUS *mCapsuleStatusArray; +UINT32 mCapsuleTotalNumber; +EFI_CAPSULE_HEADER *mPersistedCapsules; // MU_CHANGE - Enable Capsule Persist Lib. /** The firmware implements to process the capsule image. @@ -203,9 +206,14 @@ UpdateImageProgress ( } } + // MU_CHANGE - Return success on Progress Display Error to allow capsule to proceed in the face of progress errors. Status = DisplayUpdateProgress (Completion, Color); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "DisplayUpdateProgress returned %r\n", Status)); + } - return Status; + return EFI_SUCCESS; + // MU_CHANGE - Return success on Progress Display Error to allow capsule to proceed in the face of progress errors.END } /** @@ -225,12 +233,49 @@ InitCapsulePtr ( UINTN CapsuleNameCapsuleTotalNumber; VOID **CapsuleNameCapsulePtr; EFI_PHYSICAL_ADDRESS *CapsuleNameAddress; + // MU_CHANGE - Enable Capsule Persist Lib. + EFI_STATUS PersistStatus; + UINTN PersistedCapsuleBufferSize; + EFI_CAPSULE_HEADER *CurrentPersistedCapsule; CapsuleNameNumber = 0; CapsuleNameTotalNumber = 0; CapsuleNameCapsuleTotalNumber = 0; CapsuleNameCapsulePtr = NULL; + // MU_CHANGE [BEGIN] - Enable Capsule Persist Lib. + // + // Find all persisted capsules + // + mPersistedCapsules = NULL; + PersistedCapsuleBufferSize = 0; + PersistStatus = GetPersistedCapsules (mPersistedCapsules, &PersistedCapsuleBufferSize); + if (PersistStatus == EFI_BUFFER_TOO_SMALL) { + mPersistedCapsules = AllocatePool (PersistedCapsuleBufferSize); + if (mPersistedCapsules != NULL) { + // if allocation succeeds, go grab all the capsules. Otherwise, just pass. + // maybe the HOB Capsules will still work. + PersistStatus = GetPersistedCapsules (mPersistedCapsules, &PersistedCapsuleBufferSize); + } + } + + // success from first call just means no capsules to look at, and PersistedCapusleBufferSize is 0. + // Failure to allocate means mPersistedCapsules = NULL, so no capsules to look at. + if (!EFI_ERROR (PersistStatus) && (mPersistedCapsules != NULL) && (PersistedCapsuleBufferSize > 0)) { + CurrentPersistedCapsule = mPersistedCapsules; + while ((UINT8 *)CurrentPersistedCapsule < ((UINT8 *)mPersistedCapsules + PersistedCapsuleBufferSize)) { + if (CurrentPersistedCapsule->CapsuleImageSize == 0) { + // Avoid an infinite loop in the case where corrupted capsule has CapsuleImageSize = 0. + break; + } + + mCapsuleTotalNumber++; + CurrentPersistedCapsule = (EFI_CAPSULE_HEADER *)((UINT8 *)CurrentPersistedCapsule + CurrentPersistedCapsule->CapsuleImageSize); + } + } + + // MU_CHANGE [END] - Enable Capsule Persist Lib. + // // Find all capsule images from hob // @@ -269,6 +314,13 @@ InitCapsulePtr ( if (mCapsuleStatusArray == NULL) { DEBUG ((DEBUG_ERROR, "Allocate mCapsuleStatusArray fail!\n")); FreePool (mCapsulePtr); + // MU_CHANGE - Enable Capsule Persist Lib. + if (mPersistedCapsules != NULL) { + FreePool (mPersistedCapsules); + mPersistedCapsules = NULL; + } + + // MU_CHANGE - Enable Capsule Persist Lib End mCapsulePtr = NULL; mCapsuleTotalNumber = 0; return; @@ -303,6 +355,22 @@ InitCapsulePtr ( HobPointer.Raw = GET_NEXT_HOB (HobPointer); } + // MU_CHANGE [BEGIN] - Enable Capsule Persist Lib. + if (!EFI_ERROR (PersistStatus) && (mPersistedCapsules != NULL) && (PersistedCapsuleBufferSize > 0)) { + CurrentPersistedCapsule = mPersistedCapsules; + while ((UINT8 *)CurrentPersistedCapsule < ((UINT8 *)mPersistedCapsules + PersistedCapsuleBufferSize)) { + if (CurrentPersistedCapsule->CapsuleImageSize == 0) { + // Avoid an infinite loop in the case where corrupted capsule has CapsuleImageSize = 0. + break; + } + + mCapsulePtr[Index++] = (VOID *)CurrentPersistedCapsule; + CurrentPersistedCapsule = (EFI_CAPSULE_HEADER *)((UINT8 *)CurrentPersistedCapsule + CurrentPersistedCapsule->CapsuleImageSize); + } + } + + // MU_CHANGE [END] - Enable Capsule Persist Lib + // // Find Capsule On Disk Names // @@ -519,8 +587,9 @@ ProcessTheseCapsules ( // // We didn't find a hob, so had no errors. // - DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n")); - mNeedReset = TRUE; + // MU_CHANGE: switch from error to info below, adjust print string + DEBUG ((DEBUG_INFO, "%a - Can not find capsule data\n", __FUNCTION__)); + // mNeedReset = TRUE; // MU_CHANGE - turn off needing the reset return EFI_SUCCESS; } @@ -554,7 +623,9 @@ ProcessTheseCapsules ( } } - DEBUG ((DEBUG_INFO, "Updating the firmware ......\n")); + // MU_CHANGE - Printing to the screen is unacceptable in production FW. + // Even if the graphics capsule didn't exist. + // DEBUG ((DEBUG_INFO, "Updating the firmware ......\n")); // // All capsules left are recognized by platform. @@ -635,7 +706,10 @@ DoResetSystem ( REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32 (PcdStatusCodeSubClassCapsule) | PcdGet32 (PcdCapsuleStatusCodeResettingSystem))); - gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); + // MU_CHANGE - Use the enhanced reset subtype so that this reset can be filtered/handled + // in a platform-specific way. + // gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); + ResetSystemWithSubtype (EfiResetCold, &gCapsuleUpdateCompleteResetGuid); CpuDeadLoop (); } @@ -679,6 +753,12 @@ ProcessCapsules ( { EFI_STATUS Status; + // MU_CHANGE [BEGIN] + // TEMP CHANGE - BDS is not currently wired to call this twice + // and 1) we block embedded drivers and 2) this all falls + // within our trust model, so we can execute in a single pass. + + /* if (!mDxeCapsuleLibEndOfDxe) { Status = ProcessTheseCapsules (TRUE); @@ -690,14 +770,17 @@ ProcessCapsules ( DoResetSystem (); } } else { - Status = ProcessTheseCapsules (FALSE); - // - // Reboot System if required after all capsule processed - // - if (mNeedReset) { - DoResetSystem (); - } + */ + // Status = ProcessTheseCapsules(FALSE); + Status = ProcessTheseCapsules (TRUE); + // + // Reboot System if required after all capsule processed + // + if (mNeedReset) { + DoResetSystem (); } + // } + // MU_CHANGE [END] return Status; } diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c index 9054ae446e..2838c6a9c4 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c @@ -68,3 +68,25 @@ ProcessCapsules ( { return EFI_UNSUPPORTED; } + +// MU_CHANGE [BEGIN] - This implementation is BootServices/Runtime specific. +// Move most business logic into specific Dxe vs RuntimeDxe libs. + +/** + Function indicate the current completion progress of the firmware + update. Platform may override with own specific progress function. + + @param[in] Completion A value between 1 and 100 indicating the current completion progress of the firmware update + + @retval EFI_SUCESS Input capsule is a correct FMP capsule. +**/ +EFI_STATUS +EFIAPI +Update_Image_Progress ( + IN UINTN Completion + ) +{ + return EFI_SUCCESS; +} + +// MU_CHANGE [END] diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf index bf56f4623f..e65d95d46d 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf @@ -49,6 +49,8 @@ PrintLib HobLib BmpSupportLib + UefiBootManagerLib ## MU_CHANGE - Enable ConnectAll before capsule processing. + CapsulePersistLib ## MU_CHANGE - Enable Capsule Persist Lib. [Protocols] diff --git a/MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.c index aced356324..4e3f964c31 100644 --- a/MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.c +++ b/MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.c @@ -2,6 +2,7 @@ Null Dxe Capsule Library instance does nothing and returns unsupport status. Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -167,3 +168,25 @@ CoDRemoveTempFile ( { return EFI_UNSUPPORTED; } + +// MU_CHANGE [BEGIN] - Provide simpler interface without assumptions of staging medium. + +/** + The firmware implements to process the capsule image. + + @param CapsuleHeader Points to a capsule header. + + @retval EFI_SUCESS Process Capsule Image successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. + @retval EFI_DEVICE_ERROR Something went wrong staging the capsule +**/ +EFI_STATUS +EFIAPI +StageCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + return EFI_UNSUPPORTED; +} + +// MU_CHANGE [END] - Provide simpler interface without assumptions of staging medium. diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index c7d9e85247..fd74fe492e 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -187,6 +187,13 @@ ## @libraryclass This library is used to report memory type information change ## across a reboot. MemoryTypeInformationChangeLib|Include/Library/MemoryTypeInformationChangeLib.h + + ## MU_CHANGE + ## @libraryclass A public library interface for persisting Capsules across reset. + # This provides platform-defined support for persistence on architectures + # that may not support "persist in RAM" across a reset. + # + CapsulePersistLib|Include/Library/CapsulePersistLib.h ## MU_CHANGE - Add DeviceStateLib to MdeModulePkg ## @libraryclass Provides a way to store and update the current device state diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 8783c37542..f63d76c97c 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -74,6 +74,7 @@ TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf + CapsulePersistLib|MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.inf ## MU_CHANGE PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf @@ -235,6 +236,7 @@ MdeModulePkg/Library/ParallelLzmaCustomDecompressLib/ParallelLzmaCustomDecompressLib.inf ## MU_CHANGE MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf MdeModulePkg/Library/MemoryTypeInformationChangeLibNull/MemoryTypeInformationChangeLibNull.inf ## MU_CHANGE + MdeModulePkg/Library/CapsulePersistLibNull/CapsulePersistLibNull.inf ## MU_CHANGE MdeModulePkg/Library/SecurityLockAuditDebugMessageLib/SecurityLockAuditDebugMessageLib.inf ## MU_CHANGE MdeModulePkg/Library/SecurityLockAuditLibNull/SecurityLockAuditLibNull.inf ## MU_CHANGE MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf diff --git a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c index 05945d54fa..7b81839df2 100644 --- a/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c +++ b/MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleService.c @@ -178,6 +178,15 @@ UpdateCapsule ( if ((CapsuleHeader->Flags & CAPSULE_FLAGS_INITIATE_RESET) != 0) { InitiateReset = TRUE; } + + // MU_CHANGE - Stage Runtime Capsules (in case special handling is needed) + // To persist across reset. + Status = StageCapsuleImage (CapsuleHeader); + if (EFI_ERROR (Status)) { + return Status; + } + + // MU_CHANGE - End } }