]> git.proxmox.com Git - mirror_edk2.git/blobdiff - FmpDevicePkg/FmpDxe/FmpDxe.c
FmpDevicePkg/FmpDxe: Use new FmpDeviceLib APIs
[mirror_edk2.git] / FmpDevicePkg / FmpDxe / FmpDxe.c
index f485f5b939f52fec2a8e27263c2e06c5d3fe7d0d..dab3310834c113a05cdcc5d15c2eda91f7d6aaf3 100644 (file)
@@ -4,46 +4,13 @@
   information provided through PCDs and libraries.\r
 \r
   Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>\r
-  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>\r
 \r
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
-#include <PiDxe.h>\r
-#include <Library/DebugLib.h>\r
-#include <Library/BaseLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/UefiLib.h>\r
-#include <Library/FmpAuthenticationLib.h>\r
-#include <Library/FmpDeviceLib.h>\r
-#include <Library/FmpPayloadHeaderLib.h>\r
-#include <Library/CapsuleUpdatePolicyLib.h>\r
-#include <Protocol/FirmwareManagement.h>\r
-#include <Protocol/FirmwareManagementProgress.h>\r
-#include <Guid/SystemResourceTable.h>\r
-#include <Guid/EventGroup.h>\r
-#include "VariableSupport.h"\r
-\r
-#define VERSION_STRING_NOT_SUPPORTED  L"VERSION STRING NOT SUPPORTED"\r
-#define VERSION_STRING_NOT_AVAILABLE  L"VERSION STRING NOT AVAILABLE"\r
-\r
-/**\r
-  Check to see if any of the keys in PcdFmpDevicePkcs7CertBufferXdr matches\r
-  the test key.  PcdFmpDeviceTestKeySha256Digest contains the SHA256 hash of\r
-  the test key.  For each key in PcdFmpDevicePkcs7CertBufferXdr, compute the\r
-  SHA256 hash and compare it to PcdFmpDeviceTestKeySha256Digest.  If the\r
-  SHA256 hash matches or there is then error computing the SHA256 hash, then\r
-  set PcdTestKeyUsed to TRUE.  Skip this check if PcdTestKeyUsed is already\r
-  TRUE or PcdFmpDeviceTestKeySha256Digest is not exactly SHA256_DIGEST_SIZE\r
-  bytes.\r
-**/\r
-VOID\r
-DetectTestKey (\r
-  VOID\r
-  );\r
+#include "FmpDxe.h"\r
 \r
 ///\r
 /// FILE_GUID from FmpDxe.inf.  When FmpDxe.inf is used in a platform, the\r
@@ -56,30 +23,73 @@ const EFI_GUID  mDefaultModuleFileGuid = {
   0x78ef0a56, 0x1cf0, 0x4535, { 0xb5, 0xda, 0xf6, 0xfd, 0x2f, 0x40, 0x5a, 0x11 }\r
 };\r
 \r
-EFI_FIRMWARE_IMAGE_DESCRIPTOR  mDesc;\r
-BOOLEAN                        mDescriptorPopulated     = FALSE;\r
-BOOLEAN                        mRuntimeVersionSupported = TRUE;\r
-BOOLEAN                        mFmpInstalled            = FALSE;\r
-\r
 ///\r
-/// Function pointer to progress function\r
+/// TRUE if FmpDeviceLib manages a single firmware storage device.\r
 ///\r
-EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS  mProgressFunc      = NULL;\r
-BOOLEAN                                        mProgressSupported = FALSE;\r
+BOOLEAN  mFmpSingleInstance = FALSE;\r
 \r
-CHAR16  *mImageIdName = NULL;\r
-UINT64  mImageId      = 0x1;\r
-CHAR16  *mVersionName = NULL;\r
+///\r
+/// Firmware Management Protocol instance that is initialized in the entry\r
+/// point from PCD settings.\r
+///\r
+EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL  mFmpProgress;\r
 \r
-EFI_EVENT  mFmpDeviceLockEvent;\r
 //\r
-// Indicates if an attempt has been made to lock a\r
-// FLASH storage device by calling FmpDeviceLock().\r
-// A FLASH storage device may not support being locked,\r
-// so this variable is set to TRUE even if FmpDeviceLock()\r
-// returns an error.\r
+// Template of the private context structure for the Firmware Management\r
+// Protocol instance\r
 //\r
