]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c
MdeModulePkg: Connect VariablePolicy business logic to VariableServices
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariablePolicySmmDxe.c
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c
new file mode 100644 (file)
index 0000000..6ae69df
--- /dev/null
@@ -0,0 +1,573 @@
+/** @file -- VariablePolicySmmDxe.c\r
+This protocol allows communication with Variable Policy Engine.\r
+\r
+Copyright (c) Microsoft Corporation.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/SafeIntLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+#include <Protocol/VariablePolicy.h>\r
+#include <Protocol/MmCommunication2.h>\r
+\r
+#include <Guid/VarCheckPolicyMmi.h>\r
+\r
+#include "Variable.h"\r
+\r
+EDKII_VARIABLE_POLICY_PROTOCOL  mVariablePolicyProtocol;\r
+EFI_MM_COMMUNICATION2_PROTOCOL  *mMmCommunication;\r
+\r
+VOID      *mMmCommunicationBuffer;\r
+UINTN     mMmCommunicationBufferSize;\r
+EFI_LOCK  mMmCommunicationLock;\r
+\r
+/**\r
+  Internal helper function to consolidate communication method.\r
+\r
+  @param[in,out]  CommBuffer\r
+  @param[in,out]  CommSize    Size of the CommBuffer.\r
+\r
+  @retval   EFI_STATUS    Result from communication method.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+InternalMmCommunicate (\r
+  IN OUT VOID             *CommBuffer,\r
+  IN OUT UINTN            *CommSize\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  if (CommBuffer == NULL || CommSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  Status = mMmCommunication->Communicate (mMmCommunication, CommBuffer, CommBuffer, CommSize);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This API function disables the variable policy enforcement. If it's\r
+  already been called once, will return EFI_ALREADY_STARTED.\r
+\r
+  @retval     EFI_SUCCESS\r
+  @retval     EFI_ALREADY_STARTED   Has already been called once this boot.\r
+  @retval     EFI_WRITE_PROTECTED   Interface has been locked until reboot.\r
+  @retval     EFI_WRITE_PROTECTED   Interface option is disabled by platform PCD.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProtocolDisableVariablePolicy (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_MM_COMMUNICATE_HEADER     *CommHeader;\r
+  VAR_CHECK_POLICY_COMM_HEADER  *PolicyHeader;\r
+  UINTN                         BufferSize;\r
+\r
+  // Check the PCD for convenience.\r
+  // This would also be rejected by the lib, but why go to MM if we don't have to?\r
+  if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // Set up the MM communication.\r
+  BufferSize    = mMmCommunicationBufferSize;\r
+  CommHeader    = mMmCommunicationBuffer;\r
+  PolicyHeader  = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;\r
+  CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );\r
+  CommHeader->MessageLength = BufferSize;\r
+  PolicyHeader->Signature   = VAR_CHECK_POLICY_COMM_SIG;\r
+  PolicyHeader->Revision    = VAR_CHECK_POLICY_COMM_REVISION;\r
+  PolicyHeader->Command     = VAR_CHECK_POLICY_COMMAND_DISABLE;\r
+\r
+  Status = InternalMmCommunicate (CommHeader, &BufferSize);\r
+  DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));\r
+\r
+  ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;\r
+}\r
+\r
+\r
+/**\r
+  This API function returns whether or not the policy engine is\r
+  currently being enforced.\r
+\r
+  @param[out]   State       Pointer to a return value for whether the policy enforcement\r
+                            is currently enabled.\r
+\r
+  @retval     EFI_SUCCESS\r
+  @retval     Others        An error has prevented this command from completing.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProtocolIsVariablePolicyEnabled (\r
+  OUT BOOLEAN     *State\r
+  )\r
+{\r
+  EFI_STATUS                                Status;\r
+  EFI_MM_COMMUNICATE_HEADER                 *CommHeader;\r
+  VAR_CHECK_POLICY_COMM_HEADER              *PolicyHeader;\r
+  VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS   *CommandParams;\r
+  UINTN                                     BufferSize;\r
+\r
+  if (State == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // Set up the MM communication.\r
+  BufferSize    = mMmCommunicationBufferSize;\r
+  CommHeader    = mMmCommunicationBuffer;\r
+  PolicyHeader  = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;\r
+  CommandParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)(PolicyHeader + 1);\r
+  CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );\r
+  CommHeader->MessageLength = BufferSize;\r
+  PolicyHeader->Signature   = VAR_CHECK_POLICY_COMM_SIG;\r
+  PolicyHeader->Revision    = VAR_CHECK_POLICY_COMM_REVISION;\r
+  PolicyHeader->Command     = VAR_CHECK_POLICY_COMMAND_IS_ENABLED;\r
+\r
+  Status = InternalMmCommunicate (CommHeader, &BufferSize);\r
+  DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));\r
+\r
+  if (!EFI_ERROR( Status )) {\r
+    Status = PolicyHeader->Result;\r
+    *State = CommandParams->State;\r
+  }\r
+\r
+  ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This API function validates and registers a new policy with\r
+  the policy enforcement engine.\r
+\r
+  @param[in]  NewPolicy     Pointer to the incoming policy structure.\r
+\r
+  @retval     EFI_SUCCESS\r
+  @retval     EFI_INVALID_PARAMETER   NewPolicy is NULL or is internally inconsistent.\r
+  @retval     EFI_ALREADY_STARTED     An identical matching policy already exists.\r
+  @retval     EFI_WRITE_PROTECTED     The interface has been locked until the next reboot.\r
+  @retval     EFI_UNSUPPORTED         Policy enforcement has been disabled. No reason to add more policies.\r
+  @retval     EFI_ABORTED             A calculation error has prevented this function from completing.\r
+  @retval     EFI_OUT_OF_RESOURCES    Cannot grow the table to hold any more policies.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProtocolRegisterVariablePolicy (\r
+  IN CONST VARIABLE_POLICY_ENTRY    *NewPolicy\r
+  )\r
+{\r
+  EFI_STATUS                                Status;\r
+  EFI_MM_COMMUNICATE_HEADER                 *CommHeader;\r
+  VAR_CHECK_POLICY_COMM_HEADER              *PolicyHeader;\r
+  VOID                                      *PolicyBuffer;\r
+  UINTN                                     BufferSize;\r
+  UINTN                                     RequiredSize;\r
+\r
+  if (NewPolicy == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  // First, make sure that the required size does not exceed the capabilities\r
+  // of the MmCommunication buffer.\r
+  RequiredSize = OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + sizeof(VAR_CHECK_POLICY_COMM_HEADER);\r
+  Status = SafeUintnAdd( RequiredSize, NewPolicy->Size, &RequiredSize );\r
+  if (EFI_ERROR( Status ) || RequiredSize > mMmCommunicationBufferSize) {\r
+    DEBUG(( DEBUG_ERROR, "%a - Policy too large for buffer! %r, %d > %d \n", __FUNCTION__,\r
+            Status, RequiredSize, mMmCommunicationBufferSize ));\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // Set up the MM communication.\r
+  BufferSize    = mMmCommunicationBufferSize;\r
+  CommHeader    = mMmCommunicationBuffer;\r
+  PolicyHeader  = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;\r
+  PolicyBuffer  = (VOID*)(PolicyHeader + 1);\r
+  CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );\r
+  CommHeader->MessageLength = BufferSize;\r
+  PolicyHeader->Signature   = VAR_CHECK_POLICY_COMM_SIG;\r
+  PolicyHeader->Revision    = VAR_CHECK_POLICY_COMM_REVISION;\r
+  PolicyHeader->Command     = VAR_CHECK_POLICY_COMMAND_REGISTER;\r
+\r
+  // Copy the policy into place. This copy is safe because we've already tested above.\r
+  CopyMem( PolicyBuffer, NewPolicy, NewPolicy->Size );\r
+\r
+  Status = InternalMmCommunicate (CommHeader, &BufferSize);\r
+  DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));\r
+\r
+  ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;\r
+}\r
+\r
+\r
+/**\r
+  This helper function takes care of the overhead of formatting, sending, and interpreting\r
+  the results for a single DumpVariablePolicy request.\r
+\r
+  @param[in]      PageRequested   The page of the paginated results from MM. 0 for metadata.\r
+  @param[out]     TotalSize       The total size of the entire buffer. Returned as part of metadata.\r
+  @param[out]     PageSize        The size of the current page being returned. Not valid as part of metadata.\r
+  @param[out]     HasMore         A flag indicating whether there are more pages after this one.\r
+  @param[out]     Buffer          The start of the current page from MM.\r
+\r
+  @retval     EFI_SUCCESS             Output params have been updated (either metadata or dump page).\r
+  @retval     EFI_INVALID_PARAMETER   One of the output params is NULL.\r
+  @retval     Others                  Response from MM handler.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DumpVariablePolicyHelper (\r
+  IN  UINT32        PageRequested,\r
+  OUT UINT32        *TotalSize,\r
+  OUT UINT32        *PageSize,\r
+  OUT BOOLEAN       *HasMore,\r
+  OUT UINT8         **Buffer\r
+  )\r
+{\r
+  EFI_STATUS                              Status;\r
+  EFI_MM_COMMUNICATE_HEADER               *CommHeader;\r
+  VAR_CHECK_POLICY_COMM_HEADER            *PolicyHeader;\r
+  VAR_CHECK_POLICY_COMM_DUMP_PARAMS       *CommandParams;\r
+  UINTN                                   BufferSize;\r
+\r
+  if (TotalSize == NULL || PageSize == NULL || HasMore == NULL || Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  // Set up the MM communication.\r
+  BufferSize    = mMmCommunicationBufferSize;\r
+  CommHeader    = mMmCommunicationBuffer;\r
+  PolicyHeader  = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;\r
+  CommandParams = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(PolicyHeader + 1);\r
+  CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );\r
+  CommHeader->MessageLength = BufferSize;\r
+  PolicyHeader->Signature   = VAR_CHECK_POLICY_COMM_SIG;\r
+  PolicyHeader->Revision    = VAR_CHECK_POLICY_COMM_REVISION;\r
+  PolicyHeader->Command     = VAR_CHECK_POLICY_COMMAND_DUMP;\r
+\r
+  CommandParams->PageRequested = PageRequested;\r
+\r
+  Status = InternalMmCommunicate (CommHeader, &BufferSize);\r
+  DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));\r
+\r
+  if (!EFI_ERROR( Status )) {\r
+    Status = PolicyHeader->Result;\r
+    *TotalSize = CommandParams->TotalSize;\r
+    *PageSize = CommandParams->PageSize;\r
+    *HasMore = CommandParams->HasMore;\r
+    *Buffer = (UINT8*)(CommandParams + 1);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This API function will dump the entire contents of the variable policy table.\r
+\r
+  Similar to GetVariable, the first call can be made with a 0 size and it will return\r
+  the size of the buffer required to hold the entire table.\r
+\r
+  @param[out]     Policy  Pointer to the policy buffer. Can be NULL if Size is 0.\r
+  @param[in,out]  Size    On input, the size of the output buffer. On output, the size\r
+                          of the data returned.\r
+\r
+  @retval     EFI_SUCCESS             Policy data is in the output buffer and Size has been updated.\r
+  @retval     EFI_INVALID_PARAMETER   Size is NULL, or Size is non-zero and Policy is NULL.\r
+  @retval     EFI_BUFFER_TOO_SMALL    Size is insufficient to hold policy. Size updated with required size.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProtocolDumpVariablePolicy (\r
+  OUT UINT8             *Policy OPTIONAL,\r
+  IN OUT UINT32         *Size\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+  UINT8         *Source;\r
+  UINT8         *Destination;\r
+  UINT32        PolicySize;\r
+  UINT32        PageSize;\r
+  BOOLEAN       HasMore;\r
+  UINT32        PageIndex;\r
+\r
+  if (Size == NULL || (*Size > 0 && Policy == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // Repeat this whole process until we either have a failure case or get the entire buffer.\r
+  do {\r
+    // First, we must check the zero page to determine the buffer size and\r
+    // reset the internal state.\r
+    PolicySize = 0;\r
+    PageSize = 0;\r
+    HasMore = FALSE;\r
+    Status = DumpVariablePolicyHelper (0, &PolicySize, &PageSize, &HasMore, &Source);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    // If we're good, we can at least check the required size now.\r
+    if (*Size < PolicySize) {\r
+      *Size = PolicySize;\r
+      Status = EFI_BUFFER_TOO_SMALL;\r
+      break;\r
+    }\r
+\r
+    // On further thought, let's update the size either way.\r
+    *Size = PolicySize;\r
+    // And get ready to ROCK.\r
+    Destination = Policy;\r
+\r
+    // Keep looping and copying until we're either done or freak out.\r
+    for (PageIndex = 1; !EFI_ERROR (Status) && HasMore && PageIndex < MAX_UINT32; PageIndex++) {\r
+      Status = DumpVariablePolicyHelper (PageIndex, &PolicySize, &PageSize, &HasMore, &Source);\r
+      if (!EFI_ERROR (Status)) {\r
+        CopyMem (Destination, Source, PageSize);\r
+        Destination += PageSize;\r
+      }\r
+    }\r
+\r
+    // Next, we check to see whether\r
+  } while (Status == EFI_TIMEOUT);\r
+\r
+  ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // There's currently no use for this, but it shouldn't be hard to implement.\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This API function locks the interface so that no more policy updates\r
+  can be performed or changes made to the enforcement until the next boot.\r
+\r
+  @retval     EFI_SUCCESS\r
+  @retval     Others        An error has prevented this command from completing.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ProtocolLockVariablePolicy (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_MM_COMMUNICATE_HEADER     *CommHeader;\r
+  VAR_CHECK_POLICY_COMM_HEADER  *PolicyHeader;\r
+  UINTN                         BufferSize;\r
+\r
+  AcquireLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  // Set up the MM communication.\r
+  BufferSize    = mMmCommunicationBufferSize;\r
+  CommHeader    = mMmCommunicationBuffer;\r
+  PolicyHeader  = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;\r
+  CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );\r
+  CommHeader->MessageLength = BufferSize;\r
+  PolicyHeader->Signature   = VAR_CHECK_POLICY_COMM_SIG;\r
+  PolicyHeader->Revision    = VAR_CHECK_POLICY_COMM_REVISION;\r
+  PolicyHeader->Command     = VAR_CHECK_POLICY_COMMAND_LOCK;\r
+\r
+  Status = InternalMmCommunicate (CommHeader, &BufferSize);\r
+  DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));\r
+\r
+  ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);\r
+\r
+  return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;\r
+}\r
+\r
+\r
+/**\r
+  This helper function locates the shared comm buffer and assigns it to input pointers.\r
+\r
+  @param[in,out]  BufferSize      On input, the minimum buffer size required INCLUDING the MM communicate header.\r
+                                  On output, the size of the matching buffer found.\r
+  @param[out]     LocatedBuffer   A pointer to the matching buffer.\r
+\r
+  @retval     EFI_SUCCESS\r
+  @retval     EFI_INVALID_PARAMETER   One of the output pointers was NULL.\r
+  @retval     EFI_OUT_OF_RESOURCES    Not enough memory to allocate a comm buffer.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+InitMmCommonCommBuffer (\r
+  IN OUT  UINTN       *BufferSize,\r
+  OUT     VOID        **LocatedBuffer\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  // Make sure that we're working with good pointers.\r
+  if (BufferSize == NULL || LocatedBuffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  // Allocate the runtime memory for the comm buffer.\r
+  *LocatedBuffer = AllocateRuntimePool (*BufferSize);\r
+  if (*LocatedBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    *BufferSize = 0;\r
+  }\r
+\r
+  EfiInitializeLock (&mMmCommunicationLock, TPL_NOTIFY);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Convert internal pointer addresses to virtual addresses.\r
+\r
+  @param[in] Event      Event whose notification function is being invoked.\r
+  @param[in] Context    The pointer to the notification function's context, which\r
+                        is implementation-dependent.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VariablePolicyVirtualAddressCallback (\r
+  IN  EFI_EVENT   Event,\r
+  IN  VOID        *Context\r
+  )\r
+{\r
+  EfiConvertPointer (0, (VOID **)&mMmCommunication);\r
+  EfiConvertPointer (0, (VOID **)&mMmCommunicationBuffer);\r
+}\r
+\r
+\r
+/**\r
+  The driver's entry point.\r
+\r
+  @param[in] ImageHandle  The firmware allocated handle for the EFI image.\r
+  @param[in] SystemTable  A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS     The entry point executed successfully.\r
+  @retval other           Some error occured when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VariablePolicySmmDxeMain (\r
+  IN    EFI_HANDLE                  ImageHandle,\r
+  IN    EFI_SYSTEM_TABLE            *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  BOOLEAN                 ProtocolInstalled;\r
+  BOOLEAN                 VirtualAddressChangeRegistered;\r
+  EFI_EVENT               VirtualAddressChangeEvent;\r
+\r
+  Status = EFI_SUCCESS;\r
+  ProtocolInstalled = FALSE;\r
+  VirtualAddressChangeRegistered = FALSE;\r
+\r
+  // Update the minimum buffer size.\r
+  mMmCommunicationBufferSize = VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE;\r
+  // Locate the shared comm buffer to use for sending MM commands.\r
+  Status = InitMmCommonCommBuffer( &mMmCommunicationBufferSize, &mMmCommunicationBuffer );\r
+  if (EFI_ERROR( Status )) {\r
+    DEBUG((DEBUG_ERROR, "%a - Failed to locate a viable MM comm buffer! %r\n", __FUNCTION__, Status));\r
+    ASSERT_EFI_ERROR( Status );\r
+    return Status;\r
+  }\r
+\r
+  // Locate the MmCommunication protocol.\r
+  Status = gBS->LocateProtocol( &gEfiMmCommunication2ProtocolGuid, NULL, (VOID**)&mMmCommunication );\r
+  if (EFI_ERROR( Status )) {\r
+    DEBUG((DEBUG_ERROR, "%a - Failed to locate MmCommunication protocol! %r\n", __FUNCTION__, Status));\r
+    ASSERT_EFI_ERROR( Status );\r
+    return Status;\r
+  }\r
+\r
+  // Configure the VariablePolicy protocol structure.\r
+  mVariablePolicyProtocol.Revision                = EDKII_VARIABLE_POLICY_PROTOCOL_REVISION;\r
+  mVariablePolicyProtocol.DisableVariablePolicy   = ProtocolDisableVariablePolicy;\r
+  mVariablePolicyProtocol.IsVariablePolicyEnabled = ProtocolIsVariablePolicyEnabled;\r
+  mVariablePolicyProtocol.RegisterVariablePolicy  = ProtocolRegisterVariablePolicy;\r
+  mVariablePolicyProtocol.DumpVariablePolicy      = ProtocolDumpVariablePolicy;\r
+  mVariablePolicyProtocol.LockVariablePolicy      = ProtocolLockVariablePolicy;\r
+\r
+  // Register all the protocols and return the status.\r
+  Status = gBS->InstallMultipleProtocolInterfaces( &ImageHandle,\r
+                                                   &gEdkiiVariablePolicyProtocolGuid, &mVariablePolicyProtocol,\r
+                                                   NULL );\r
+  if (EFI_ERROR( Status )) {\r
+    DEBUG(( DEBUG_ERROR, "%a - Failed to install protocol! %r\n", __FUNCTION__, Status ));\r
+    goto Exit;\r
+  }\r
+  else {\r
+    ProtocolInstalled = TRUE;\r
+  }\r
+\r
+  // Normally, we might want to register a callback\r
+  // to lock the interface, but this is integrated\r
+  // into the existing callbacks in VaraiableSmm.c\r
+  // and VariableDxe.c.\r
+\r
+  //\r
+  // Register a VirtualAddressChange callback for the MmComm protocol and Comm buffer.\r
+  Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,\r
+                                TPL_NOTIFY,\r
+                                VariablePolicyVirtualAddressCallback,\r
+                                NULL,\r
+                                &gEfiEventVirtualAddressChangeGuid,\r
+                                &VirtualAddressChangeEvent);\r
+  if (EFI_ERROR( Status )) {\r
+    DEBUG(( DEBUG_ERROR, "%a - Failed to create VirtualAddressChange event! %r\n", __FUNCTION__, Status ));\r
+    goto Exit;\r
+  }\r
+  else {\r
+    VirtualAddressChangeRegistered = TRUE;\r
+  }\r
+\r
+\r
+Exit:\r
+  //\r
+  // If we're about to return a failed status (and unload this driver), we must first undo anything that\r
+  // has been successfully done.\r
+  if (EFI_ERROR( Status )) {\r
+    if (ProtocolInstalled) {\r
+      gBS->UninstallProtocolInterface( &ImageHandle, &gEdkiiVariablePolicyProtocolGuid, &mVariablePolicyProtocol );\r
+    }\r
+    if (VirtualAddressChangeRegistered) {\r
+      gBS->CloseEvent( VirtualAddressChangeEvent );\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r