-BOOLEAN    mFmpDeviceLocked = FALSE;\r
+const FIRMWARE_MANAGEMENT_PRIVATE_DATA  mFirmwareManagementPrivateDataTemplate = {\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA_SIGNATURE,  // Signature\r
+  NULL,                                        // Handle\r
+  {                                            // Fmp\r
+    GetTheImageInfo,\r
+    GetTheImage,\r
+    SetTheImage,\r
+    CheckTheImage,\r
+    GetPackageInfo,\r
+    SetPackageInfo\r
+  },\r
+  FALSE,                                       // DescriptorPopulated\r
+  {                                            // Desc\r
+    1,     // ImageIndex\r
+    //\r
+    // ImageTypeId\r
+    //\r
+    { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },\r
+    1,     // ImageId\r
+    NULL,  // ImageIdName\r
+    0,     // Version\r
+    NULL,  // VersionName\r
+    0,     // Size\r
+    0,     // AttributesSupported\r
+    0,     // AttributesSetting\r
+    0,     // Compatibilities\r
+    0,     // LowestSupportedImageVersion\r
+    0,     // LastAttemptVersion\r
+    0,     // LastAttemptStatus\r
+    0      // HardwareInstance\r
+  },\r
+  NULL,                                        // ImageIdName\r
+  NULL,                                        // VersionName\r
+  TRUE,                                        // RuntimeVersionSupported\r
+  NULL,                                        // FmpDeviceLockEvent\r
+  FALSE                                        // FmpDeviceLocked\r
+};\r
+\r
+///\r
+/// GUID that is used to create event used to lock the firmware storage device.\r
+///\r
+EFI_GUID  *mLockGuid = NULL;\r
+\r
+///\r
+/// Progress() function pointer passed into SetTheImage()\r
+///\r
+EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS  mProgressFunc = NULL;\r
+\r
+///\r
+/// Null-terminated Unicode string retrieved from PcdFmpDeviceImageIdName.\r
+///\r
+CHAR16  *mImageIdName = NULL;\r
 \r
 /**\r
   Callback function to report the process of the firmware updating.\r
@@ -111,10 +121,6 @@ FmpDxeProgress (
 \r
   Status = EFI_UNSUPPORTED;\r
 \r
-  if (!mProgressSupported) {\r
-    return Status;\r
-  }\r
-\r
   if (mProgressFunc == NULL) {\r
     return Status;\r
   }\r
@@ -125,7 +131,6 @@ FmpDxeProgress (
   Status = mProgressFunc (((Completion * 92) / 100) + 6);\r
 \r
   if (Status == EFI_UNSUPPORTED) {\r
-    mProgressSupported = FALSE;\r
     mProgressFunc = NULL;\r
   }\r
 \r
@@ -183,7 +188,7 @@ GetImageTypeNameString (
   2. Check if we have a variable for lowest supported version (this will be updated with each capsule applied)\r
   3. Check Fixed at build PCD\r
 \r
-  Take the largest value\r
+  @return  The largest value\r
 \r
 **/\r
 UINT32\r
@@ -237,101 +242,117 @@ GetLowestSupportedVersion (
 }\r
 \r
 /**\r
-  Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the module global\r
-  variable mDesc.\r
+  Populates the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure in the private\r
+  context structure.\r
+\r
+  @param[in] Private  Pointer to the private context structure for the\r
+                      Firmware Management Protocol instance.\r
 \r
 **/\r
 VOID\r
 PopulateDescriptor (\r
-  VOID\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private\r
   )\r
 {\r
   EFI_STATUS  Status;\r
 \r
-  mDesc.ImageIndex = 1;\r
-  CopyGuid (&mDesc.ImageTypeId, GetImageTypeIdGuid());\r
-  mDesc.ImageId = mImageId;\r
-  mDesc.ImageIdName = GetImageTypeNameString();\r
+  if (Private->DescriptorPopulated) {\r
+    return;\r
+  }\r
+\r
+  Private->Descriptor.ImageIndex = 1;\r
+  CopyGuid (&Private->Descriptor.ImageTypeId, GetImageTypeIdGuid());\r
+  Private->Descriptor.ImageId     = Private->Descriptor.ImageIndex;\r
+  Private->Descriptor.ImageIdName = GetImageTypeNameString();\r
 \r
   //\r
   // Get the version.  Some devices don't support getting the firmware version\r
   // at runtime.  If FmpDeviceLib does not support returning a version, then\r
   // it is stored in a UEFI variable.\r
   //\r
-  Status = FmpDeviceGetVersion (&mDesc.Version);\r
+  Status = FmpDeviceGetVersion (&Private->Descriptor.Version);\r
   if (Status == EFI_UNSUPPORTED) {\r
-    mRuntimeVersionSupported = FALSE;\r
-    mDesc.Version = GetVersionFromVariable();\r
+    Private->RuntimeVersionSupported = FALSE;\r
+    Private->Descriptor.Version = GetVersionFromVariable();\r
   } else if (EFI_ERROR (Status)) {\r
     //\r
     // Unexpected error.   Use default version.\r
     //\r
     DEBUG ((DEBUG_ERROR, "FmpDxe: GetVersion() from FmpDeviceLib (%s) returned %r\n", GetImageTypeNameString(), Status));\r
-    mDesc.Version = DEFAULT_VERSION;\r
+    Private->Descriptor.Version = DEFAULT_VERSION;\r
   }\r
 \r
   //\r
   // Free the current version name.  Shouldn't really happen but this populate\r
   // function could be called multiple times (to refresh).\r
   //\r
-  if (mVersionName != NULL) {\r
-    FreePool (mVersionName);\r
-    mVersionName = NULL;\r
+  if (Private->Descriptor.VersionName != NULL) {\r
+    FreePool (Private->Descriptor.VersionName);\r
+    Private->Descriptor.VersionName = NULL;\r
   }\r
 \r
   //\r
   // Attempt to get the version string from the FmpDeviceLib\r
   //\r
-  Status = FmpDeviceGetVersionString (&mVersionName);\r
+  Status = FmpDeviceGetVersionString (&Private->Descriptor.VersionName);\r
   if (Status == EFI_UNSUPPORTED) {\r
     DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() unsupported in FmpDeviceLib.\n"));\r
-    mVersionName = AllocateCopyPool (\r
-                     sizeof (VERSION_STRING_NOT_SUPPORTED),\r
-                     VERSION_STRING_NOT_SUPPORTED\r
-                     );\r
+    Private->Descriptor.VersionName = AllocateCopyPool (\r
+                                        sizeof (VERSION_STRING_NOT_SUPPORTED),\r
+                                        VERSION_STRING_NOT_SUPPORTED\r
+                                        );\r
   } else if (EFI_ERROR (Status)) {\r
     DEBUG ((DEBUG_INFO, "FmpDxe: GetVersionString() not available in FmpDeviceLib.\n"));\r
-    mVersionName = AllocateCopyPool (\r
-                     sizeof (VERSION_STRING_NOT_AVAILABLE),\r
-                     VERSION_STRING_NOT_AVAILABLE\r
-                     );\r
+    Private->Descriptor.VersionName = AllocateCopyPool (\r
+                                        sizeof (VERSION_STRING_NOT_AVAILABLE),\r
+                                        VERSION_STRING_NOT_AVAILABLE\r
+                                        );\r
   }\r
 \r
-  mDesc.VersionName = mVersionName;\r
-\r
-  mDesc.LowestSupportedImageVersion = GetLowestSupportedVersion();\r
+  Private->Descriptor.LowestSupportedImageVersion = GetLowestSupportedVersion();\r
 \r
   //\r
   // Get attributes from the FmpDeviceLib\r
   //\r
-  FmpDeviceGetAttributes (&mDesc.AttributesSupported, &mDesc.AttributesSetting);\r
+  FmpDeviceGetAttributes (\r
+    &Private->Descriptor.AttributesSupported,\r
+    &Private->Descriptor.AttributesSetting\r
+    );\r
 \r
   //\r
   // Force set the updatable bits in the attributes;\r
   //\r
-  mDesc.AttributesSupported |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
-  mDesc.AttributesSetting   |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
+  Private->Descriptor.AttributesSupported |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
+  Private->Descriptor.AttributesSetting   |= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;\r
 \r
   //\r
   // Force set the authentication bits in the attributes;\r
   //\r
-  mDesc.AttributesSupported |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
-  mDesc.AttributesSetting   |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
+  Private->Descriptor.AttributesSupported |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
+  Private->Descriptor.AttributesSetting   |= (IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED);\r
 \r
-  mDesc.Compatibilities = 0;\r
+  Private->Descriptor.Compatibilities = 0;\r
 \r
   //\r
   // Get the size of the firmware image from the FmpDeviceLib\r
   //\r
-  Status = FmpDeviceGetSize (&mDesc.Size);\r
+  Status = FmpDeviceGetSize (&Private->Descriptor.Size);\r
   if (EFI_ERROR (Status)) {\r
-    mDesc.Size = 0;\r
+    Private->Descriptor.Size = 0;\r
   }\r
 \r
-  mDesc.LastAttemptVersion = GetLastAttemptVersionFromVariable ();\r
-  mDesc.LastAttemptStatus  = GetLastAttemptStatusFromVariable ();\r
+  Private->Descriptor.LastAttemptVersion = GetLastAttemptVersionFromVariable ();\r
+  Private->Descriptor.LastAttemptStatus  = GetLastAttemptStatusFromVariable ();\r
 \r
-  mDescriptorPopulated = TRUE;\r
+  //\r
+  // Get the hardware instance from FmpDeviceLib\r
+  //\r
+  Status = FmpDeviceGetHardwareInstance (&Private->Descriptor.HardwareInstance);\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    Private->Descriptor.HardwareInstance = 0;\r
+  }\r
+\r
+  Private->DescriptorPopulated = TRUE;\r
 }\r
 \r
 /**\r
@@ -385,10 +406,17 @@ GetTheImageInfo (
   OUT    CHAR16                            **PackageVersionName\r
   )\r
 {\r
-  EFI_STATUS Status;\r
+  EFI_STATUS                        Status;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
 \r
   Status = EFI_SUCCESS;\r
 \r
+  //\r
+  // Retrieve the private context structure\r
+  //\r
+  Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This);\r
+  FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
+\r
   //\r
   // Check for valid pointer\r
   //\r
@@ -424,15 +452,15 @@ GetTheImageInfo (
   //\r
   *ImageInfoSize = sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR);\r
 \r
-\r
-  if (!mDescriptorPopulated) {\r
-    PopulateDescriptor();\r
-  }\r
+  //\r
+  // make sure the descriptor has already been loaded\r
+  //\r
+  PopulateDescriptor (Private);\r
 \r
   //\r
   // Copy the image descriptor\r
   //\r
-  CopyMem (ImageInfo, &mDesc, sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR));\r
+  CopyMem (ImageInfo, &Private->Descriptor, sizeof (EFI_FIRMWARE_IMAGE_DESCRIPTOR));\r
 \r
   *DescriptorVersion = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;\r
   *DescriptorCount = 1;\r
@@ -483,11 +511,18 @@ GetTheImage (
   IN OUT UINTN                             *ImageSize\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINTN       Size;\r
+  EFI_STATUS                        Status;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
+  UINTN                             Size;\r
 \r
   Status = EFI_SUCCESS;\r
 \r
+  //\r
+  // Retrieve the private context structure\r
+  //\r
+  Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This);\r
+  FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
+\r
   //\r
   // Check to make sure index is 1 (only 1 image for this device)\r
   //\r
@@ -632,18 +667,19 @@ CheckTheImage (
   OUT UINT32                            *ImageUpdatable\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINTN       RawSize;\r
-  VOID        *FmpPayloadHeader;\r
-  UINTN       FmpPayloadSize;\r
-  UINT32      Version;\r
-  UINT32      FmpHeaderSize;\r
-  UINTN       AllHeaderSize;\r
-  UINT32      Index;\r
-  VOID        *PublicKeyData;\r
-  UINTN       PublicKeyDataLength;\r
-  UINT8       *PublicKeyDataXdr;\r
-  UINT8       *PublicKeyDataXdrEnd;\r
+  EFI_STATUS                        Status;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
+  UINTN                             RawSize;\r
+  VOID                              *FmpPayloadHeader;\r
+  UINTN                             FmpPayloadSize;\r
+  UINT32                            Version;\r
+  UINT32                            FmpHeaderSize;\r
+  UINTN                             AllHeaderSize;\r
+  UINT32                            Index;\r
+  VOID                              *PublicKeyData;\r
+  UINTN                             PublicKeyDataLength;\r
+  UINT8                             *PublicKeyDataXdr;\r
+  UINT8                             *PublicKeyDataXdrEnd;\r
 \r
   Status           = EFI_SUCCESS;\r
   RawSize          = 0;\r
@@ -653,12 +689,16 @@ CheckTheImage (
   FmpHeaderSize    = 0;\r
   AllHeaderSize    = 0;\r
 \r
+  //\r
+  // Retrieve the private context structure\r
+  //\r
+  Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This);\r
+  FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
+\r
   //\r
   // make sure the descriptor has already been loaded\r
   //\r
-  if (!mDescriptorPopulated) {\r
-    PopulateDescriptor();\r
-  }\r
+  PopulateDescriptor (Private);\r
 \r
   if (ImageUpdatable == NULL) {\r
     DEBUG ((DEBUG_ERROR, "FmpDxe: CheckImage() - ImageUpdatable Pointer Parameter is NULL.\n"));\r
@@ -776,11 +816,11 @@ CheckTheImage (
   //\r
   // Check the lowest supported version\r
   //\r
-  if (Version < mDesc.LowestSupportedImageVersion) {\r
+  if (Version < Private->Descriptor.LowestSupportedImageVersion) {\r
     DEBUG (\r
       (DEBUG_ERROR,\r
       "FmpDxe: CheckTheImage() - Version Lower than lowest supported version. 0x%08X < 0x%08X\n",\r
-      Version, mDesc.LowestSupportedImageVersion)\r
+      Version, Private->Descriptor.LowestSupportedImageVersion)\r
       );\r
     *ImageUpdatable = IMAGE_UPDATABLE_INVALID_OLD;\r
     Status = EFI_SUCCESS;\r
@@ -880,17 +920,18 @@ SetTheImage (
   OUT CHAR16                                         **AbortReason\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINT32      Updateable;\r
-  BOOLEAN     BooleanValue;\r
-  UINT32      FmpHeaderSize;\r
-  VOID        *FmpHeader;\r
-  UINTN       FmpPayloadSize;\r
-  UINT32      AllHeaderSize;\r
-  UINT32      IncommingFwVersion;\r
-  UINT32      LastAttemptStatus;\r
-  UINT32      Version;\r
-  UINT32      LowestSupportedVersion;\r
+  EFI_STATUS                        Status;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
+  UINT32                            Updateable;\r
+  BOOLEAN                           BooleanValue;\r
+  UINT32                            FmpHeaderSize;\r
+  VOID                              *FmpHeader;\r
+  UINTN                             FmpPayloadSize;\r
+  UINT32                            AllHeaderSize;\r
+  UINT32                            IncommingFwVersion;\r
+  UINT32                            LastAttemptStatus;\r
+  UINT32                            Version;\r
+  UINT32                            LowestSupportedVersion;\r
 \r
   Status             = EFI_SUCCESS;\r
   Updateable         = 0;\r
@@ -902,6 +943,11 @@ SetTheImage (
   IncommingFwVersion = 0;\r
   LastAttemptStatus  = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;\r
 \r
+  //\r
+  // Retrieve the private context structure\r
+  //\r
+  Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (This);\r
+  FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
 \r
   SetLastAttemptVersionInVariable (IncommingFwVersion); //set to 0 to clear any previous results.\r
 \r
@@ -909,7 +955,7 @@ SetTheImage (
   // if we have locked the device, then skip the set operation.\r
   // it should be blocked by hardware too but we can catch here even faster\r
   //\r
-  if (mFmpDeviceLocked) {\r
+  if (Private->FmpDeviceLocked) {\r
     DEBUG ((DEBUG_ERROR, "FmpDxe: SetTheImage() - Device is already locked.  Can't update.\n"));\r
     Status = EFI_UNSUPPORTED;\r
     goto cleanup;\r
@@ -963,7 +1009,6 @@ SetTheImage (
   }\r
 \r
   mProgressFunc = Progress;\r
-  mProgressSupported = TRUE;\r
 \r
   //\r
   // Checking the image is at least 1%\r
@@ -1084,7 +1129,7 @@ SetTheImage (
   //\r
   // Update the version stored in variable\r
   //\r
-  if (!mRuntimeVersionSupported) {\r
+  if (!Private->RuntimeVersionSupported) {\r
     Version = DEFAULT_VERSION;\r
     GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &Version);\r
     SetVersionInVariable (Version);\r
@@ -1103,7 +1148,6 @@ SetTheImage (
 \r
 cleanup:\r
   mProgressFunc = NULL;\r
-  mProgressSupported = FALSE;\r
   SetLastAttemptStatusInVariable (LastAttemptStatus);\r
 \r
   if (Progress != NULL) {\r
@@ -1117,7 +1161,7 @@ cleanup:
   // Need repopulate after SetImage is called to\r
   // update LastAttemptVersion and LastAttemptStatus.\r
   //\r
-  mDescriptorPopulated = FALSE;\r
+  Private->DescriptorPopulated = FALSE;\r
 \r
   return Status;\r
 }\r
@@ -1228,12 +1272,16 @@ FmpDxeLockEventNotify (
   IN VOID       *Context\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
+  EFI_STATUS                        Status;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
+\r
+  Private = (FIRMWARE_MANAGEMENT_PRIVATE_DATA *)Context;\r
 \r
-  if (!mFmpDeviceLocked) {\r
+  if (!Private->FmpDeviceLocked) {\r
     //\r
     // Lock the firmware device\r
     //\r
+    FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
     Status = FmpDeviceLock();\r
     if (EFI_ERROR (Status)) {\r
       if (Status != EFI_UNSUPPORTED) {\r
@@ -1242,7 +1290,7 @@ FmpDxeLockEventNotify (
         DEBUG ((DEBUG_WARN, "FmpDxe: FmpDeviceLock() returned error.  Status = %r\n", Status));\r
       }\r
     }\r
-    mFmpDeviceLocked = TRUE;\r
+    Private->FmpDeviceLocked = TRUE;\r
   }\r
 }\r
 \r
@@ -1262,85 +1310,198 @@ InstallFmpInstance (
   IN EFI_HANDLE  Handle\r
   )\r
 {\r
-  EFI_STATUS                                   Status;\r
-  EFI_FIRMWARE_MANAGEMENT_PROTOCOL             *Fmp;\r
-  EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL  *FmpProgress;\r
+  EFI_STATUS                        Status;\r
+  EFI_FIRMWARE_MANAGEMENT_PROTOCOL  *Fmp;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
 \r
-  Status      = EFI_SUCCESS;\r
-  Fmp         = NULL;\r
-  FmpProgress = NULL;\r
+  DEBUG ((DEBUG_ERROR, "InstallFmpInstance: Entry\n"));\r
 \r
   //\r
   // Only allow a single FMP Protocol instance to be installed\r
   //\r
-  if (mFmpInstalled) {\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiFirmwareManagementProtocolGuid,\r
+                  (VOID **)&Fmp,\r
+                  NULL,\r
+                  NULL,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
     return EFI_ALREADY_STARTED;\r
   }\r
 \r
   //\r
   // Allocate FMP Protocol instance\r
   //\r
-  Fmp = AllocateZeroPool (sizeof (EFI_FIRMWARE_MANAGEMENT_PROTOCOL));\r
-  if (Fmp == NULL) {\r
-    DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Protocol instance.\n"));\r
+  Private = AllocateCopyPool (\r
+              sizeof (mFirmwareManagementPrivateDataTemplate),\r
+              &mFirmwareManagementPrivateDataTemplate\r
+              );\r
+  if (Private == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for private structure.\n"));\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto cleanup;\r
   }\r
 \r
   //\r
-  // Allocate FMP Progress Protocol instance\r
+  // Initialize private context data structure\r
   //\r
-  FmpProgress = AllocateZeroPool (sizeof (EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL));\r
-  if (FmpProgress == NULL) {\r
-    DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to allocate memory for FMP Progress Protocol instance.\n"));\r
-    Status = EFI_OUT_OF_RESOURCES;\r
-    FreePool (Fmp);\r
+  DEBUG ((DEBUG_ERROR, "InstallFmpInstance: Initialize private context data structure\n"));\r
+\r
+  Private->Handle = Handle;\r
+\r
+  Private->FmpDeviceContext = NULL;\r
+  Status = FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    Private->FmpDeviceContext = NULL;\r
+  } else if (EFI_ERROR (Status)) {\r
     goto cleanup;\r
   }\r
 \r
-  //\r
-  // Set up FMP Protocol function pointers\r
-  //\r
-  Fmp->GetImageInfo   = GetTheImageInfo;\r
-  Fmp->GetImage       = GetTheImage;\r
-  Fmp->SetImage       = SetTheImage;\r
-  Fmp->CheckImage     = CheckTheImage;\r
-  Fmp->GetPackageInfo = GetPackageInfo;\r
-  Fmp->SetPackageInfo = SetPackageInfo;\r
+  DEBUG ((DEBUG_ERROR, "InstallFmpInstance: Lock events\n"));\r
 \r
-  //\r
-  // Fill in FMP Progress Protocol fields for Version 1\r
-  //\r
-  FmpProgress->Version                        = 1;\r
-  FmpProgress->ProgressBarForegroundColor.Raw = PcdGet32 (PcdFmpDeviceProgressColor);\r
-  FmpProgress->WatchdogSeconds                = PcdGet8 (PcdFmpDeviceProgressWatchdogTimeInSeconds);\r
+  if (IsLockFmpDeviceAtLockEventGuidRequired ()) {\r
+    //\r
+    // Lock all UEFI Variables used by this module.\r
+    //\r
+    Status = LockAllFmpVariables ();\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variables.  Status = %r.\n", Status));\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "FmpDxe: All variables locked\n"));\r
+    }\r
+\r
+    //\r
+    // Create and register notify function to lock the FMP device.\r
+    //\r
+    Status = gBS->CreateEventEx (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    FmpDxeLockEventNotify,\r
+                    Private,\r
+                    mLockGuid,\r
+                    &Private->FmpDeviceLockEvent\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to register notification.  Status = %r\n", Status));\r
+    }\r
+    ASSERT_EFI_ERROR (Status);\r
+  } else {\r
+    DEBUG ((DEBUG_VERBOSE, "FmpDxe: Not registering notification to call FmpDeviceLock() because mfg mode\n"));\r
+  }\r
 \r
   //\r
   // Install FMP Protocol and FMP Progress Protocol\r
   //\r
+  DEBUG ((DEBUG_ERROR, "InstallFmpInstance: Install FMP Protocol and FMP Progress Protocol\n"));\r
+\r
   Status = gBS->InstallMultipleProtocolInterfaces (\r
-                  &Handle,\r
-                  &gEfiFirmwareManagementProtocolGuid,\r
-                  Fmp,\r
-                  &gEdkiiFirmwareManagementProgressProtocolGuid,\r
-                  FmpProgress,\r
+                  &Private->Handle,\r
+                  &gEfiFirmwareManagementProtocolGuid, &Private->Fmp,\r
+                  &gEdkiiFirmwareManagementProgressProtocolGuid, &mFmpProgress,\r
                   NULL\r
                   );\r
 \r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((DEBUG_ERROR, "FmpDxe: FMP Protocol install error. Status = %r.\n", Status));\r
-    FreePool (Fmp);\r
+    DEBUG ((DEBUG_ERROR, "FmpDxe: Protocol install error. Status = %r.\n", Status));\r
     goto cleanup;\r
   }\r
 \r
-  DEBUG ((DEBUG_INFO, "FmpDxe: FMP Protocol Installed!\n"));\r
-  mFmpInstalled = TRUE;\r
+  DEBUG ((DEBUG_INFO, "FmpDxe: Protocols Installed!\n"));\r
 \r
 cleanup:\r
 \r
+  if (EFI_ERROR (Status)) {\r
+    if (Private != NULL) {\r
+      if (Private->FmpDeviceLockEvent != NULL) {\r
+        gBS->CloseEvent (Private->FmpDeviceLockEvent);\r
+      }\r
+      FreePool (Private);\r
+    }\r
+  }\r
+\r
   return Status;\r
 }\r
 \r
+/**\r
+  Function to uninstall FMP instance.\r
+\r
+  @param[in]  Handle  The device handle to install a FMP instance on.\r
+\r
+  @retval  EFI_SUCCESS            FMP Installed\r
+  @retval  EFI_INVALID_PARAMETER  Handle was invalid\r
+  @retval  other                  Error installing FMP\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+UninstallFmpInstance (\r
+  IN EFI_HANDLE  Handle\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_FIRMWARE_MANAGEMENT_PROTOCOL  *Fmp;\r
+  FIRMWARE_MANAGEMENT_PRIVATE_DATA  *Private;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiFirmwareManagementProtocolGuid,\r
+                  (VOID **)&Fmp,\r
+                  NULL,\r
+                  NULL,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private = FIRMWARE_MANAGEMENT_PRIVATE_DATA_FROM_THIS (Fmp);\r
+  FmpDeviceSetContext (Private->Handle, &Private->FmpDeviceContext);\r
+\r
+  if (Private->FmpDeviceLockEvent != NULL) {\r
+    gBS->CloseEvent (Private->FmpDeviceLockEvent);\r
+  }\r
+\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  Private->Handle,\r
+                  &gEfiFirmwareManagementProtocolGuid, &Private->Fmp,\r
+                  &gEdkiiFirmwareManagementProgressProtocolGuid, &mFmpProgress,\r
+                  NULL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  FmpDeviceSetContext (NULL, &Private->FmpDeviceContext);\r
+\r
+  FreePool (Private);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Unloads the application and its installed protocol.\r
+\r
+  @param ImageHandle       Handle that identifies the image to be unloaded.\r
+  @param  SystemTable      The system table.\r
+\r
+  @retval EFI_SUCCESS      The image has been unloaded.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FmpDxeLibDestructor (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  if (mFmpSingleInstance) {\r
+    return UninstallFmpInstance (ImageHandle);\r
+  }\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Main entry for this driver/library.\r
 \r
@@ -1356,7 +1517,6 @@ FmpDxeEntryPoint (
   )\r
 {\r
   EFI_STATUS  Status;\r
-  EFI_GUID    *LockGuid;\r
 \r
   //\r
   // Verify that a new FILE_GUID value has been provided in the <Defines>\r
@@ -1381,49 +1541,28 @@ FmpDxeEntryPoint (
     ASSERT (FALSE);\r
   }\r
 \r
+\r
   //\r
   // Detects if PcdFmpDevicePkcs7CertBufferXdr contains a test key.\r
   //\r
   DetectTestKey ();\r
 \r
-  if (IsLockFmpDeviceAtLockEventGuidRequired ()) {\r
-    //\r
-    // Lock all UEFI Variables used by this module.\r
-    //\r
-    Status = LockAllFmpVariables ();\r
-    if (EFI_ERROR (Status)) {\r
-      DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variables.  Status = %r.\n", Status));\r
-    } else {\r
-      DEBUG ((DEBUG_INFO, "FmpDxe: All variables locked\n"));\r
-    }\r
-\r
-    //\r
-    // Register notify function to lock the FMP device.\r
-    // The lock event GUID is retrieved from PcdFmpDeviceLockEventGuid.\r
-    // If PcdFmpDeviceLockEventGuid is not the size of an EFI_GUID, then\r
-    // gEfiEndOfDxeEventGroupGuid is used.\r
-    //\r
-    LockGuid = &gEfiEndOfDxeEventGroupGuid;\r
-    if (PcdGetSize (PcdFmpDeviceLockEventGuid) == sizeof (EFI_GUID)) {\r
-      LockGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceLockEventGuid);\r
-    }\r
-    DEBUG ((DEBUG_INFO, "FmpDxe: Lock GUID: %g\n", LockGuid));\r
+  //\r
+  // Fill in FMP Progress Protocol fields for Version 1\r
+  //\r
+  mFmpProgress.Version                        = 1;\r
+  mFmpProgress.ProgressBarForegroundColor.Raw = PcdGet32 (PcdFmpDeviceProgressColor);\r
+  mFmpProgress.WatchdogSeconds                = PcdGet8 (PcdFmpDeviceProgressWatchdogTimeInSeconds);\r
 \r
-    Status = gBS->CreateEventEx (\r
-                    EVT_NOTIFY_SIGNAL,\r
-                    TPL_CALLBACK,\r
-                    FmpDxeLockEventNotify,\r
-                    NULL,\r
-                    LockGuid,\r
-                    &mFmpDeviceLockEvent\r
-                    );\r
-    if (EFI_ERROR (Status)) {\r
-      DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to register notification.  Status = %r\n", Status));\r
-    }\r
-    ASSERT_EFI_ERROR (Status);\r
-  } else {\r
-    DEBUG ((DEBUG_VERBOSE, "FmpDxe: Not registering notification to call FmpDeviceLock() because mfg mode\n"));\r
+  // The lock event GUID is retrieved from PcdFmpDeviceLockEventGuid.\r
+  // If PcdFmpDeviceLockEventGuid is not the size of an EFI_GUID, then\r
+  // gEfiEndOfDxeEventGroupGuid is used.\r
+  //\r
+  mLockGuid = &gEfiEndOfDxeEventGroupGuid;\r
+  if (PcdGetSize (PcdFmpDeviceLockEventGuid) == sizeof (EFI_GUID)) {\r
+    mLockGuid = (EFI_GUID *)PcdGetPtr (PcdFmpDeviceLockEventGuid);\r
   }\r
+  DEBUG ((DEBUG_INFO, "FmpDxe: Lock GUID: %g\n", mLockGuid));\r
 \r
   //\r
   // Register with library the install function so if the library uses\r
@@ -1433,8 +1572,15 @@ FmpDxeEntryPoint (
   //\r
   Status = RegisterFmpInstaller (InstallFmpInstance);\r
   if (Status == EFI_UNSUPPORTED) {\r
+    mFmpSingleInstance = TRUE;\r
     DEBUG ((DEBUG_INFO, "FmpDxe: FmpDeviceLib registration returned EFI_UNSUPPORTED.  Installing single FMP instance.\n"));\r
-    Status = InstallFmpInstance (ImageHandle);\r
+    Status = RegisterFmpUninstaller (UninstallFmpInstance);\r
+    if (Status == EFI_UNSUPPORTED) {\r
+      Status = InstallFmpInstance (ImageHandle);\r
+    } else {\r
+      DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib RegisterFmpInstaller and RegisterFmpUninstaller do not match.\n"));\r
+      Status = EFI_UNSUPPORTED;\r
+    }\r
   } else if (EFI_ERROR (Status)) {\r
     DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib registration returned %r.  No FMP installed.\n", Status));\r
   } else {\r
@@ -1442,6 +1588,10 @@ FmpDxeEntryPoint (
       DEBUG_INFO,\r
       "FmpDxe: FmpDeviceLib registration returned EFI_SUCCESS.  Expect FMP to be installed during the BDS/Device connection phase.\n"\r
       ));\r
+    Status = RegisterFmpUninstaller (UninstallFmpInstance);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "FmpDxe: FmpDeviceLib RegisterFmpInstaller and RegisterFmpUninstaller do not match.\n"));\r
+    }\r
   }\r
 \r
   return Status;\r