/** @file
  Implementation of the shared functions to do the platform driver vverride mapping.

  Copyright (c) 2007 - 2009, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "InternalPlatDriOverrideDxe.h"

#define PLATFORM_OVERRIDE_ITEM_SIGNATURE      SIGNATURE_32('p','d','o','i')
 typedef struct _PLATFORM_OVERRIDE_ITEM {
  UINTN                                 Signature;
  LIST_ENTRY                            Link;
  UINT32                                DriverInfoNum;
  EFI_DEVICE_PATH_PROTOCOL              *ControllerDevicePath;
  ///
  /// List of DRIVER_IMAGE_INFO
  ///
  LIST_ENTRY                            DriverInfoList;
  EFI_HANDLE                            LastReturnedImageHandle;
} PLATFORM_OVERRIDE_ITEM;

#define DRIVER_IMAGE_INFO_SIGNATURE           SIGNATURE_32('p','d','i','i')
typedef struct _DRIVER_IMAGE_INFO {
  UINTN                                 Signature;
  LIST_ENTRY                            Link;
  EFI_HANDLE                            ImageHandle;
  EFI_DEVICE_PATH_PROTOCOL              *DriverImagePath;
  BOOLEAN                               UnLoadable;
  BOOLEAN                               UnStartable;
} DRIVER_IMAGE_INFO;

#define DEVICE_PATH_STACK_ITEM_SIGNATURE      SIGNATURE_32('d','p','s','i')
typedef struct _DEVICE_PATH_STACK_ITEM{
  UINTN                                 Signature;
  LIST_ENTRY                            Link;
  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
} DEVICE_PATH_STACK_ITEM;


LIST_ENTRY   mDevicePathStack = INITIALIZE_LIST_HEAD_VARIABLE (mDevicePathStack);

/**
  Push a controller device path into a globle device path list.

  @param  DevicePath     The controller device path to push into stack

  @retval EFI_SUCCESS    Device path successfully pushed into the stack.

**/
EFI_STATUS
EFIAPI
PushDevPathStack (
  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath
  )
{
  DEVICE_PATH_STACK_ITEM  *DevicePathStackItem;

  DevicePathStackItem = AllocateZeroPool (sizeof (DEVICE_PATH_STACK_ITEM));
  ASSERT (DevicePathStackItem != NULL);
  DevicePathStackItem->Signature = DEVICE_PATH_STACK_ITEM_SIGNATURE;
  DevicePathStackItem->DevicePath = DuplicateDevicePath (DevicePath);
  InsertTailList (&mDevicePathStack, &DevicePathStackItem->Link);
  return EFI_SUCCESS;
}


/**
  Pop a controller device path from a globle device path list

  @param  DevicePath     The controller device path popped from stack

  @retval EFI_SUCCESS    Controller device path successfully popped.
  @retval EFI_NOT_FOUND  Stack is empty.

**/
EFI_STATUS
EFIAPI
PopDevPathStack (
  OUT  EFI_DEVICE_PATH_PROTOCOL    **DevicePath
  )
{
  DEVICE_PATH_STACK_ITEM  *DevicePathStackItem;
  LIST_ENTRY              *ItemListIndex;

  ItemListIndex = mDevicePathStack.BackLink;
  //
  // Check if the stack is empty
  //
  if (ItemListIndex != &mDevicePathStack){
    DevicePathStackItem = CR(ItemListIndex, DEVICE_PATH_STACK_ITEM, Link, DEVICE_PATH_STACK_ITEM_SIGNATURE);
    if (DevicePath != NULL) {
      *DevicePath = DuplicateDevicePath (DevicePathStackItem->DevicePath);
    }
    FreePool (DevicePathStackItem->DevicePath);
    RemoveEntryList (&DevicePathStackItem->Link);
    FreePool (DevicePathStackItem);
    return EFI_SUCCESS;
  }
  return EFI_NOT_FOUND;
}


/**
  Check whether a controller device path is in a globle device path list

  @param  DevicePath     The controller device path to check

  @retval TRUE           DevicePath exists in the stack.
  @retval FALSE          DevicePath does not exist in the stack.

**/
BOOLEAN
EFIAPI
CheckExistInStack (
  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath
  )
{
  DEVICE_PATH_STACK_ITEM  *DevicePathStackItem;
  LIST_ENTRY              *ItemListIndex;
  UINTN                   DevicePathSize;

  ItemListIndex = mDevicePathStack.BackLink;
  while (ItemListIndex != &mDevicePathStack){
    DevicePathStackItem = CR(ItemListIndex, DEVICE_PATH_STACK_ITEM, Link, DEVICE_PATH_STACK_ITEM_SIGNATURE);
    DevicePathSize = GetDevicePathSize (DevicePath);
    if (DevicePathSize == GetDevicePathSize (DevicePathStackItem->DevicePath)) {
      if (CompareMem (DevicePath, DevicePathStackItem->DevicePath, DevicePathSize) == 0) {
        return TRUE;
      }
    }
    ItemListIndex = ItemListIndex->BackLink;
  }

  return FALSE;
}

/**
  Update the FV file device path if it is not valid.

  According to a file GUID, check a Fv file device path is valid. If it is invalid,
  try to return the valid device path.
  FV address maybe changes for memory layout adjust from time to time, use this funciton
  could promise the Fv file device path is right.

  @param  DevicePath               On input, the FV file device path to check
                                   On output, the updated valid FV file device path
  @param  FileGuid                 The FV file GUID
  @param  CallerImageHandle        Image handle of the caller

  @retval EFI_INVALID_PARAMETER    the input DevicePath or FileGuid is invalid
                                   parameter
  @retval EFI_UNSUPPORTED          the input DevicePath does not contain FV file
                                   GUID at all
  @retval EFI_ALREADY_STARTED      the input DevicePath has pointed to FV file, it
                                   is valid
  @retval EFI_SUCCESS              Successfully updated the invalid DevicePath,
                                   and return the updated device path in DevicePath

**/
EFI_STATUS
EFIAPI
UpdateFvFileDevicePath (
  IN  OUT EFI_DEVICE_PATH_PROTOCOL      **DevicePath,
  IN  EFI_GUID                          *FileGuid,
  IN  EFI_HANDLE                        CallerImageHandle
  )
{
  EFI_DEVICE_PATH_PROTOCOL      *TempDevicePath;
  EFI_DEVICE_PATH_PROTOCOL      *LastDeviceNode;
  EFI_STATUS                    Status;
  EFI_GUID                      *GuidPoint;
  UINTN                         Index;
  UINTN                         FvHandleCount;
  EFI_HANDLE                    *FvHandleBuffer;
  EFI_FV_FILETYPE               Type;
  UINTN                         Size;
  EFI_FV_FILE_ATTRIBUTES        Attributes;
  UINT32                        AuthenticationStatus;
  BOOLEAN                       FindFvFile;
  EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
  EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode;
  EFI_HANDLE                    FoundFvHandle;
  EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;
  BOOLEAN                       HasFvNode;

  if (DevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (*DevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check whether the device path points to the default the input FV file
  //
  TempDevicePath = *DevicePath;
  LastDeviceNode = TempDevicePath;
  while (!IsDevicePathEnd (TempDevicePath)) {
     LastDeviceNode = TempDevicePath;
     TempDevicePath = NextDevicePathNode (TempDevicePath);
  }
  GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode);
  if (GuidPoint == NULL) {
    //
    // If this option does not point to a FV file, just return EFI_UNSUPPORTED.
    //
    return EFI_UNSUPPORTED;
  }

  if (FileGuid != NULL) {
    if (!CompareGuid (GuidPoint, FileGuid)) {
      //
      // If the FV file is not the input file GUID, just return EFI_UNSUPPORTED
      //
      return EFI_UNSUPPORTED;
    }
  } else {
    FileGuid = GuidPoint;
  }

  //
  // Check to see if the device path contains memory map node
  //
  TempDevicePath = *DevicePath;
  HasFvNode = FALSE;
  while (!IsDevicePathEnd (TempDevicePath)) {
    //
    // Use old Device Path
    //
    if (DevicePathType (TempDevicePath) == HARDWARE_DEVICE_PATH &&
        DevicePathSubType (TempDevicePath) == HW_MEMMAP_DP) {
      HasFvNode = TRUE;
      break;
    }
    TempDevicePath = NextDevicePathNode (TempDevicePath);
  }

  if (!HasFvNode) {
    return EFI_UNSUPPORTED;
  }

  //
  // Check whether the input Fv file device path is valid
  //
  TempDevicePath = *DevicePath;
  FoundFvHandle = NULL;
  Status = gBS->LocateDevicePath (
                  &gEfiFirmwareVolume2ProtocolGuid,
                  &TempDevicePath,
                  &FoundFvHandle
                  );
  if (!EFI_ERROR (Status)) {
    Status = gBS->HandleProtocol (
                    FoundFvHandle,
                    &gEfiFirmwareVolume2ProtocolGuid,
                    (VOID **) &Fv
                    );
    if (!EFI_ERROR (Status)) {
      //
      // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there
      //
      Status = Fv->ReadFile (
                     Fv,
                     FileGuid,
                     NULL,
                     &Size,
                     &Type,
                     &Attributes,
                     &AuthenticationStatus
                     );
      if (!EFI_ERROR (Status)) {
        return EFI_ALREADY_STARTED;
      }
    }
  }

  //
  // Look for the input wanted FV file in current FV
  // First, try to look for in Caller own FV. Caller and input wanted FV file usually are in the same FV
  //
  FindFvFile = FALSE;
  FoundFvHandle = NULL;
  Status = gBS->HandleProtocol (
                  CallerImageHandle,
                  &gEfiLoadedImageProtocolGuid,
                  (VOID **) &LoadedImage
                  );
  if (!EFI_ERROR (Status)) {
    Status = gBS->HandleProtocol (
                    LoadedImage->DeviceHandle,
                    &gEfiFirmwareVolume2ProtocolGuid,
                    (VOID **) &Fv
                    );
    if (!EFI_ERROR (Status)) {
      Status = Fv->ReadFile (
                     Fv,
                     FileGuid,
                     NULL,
                     &Size,
                     &Type,
                     &Attributes,
                     &AuthenticationStatus
                     );
      if (!EFI_ERROR (Status)) {
        FindFvFile = TRUE;
        FoundFvHandle = LoadedImage->DeviceHandle;
      }
    }
  }
  //
  // Second, if fail to find, try to enumerate all FV
  //
  if (!FindFvFile) {
    gBS->LocateHandleBuffer (
           ByProtocol,
           &gEfiFirmwareVolume2ProtocolGuid,
           NULL,
           &FvHandleCount,
           &FvHandleBuffer
           );
    for (Index = 0; Index < FvHandleCount; Index++) {
      gBS->HandleProtocol (
             FvHandleBuffer[Index],
             &gEfiFirmwareVolume2ProtocolGuid,
             (VOID **) &Fv
             );

      Status = Fv->ReadFile (
                     Fv,
                     FileGuid,
                     NULL,
                     &Size,
                     &Type,
                     &Attributes,
                     &AuthenticationStatus
                     );
      if (EFI_ERROR (Status)) {
        //
        // Skip if input Fv file not in the FV
        //
        continue;
      }
      FindFvFile = TRUE;
      FoundFvHandle = FvHandleBuffer[Index];
      break;
    }
  }

  if (FindFvFile) {
    //
    // Build the shell device path
    //
    NewDevicePath = DevicePathFromHandle (FoundFvHandle);
    EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid);
    NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode);
    *DevicePath = NewDevicePath;
    return EFI_SUCCESS;
  }
  return EFI_NOT_FOUND;
}

/**
  Gets the data and size of a variable.

  Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
  buffer, and the size of the buffer. If failure return NULL.

  @param  Name                     String part of EFI variable name
  @param  VendorGuid               GUID part of EFI variable name
  @param  VariableSize             Returns the size of the EFI variable that was
                                   read

  @return Dynamically allocated memory that contains a copy of the EFI variable.
          Caller is responsible freeing the buffer.
  @retval NULL                     Variable was not read

**/
VOID *
EFIAPI
GetVariableAndSize (
  IN  CHAR16              *Name,
  IN  EFI_GUID            *VendorGuid,
  OUT UINTN               *VariableSize
  )
{
  EFI_STATUS  Status;
  UINTN       BufferSize;
  VOID        *Buffer;

  Buffer = NULL;

  //
  // Pass in a zero size buffer to find the required buffer size.
  //
  BufferSize  = 0;
  Status      = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
  if (Status == EFI_BUFFER_TOO_SMALL) {
    //
    // Allocate the buffer to return
    //
    Buffer = AllocateZeroPool (BufferSize);
    if (Buffer == NULL) {
      return NULL;
    }
    //
    // Read variable into the allocated buffer.
    //
    Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
    if (EFI_ERROR (Status)) {
      BufferSize = 0;
    }
  }

  *VariableSize = BufferSize;
  return Buffer;
}

/**
  Connect to the handle to a device on the device path.

  This function will create all handles associate with every device
  path node. If the handle associate with one device path node can not
  be created success, then still give one chance to do the dispatch,
  which load the missing drivers if possible.

  @param  DevicePathToConnect      The device path which will be connected, it can
                                   be a multi-instance device path

  @retval EFI_SUCCESS              All handles associate with every device path
                                   node have been created
  @retval EFI_OUT_OF_RESOURCES     There is no resource to create new handles
  @retval EFI_NOT_FOUND            Create the handle associate with one device
                                   path node failed

**/
EFI_STATUS
EFIAPI
ConnectDevicePath (
  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePathToConnect
  )
{
  EFI_STATUS                Status;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
  EFI_DEVICE_PATH_PROTOCOL  *CopyOfDevicePath;
  EFI_DEVICE_PATH_PROTOCOL  *Instance;
  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;
  EFI_DEVICE_PATH_PROTOCOL  *Next;
  EFI_HANDLE                Handle;
  EFI_HANDLE                PreviousHandle;
  UINTN                     Size;

  if (DevicePathToConnect == NULL) {
    return EFI_SUCCESS;
  }

  DevicePath        = DuplicateDevicePath (DevicePathToConnect);
  CopyOfDevicePath  = DevicePath;
  if (DevicePath == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  do {
    //
    // The outer loop handles multi instance device paths.
    // Only console variables contain multiple instance device paths.
    //
    // After this call DevicePath points to the next Instance
    //
    Instance  = GetNextDevicePathInstance (&DevicePath, &Size);
    ASSERT (Instance != NULL);

    Next      = Instance;
    while (!IsDevicePathEndType (Next)) {
      Next = NextDevicePathNode (Next);
    }

    SetDevicePathEndNode (Next);

    //
    // Start the real work of connect with RemainingDevicePath
    //
    PreviousHandle = NULL;
    do {
      //
      // Find the handle that best matches the Device Path. If it is only a
      // partial match the remaining part of the device path is returned in
      // RemainingDevicePath.
      //
      RemainingDevicePath = Instance;
      Status              = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &Handle);

      if (!EFI_ERROR (Status)) {
        if (Handle == PreviousHandle) {
          //
          // If no forward progress is made try invoking the Dispatcher.
          // A new FV may have been added to the system an new drivers
          // may now be found.
          // Status == EFI_SUCCESS means a driver was dispatched
          // Status == EFI_NOT_FOUND means no new drivers were dispatched
          //
          Status = gDS->Dispatch ();
        }

        if (!EFI_ERROR (Status)) {
          PreviousHandle = Handle;
          //
          // Connect all drivers that apply to Handle and RemainingDevicePath,
          // the Recursive flag is FALSE so only one level will be expanded.
          //
          // Do not check the connect status here, if the connect controller fail,
          // then still give the chance to do dispatch, because partial
          // RemainingDevicepath may be in the new FV
          //
          // 1. If the connect fails, RemainingDevicepath and handle will not
          //    change, so next time will do the dispatch, then dispatch's status
          //    will take effect
          // 2. If the connect succeeds, the RemainingDevicepath and handle will
          //    change, then avoid the dispatch, we have chance to continue the
          //    next connection
          //
          gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE);
        }
      }
      //
      // Loop until RemainingDevicePath is an empty device path
      //
    } while (!EFI_ERROR (Status) && !IsDevicePathEnd (RemainingDevicePath));

  } while (DevicePath != NULL);

  if (CopyOfDevicePath != NULL) {
    FreePool (CopyOfDevicePath);
  }
  //
  // All handle with DevicePath exists in the handle database
  //
  return Status;
}

/**
  Free all the mapping database memory resource and initialize the mapping list entry.

  @param  MappingDataBase          Mapping database list entry pointer

  @retval EFI_SUCCESS              Mapping database successfully freed
  @retval EFI_INVALID_PARAMETER    MappingDataBase is NULL

**/
EFI_STATUS
EFIAPI
FreeMappingDatabase (
  IN  OUT  LIST_ENTRY            *MappingDataBase
  )
{
  LIST_ENTRY                  *OverrideItemListIndex;
  LIST_ENTRY                  *ImageInfoListIndex;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  DRIVER_IMAGE_INFO           *DriverImageInfo;

  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
    //
    // Free PLATFORM_OVERRIDE_ITEM.ControllerDevicePath[]
    //
    if (OverrideItem->ControllerDevicePath != NULL){
      FreePool (OverrideItem->ControllerDevicePath);
    }

    ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
    while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
      //
      // Free DRIVER_IMAGE_INFO.DriverImagePath[]
      //
      DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
      if (DriverImageInfo->DriverImagePath != NULL) {
        FreePool(DriverImageInfo->DriverImagePath);
      }
      //
      // Free DRIVER_IMAGE_INFO itself
      //
      ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
      RemoveEntryList (&DriverImageInfo->Link);
      FreePool (DriverImageInfo);
    }
    //
    // Free PLATFORM_OVERRIDE_ITEM itself
    //
    OverrideItemListIndex = GetNextNode (MappingDataBase, OverrideItemListIndex);
    RemoveEntryList (&OverrideItem->Link);
    FreePool (OverrideItem);
  }

  InitializeListHead (MappingDataBase);
  return EFI_SUCCESS;
}


/**
  Create the mapping database according to variable.

  Read the environment variable(s) that contain the override mappings from Controller Device Path to
  a set of Driver Device Paths, and create the mapping database in memory with those variable info.
  VariableLayout{
  //
  // NotEnd indicate whether the variable is the last one, and has no subsequent variable need to load.
  // Each variable has MaximumVariableSize limitation, so we maybe need multiple variables to store
  // large mapping infos.
  // The variable(s) name rule is PlatDriOver, PlatDriOver1, PlatDriOver2, ....
  //
  UINT32                         NotEnd;               //Zero is the last one.
  //
  // The entry which contains the mapping that Controller Device Path to a set of Driver Device Paths
  // There are often multi mapping entries in a variable.
  //
  UINT32                         SIGNATURE;            //SIGNATURE_32('p','d','o','i')
  UINT32                         DriverNum;
  EFI_DEVICE_PATH_PROTOCOL       ControllerDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  ......
  UINT32                         NotEnd;                //Zero is the last one.
  UINT32                         SIGNATURE;
  UINT32                         DriverNum;
  EFI_DEVICE_PATH_PROTOCOL       ControllerDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  EFI_DEVICE_PATH_PROTOCOL       DriverDevicePath[];
  ......
  }

  @param  MappingDataBase          Mapping database list entry pointer

  @retval EFI_SUCCESS              Create the mapping database in memory successfully
  @retval EFI_INVALID_PARAMETER    MappingDataBase pointer is null
  @retval EFI_NOT_FOUND            Cannot find the 'PlatDriOver' NV variable
  @retval EFI_VOLUME_CORRUPTED     The found NV variable is corrupted

**/
EFI_STATUS
EFIAPI
InitOverridesMapping (
  OUT  LIST_ENTRY            *MappingDataBase
  )
{
  UINTN                       BufferSize;
  VOID                        *VariableBuffer;
  UINT8                       *VariableIndex;
  UINTN                       VariableNum;
  CHAR16                      OverrideVariableName[40];
  UINT32                      NotEnd;
  UINT32                      DriverNumber;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  BOOLEAN                     Corrupted;
  UINT32                      Signature;
  EFI_DEVICE_PATH_PROTOCOL    *ControllerDevicePath;
  EFI_DEVICE_PATH_PROTOCOL    *DriverDevicePath;
  UINTN                       Index;

  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check the environment variable(s) that contain the override mappings .
  //
  VariableBuffer = GetVariableAndSize (L"PlatDriOver", &gEfiCallerIdGuid, &BufferSize);
  ASSERT ((UINTN) VariableBuffer % sizeof(UINTN) == 0);
  if (VariableBuffer == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Traverse all variables.
  //
  VariableNum = 1;
  Corrupted = FALSE;
  do {
    VariableIndex = VariableBuffer;
    //
    // End flag
    //
    NotEnd = *(UINT32*) VariableIndex;
    //
    // Traverse the entries containing the mapping that Controller Device Path
    // to a set of Driver Device Paths within this variable.
    //
    VariableIndex = VariableIndex + sizeof (UINT32);
    while (VariableIndex < ((UINT8 *)VariableBuffer + BufferSize)) {
      //
      // Check signature of this entry
      //
      Signature = *(UINT32 *) VariableIndex;
      if (Signature != PLATFORM_OVERRIDE_ITEM_SIGNATURE) {
        Corrupted = TRUE;
        break;
      }
      //
      // Create PLATFORM_OVERRIDE_ITEM for this mapping
      //
      OverrideItem = AllocateZeroPool (sizeof (PLATFORM_OVERRIDE_ITEM));
      ASSERT (OverrideItem != NULL);
      OverrideItem->Signature = PLATFORM_OVERRIDE_ITEM_SIGNATURE;
      InitializeListHead (&OverrideItem->DriverInfoList);
      VariableIndex = VariableIndex + sizeof (UINT32);
      //
      // Get DriverNum
      //
      DriverNumber = *(UINT32*) VariableIndex;
      OverrideItem->DriverInfoNum = DriverNumber;
      VariableIndex = VariableIndex + sizeof (UINT32);
      //
      // Get ControllerDevicePath[]
      //
      ControllerDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) VariableIndex;
      OverrideItem->ControllerDevicePath = DuplicateDevicePath (ControllerDevicePath);
      VariableIndex = VariableIndex + GetDevicePathSize (ControllerDevicePath);
      //
      // Align the VariableIndex since the controller device path may not be aligned, refer to the SaveOverridesMapping()
      //
      VariableIndex += ((sizeof(UINT32) - ((UINTN) (VariableIndex))) & (sizeof(UINT32) - 1));

      //
      // Get all DriverImageDevicePath[]
      //
      for (Index = 0; Index < DriverNumber; Index++) {
        //
        // Create DRIVER_IMAGE_INFO for this DriverDevicePath[]
        //
        DriverImageInfo = AllocateZeroPool (sizeof (DRIVER_IMAGE_INFO));
        ASSERT (DriverImageInfo != NULL);
        DriverImageInfo->Signature = DRIVER_IMAGE_INFO_SIGNATURE;

        DriverDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) VariableIndex;
        DriverImageInfo->DriverImagePath = DuplicateDevicePath (DriverDevicePath);
        VariableIndex = VariableIndex + GetDevicePathSize (DriverDevicePath);
        //
        // Align the VariableIndex since the driver image device path may not be aligned, refer to the SaveOverridesMapping()
        //
        VariableIndex += ((sizeof(UINT32) - ((UINTN) (VariableIndex))) & (sizeof(UINT32) - 1));

        InsertTailList (&OverrideItem->DriverInfoList, &DriverImageInfo->Link);
      }
      InsertTailList (MappingDataBase, &OverrideItem->Link);
    }

    FreePool (VariableBuffer);
    if (Corrupted) {
      FreeMappingDatabase (MappingDataBase);
      return EFI_VOLUME_CORRUPTED;
    }

    //
    // If there are additional variables (PlatDriOver1, PlatDriOver2, PlatDriOver3.....), get them.
    // NotEnd indicates whether current variable is the end variable.
    //
    if (NotEnd != 0) {
      UnicodeSPrint (OverrideVariableName, sizeof (OverrideVariableName), L"PlatDriOver%d", VariableNum++);
      VariableBuffer = GetVariableAndSize (OverrideVariableName, &gEfiCallerIdGuid, &BufferSize);
      ASSERT ((UINTN) VariableBuffer % sizeof(UINTN) == 0);
      if (VariableBuffer == NULL) {
        FreeMappingDatabase (MappingDataBase);
        return EFI_VOLUME_CORRUPTED;
      }
    }

  } while (NotEnd != 0);

  return EFI_SUCCESS;
}


/**
  Calculate the needed size in NV variable for recording a specific PLATFORM_OVERRIDE_ITEM info.

  @param  OverrideItemListIndex    Pointer to the list of a specific PLATFORM_OVERRIDE_ITEM

  @return The needed size number

**/
UINTN
EFIAPI
GetOneItemNeededSize (
  IN  LIST_ENTRY            *OverrideItemListIndex
  )
{
  UINTN                       NeededSize;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  LIST_ENTRY                  *ImageInfoListIndex;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  UINTN                       DevicePathSize;

  NeededSize = 0;
  OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
  NeededSize += sizeof (UINT32); //UINT32  SIGNATURE;
  NeededSize += sizeof (UINT32); //UINT32  DriverNum;
  DevicePathSize = GetDevicePathSize (OverrideItem->ControllerDevicePath);
  NeededSize += DevicePathSize; // ControllerDevicePath
  //
  // Align the controller device path
  //
  NeededSize += ((sizeof(UINT32) - DevicePathSize) & (sizeof(UINT32) - 1));
  //
  // Traverse the Driver Info List of this Override Item
  //
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
    DevicePathSize = GetDevicePathSize (DriverImageInfo->DriverImagePath);
    NeededSize += DevicePathSize; //DriverDevicePath
    //
    // Align the driver image device path
    //
    NeededSize += ((sizeof(UINT32) - DevicePathSize) & (sizeof(UINT32) - 1));
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
  }

  return NeededSize;
}

/**
  Deletes all environment variable(s) that contain the override mappings from Controller Device Path to
  a set of Driver Device Paths.

  @retval EFI_SUCCESS  Delete all variable(s) successfully.

**/
EFI_STATUS
EFIAPI
DeleteOverridesVariables (
  VOID
  )
{
  EFI_STATUS                  Status;
  VOID                        *VariableBuffer;
  UINTN                       VariableNum;
  UINTN                       BufferSize;
  UINTN                       Index;
  CHAR16                      OverrideVariableName[40];

  //
  // Get environment variable(s) number
  //
  VariableNum = 0;
  VariableBuffer = GetVariableAndSize (L"PlatDriOver", &gEfiCallerIdGuid, &BufferSize);
  VariableNum++;
  if (VariableBuffer == NULL) {
    return EFI_NOT_FOUND;
  }
  //
  // Check NotEnd to get all PlatDriOverX variable(s)
  //
  while ((*(UINT32*)VariableBuffer) != 0) {
    UnicodeSPrint (OverrideVariableName, sizeof (OverrideVariableName), L"PlatDriOver%d", VariableNum);
    VariableBuffer = GetVariableAndSize (OverrideVariableName, &gEfiCallerIdGuid, &BufferSize);
    VariableNum++;
    ASSERT (VariableBuffer != NULL);
  }

  //
  // Delete PlatDriOver and all additional variables, if exist.
  //
  Status = gRT->SetVariable (
                  L"PlatDriOver",
                  &gEfiCallerIdGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  0,
                  NULL
                  );
  ASSERT (!EFI_ERROR (Status));
  for (Index = 1; Index < VariableNum; Index++) {
    UnicodeSPrint (OverrideVariableName, sizeof (OverrideVariableName), L"PlatDriOver%d", Index);
    Status = gRT->SetVariable (
                    OverrideVariableName,
                    &gEfiCallerIdGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    NULL
                    );
    ASSERT (!EFI_ERROR (Status));
  }
  return EFI_SUCCESS;
}


/**
  Save the memory mapping database into NV environment variable(s).

  @param  MappingDataBase          Mapping database list entry pointer

  @retval EFI_SUCCESS              Save memory mapping database successfully
  @retval EFI_INVALID_PARAMETER    MappingDataBase pointer is null

**/
EFI_STATUS
EFIAPI
SaveOverridesMapping (
  IN  LIST_ENTRY              *MappingDataBase
  )
{
  EFI_STATUS                  Status;
  VOID                        *VariableBuffer;
  UINT8                       *VariableIndex;
  UINTN                       NumIndex;
  CHAR16                      OverrideVariableName[40];
  UINT32                      NotEnd;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  LIST_ENTRY                  *OverrideItemListIndex;
  LIST_ENTRY                  *ItemIndex;
  LIST_ENTRY                  *ImageInfoListIndex;
  UINTN                       VariableNeededSize;
  UINT64                      MaximumVariableStorageSize;
  UINT64                      RemainingVariableStorageSize;
  UINT64                      MaximumVariableSize;
  UINTN                       OneItemNeededSize;

  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (MappingDataBase)) {
    Status = DeleteOverridesVariables ();
    return EFI_SUCCESS;
  }

  //
  // Get the the maximum size of an individual EFI variable in current system
  //
  gRT->QueryVariableInfo (
          EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
          &MaximumVariableStorageSize,
          &RemainingVariableStorageSize,
          &MaximumVariableSize
          );

  NumIndex = 0;
  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    //
    // Try to find the most proper variable size which <= MaximumVariableSize,
    // but can contain mapping info as much as possible
    //
    VariableNeededSize = sizeof (UINT32); // NotEnd;
    ItemIndex = OverrideItemListIndex;
    NotEnd = FALSE;
    //
    // Traverse all PLATFORM_OVERRIDE_ITEMs and get the total size.
    //
    while (!IsNull (MappingDataBase, ItemIndex)) {
      OneItemNeededSize = GetOneItemNeededSize (ItemIndex);
      //
      // If the total size exceeds the MaximumVariableSize, then we must use
      // multiple variables.
      //
      if ((VariableNeededSize +
           OneItemNeededSize +
           sizeof (VARIABLE_HEADER) +
           StrSize (L"PlatDriOver ")
           ) >= MaximumVariableSize
          ) {
        NotEnd = TRUE;
        break;
      }

      VariableNeededSize += OneItemNeededSize;
      ItemIndex = GetNextNode (MappingDataBase, ItemIndex);
    }

    if (NotEnd != 0) {
      if (VariableNeededSize == sizeof (UINT32)) {
        //
        // If an individual EFI variable cannot contain a single Item, return error
        //
        return EFI_OUT_OF_RESOURCES;
      }
    }

    //
    // VariableNeededSize is the most proper variable size, allocate variable buffer
    // ItemIndex now points to the next PLATFORM_OVERRIDE_ITEM which is not covered by VariableNeededSize
    //
    VariableBuffer = AllocateZeroPool (VariableNeededSize);
    ASSERT (VariableBuffer != NULL);
    ASSERT ((UINTN) VariableBuffer % sizeof(UINTN) == 0);

    //
    // Fill the variable buffer according to MappingDataBase
    //
    VariableIndex = VariableBuffer;
    *(UINT32 *) VariableIndex = NotEnd;
    VariableIndex += sizeof (UINT32); // pass NotEnd
    //
    // ItemIndex points to the next PLATFORM_OVERRIDE_ITEM which is not covered by VariableNeededSize
    //
    while (OverrideItemListIndex != ItemIndex){
      *(UINT32 *) VariableIndex = PLATFORM_OVERRIDE_ITEM_SIGNATURE;
      VariableIndex += sizeof (UINT32); // pass SIGNATURE

      OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
      *(UINT32 *) VariableIndex = OverrideItem->DriverInfoNum;
      VariableIndex += sizeof (UINT32); // pass DriverNum

      CopyMem (VariableIndex, OverrideItem->ControllerDevicePath, GetDevicePathSize (OverrideItem->ControllerDevicePath));
      VariableIndex += GetDevicePathSize (OverrideItem->ControllerDevicePath); // pass ControllerDevicePath

      //
      // Align the VariableIndex since the controller device path may not be aligned
      //
      VariableIndex += ((sizeof(UINT32) - ((UINTN) (VariableIndex))) & (sizeof(UINT32) - 1));
      //
      // Save the Driver Info List of this PLATFORM_OVERRIDE_ITEM
      //
      ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
      while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
        DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
        CopyMem (VariableIndex, DriverImageInfo->DriverImagePath, GetDevicePathSize (DriverImageInfo->DriverImagePath));
        VariableIndex += GetDevicePathSize (DriverImageInfo->DriverImagePath); // pass DriverImageDevicePath
        //
        // Align the VariableIndex since the driver image device path may not be aligned
        //
        VariableIndex += ((sizeof(UINT32) - ((UINTN) (VariableIndex))) & (sizeof(UINT32) - 1));
        ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
      }

      OverrideItemListIndex =  GetNextNode (MappingDataBase, OverrideItemListIndex);
    }

    ASSERT (((UINTN)VariableIndex - (UINTN)VariableBuffer) == VariableNeededSize);

    if (NumIndex == 0) {
      UnicodeSPrint (OverrideVariableName, sizeof (OverrideVariableName), L"PlatDriOver");
    } else {
      UnicodeSPrint (OverrideVariableName, sizeof (OverrideVariableName), L"PlatDriOver%d", NumIndex );
    }

    Status = gRT->SetVariable (
                    OverrideVariableName,
                    &gEfiCallerIdGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    VariableNeededSize,
                    VariableBuffer
                    );
    ASSERT (!EFI_ERROR(Status));

    NumIndex ++;
    FreePool (VariableBuffer);
  }

  return EFI_SUCCESS;
}

/**
  Get the first Binding protocol which has the specific image handle.

  @param  ImageHandle          The Image handle
  @param  BindingHandle        The BindingHandle of the found Driver Binding protocol.
                               If Binding protocol is not found, it is set to NULL. 

  @return                      Pointer into the Binding Protocol interface
  @retval NULL                 The paramter is not valid or the binding protocol is not found.

**/
EFI_DRIVER_BINDING_PROTOCOL *
EFIAPI
GetBindingProtocolFromImageHandle (
  IN  EFI_HANDLE   ImageHandle,
  OUT EFI_HANDLE   *BindingHandle
  )
{
  EFI_STATUS                        Status;
  UINTN                             Index;
  UINTN                             DriverBindingHandleCount;
  EFI_HANDLE                        *DriverBindingHandleBuffer;
  EFI_DRIVER_BINDING_PROTOCOL       *DriverBindingInterface;

  if (BindingHandle == NULL || ImageHandle == NULL) {
    return NULL;
  }
  //
  // Get all drivers which support driver binding protocol
  //
  DriverBindingHandleCount  = 0;
  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiDriverBindingProtocolGuid,
                  NULL,
                  &DriverBindingHandleCount,
                  &DriverBindingHandleBuffer
                  );
  if (EFI_ERROR (Status) || (DriverBindingHandleCount == 0)) {
    return NULL;
  }

  for (Index = 0; Index < DriverBindingHandleCount; Index++) {
    DriverBindingInterface = NULL;
    Status = gBS->OpenProtocol (
                    DriverBindingHandleBuffer[Index],
                    &gEfiDriverBindingProtocolGuid,
                    (VOID **) &DriverBindingInterface,
                    NULL,
                    NULL,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (EFI_ERROR (Status)) {
      continue;
    }

    if (DriverBindingInterface->ImageHandle == ImageHandle) {
      *BindingHandle = DriverBindingHandleBuffer[Index];
      FreePool (DriverBindingHandleBuffer);
      return DriverBindingInterface;
    }
  }

  //
  // If no Driver Binding Protocol instance is found
  //
  FreePool (DriverBindingHandleBuffer);
  *BindingHandle = NULL;
  return NULL;
}

/**
  Return the current TPL.

  @return Current TPL

**/
EFI_TPL
GetCurrentTpl (
  VOID
  )
{
  EFI_TPL                 Tpl;

  Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
  gBS->RestoreTPL (Tpl);

  return Tpl;
}


/**
  Retrieves the image handle of the platform override driver for a controller in
  the system from the memory mapping database.

  @param  ControllerHandle         The device handle of the controller to check if
                                   a driver override exists.
  @param  DriverImageHandle        On input, the previously returnd driver image handle.
                                   On output, a pointer to the next driver handle.
                                   Passing in a pointer to NULL, will return the
                                   first driver handle for ControllerHandle.
  @param  MappingDataBase          Mapping database list entry pointer
  @param  CallerImageHandle        The caller driver's image handle, for
                                   UpdateFvFileDevicePath use.

  @retval EFI_INVALID_PARAMETER    The handle specified by ControllerHandle is not
                                   a valid handle.  Or DriverImagePath is not a
                                   device path that was returned on a previous call
                                   to GetDriverPath().
  @retval EFI_NOT_FOUND            A driver override for ControllerHandle was not
                                   found.
  @retval EFI_UNSUPPORTED          The operation is not supported.
  @retval EFI_SUCCESS              The driver override for ControllerHandle was
                                   returned in DriverImagePath.

**/
EFI_STATUS
EFIAPI
GetDriverFromMapping (
  IN     EFI_HANDLE                                     ControllerHandle,
  IN OUT EFI_HANDLE                                     *DriverImageHandle,
  IN     LIST_ENTRY                                     *MappingDataBase,
  IN     EFI_HANDLE                                     CallerImageHandle
  )
{
  EFI_STATUS                  Status;
  EFI_DEVICE_PATH_PROTOCOL    *ControllerDevicePath;
  BOOLEAN                     ControllerFound;
  BOOLEAN                     ImageFound;
  EFI_HANDLE                  *ImageHandleBuffer;
  UINTN                       ImageHandleCount;
  UINTN                       Index;
  EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
  EFI_HANDLE                  DriverBindingHandle;
  BOOLEAN                     FoundLastReturned;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  LIST_ENTRY                  *OverrideItemListIndex;
  LIST_ENTRY                  *ImageInfoListIndex;
  EFI_DEVICE_PATH_PROTOCOL    *TempDriverImagePath;
  EFI_HANDLE                  ImageHandle;
  EFI_HANDLE                  Handle;
  EFI_DEVICE_PATH_PROTOCOL    *LoadedImageDevicePath;
  EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL  *BusSpecificDriverOverride;
  UINTN                       DevicePathSize;

  //
  // Check that ControllerHandle is a valid handle
  //
  if (ControllerHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Get the device path of ControllerHandle
  //
  Status = gBS->HandleProtocol (
                  ControllerHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **) &ControllerDevicePath
                  );
  if (EFI_ERROR (Status) || ControllerDevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Search ControllerDevicePath in MappingDataBase
  //
  OverrideItem = NULL;
  ControllerFound = FALSE;
  DevicePathSize = GetDevicePathSize (ControllerDevicePath);

  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
    if (DevicePathSize == GetDevicePathSize (OverrideItem->ControllerDevicePath)) {
      if (CompareMem (
            ControllerDevicePath,
            OverrideItem->ControllerDevicePath,
            DevicePathSize
            ) == 0
          ) {
        ControllerFound = TRUE;
        break;
      }
    }
    OverrideItemListIndex = GetNextNode (MappingDataBase, OverrideItemListIndex);
  }

  if (!ControllerFound) {
    return EFI_NOT_FOUND;
  }
  //
  // Passing in a pointer to NULL, will return the first driver device path for ControllerHandle.
  // Check whether the driverImagePath is not a device path that was returned on a previous call to GetDriverPath().
  //
  if (*DriverImageHandle != NULL) {
    if (*DriverImageHandle != OverrideItem->LastReturnedImageHandle) {
      return EFI_INVALID_PARAMETER;
    }
  }
  //
  // The GetDriverPath() may be called recursively, because it use ConnectDevicePath() internally,
  //  so should check whether there is a dead loop.
  //  Here use a controller device path stack to record all processed controller device path during a GetDriverPath() call,
  //  and check the controller device path whether appear again during the GetDriverPath() call.
  //
  if (CheckExistInStack (OverrideItem->ControllerDevicePath)) {
    //
    // There is a dependecy dead loop if the ControllerDevicePath appear in stack twice
    //
    return EFI_UNSUPPORTED;
  }
  PushDevPathStack (OverrideItem->ControllerDevicePath);

  //
  // Check every override driver, try to load and start them
  //
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
    if (DriverImageInfo->ImageHandle == NULL) {
      //
      // Skip if the image is unloadable or unstartable
      //
      if ((!DriverImageInfo->UnLoadable) && ((!DriverImageInfo->UnStartable))) {
        TempDriverImagePath = DriverImageInfo->DriverImagePath;
        //
        // If the image device path contains an FV node, check the FV file device path is valid.
        // If it is invalid, try to return the valid device path.
        // FV address maybe changes for memory layout adjust from time to time,
        // use this funciton could promise the FV file device path is right.
        //
        Status = UpdateFvFileDevicePath (&TempDriverImagePath, NULL, CallerImageHandle);
        if (!EFI_ERROR (Status)) {
          FreePool (DriverImageInfo->DriverImagePath);
          DriverImageInfo->DriverImagePath = TempDriverImagePath;
        }
        //
        // Get all Loaded Image protocol to check whether the driver image has been loaded and started
        //
        ImageFound = FALSE;
        ImageHandleCount  = 0;
        Status = gBS->LocateHandleBuffer (
                        ByProtocol,
                        &gEfiLoadedImageProtocolGuid,
                        NULL,
                        &ImageHandleCount,
                        &ImageHandleBuffer
                        );
        if (EFI_ERROR (Status) || (ImageHandleCount == 0)) {
          return EFI_NOT_FOUND;
        }

        for(Index = 0; Index < ImageHandleCount; Index ++) {
          //
          // Get the EFI Loaded Image Device Path Protocol
          //
          LoadedImageDevicePath = NULL;
          Status = gBS->HandleProtocol (
                          ImageHandleBuffer[Index],
                          &gEfiLoadedImageDevicePathProtocolGuid,
                          (VOID **) &LoadedImageDevicePath
                          );
          if (EFI_ERROR (Status)) {
            //
            // Maybe not all EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL existed.
            //
            continue;
          }

          DevicePathSize = GetDevicePathSize (DriverImageInfo->DriverImagePath);
          if (DevicePathSize == GetDevicePathSize (LoadedImageDevicePath)) {
            if (CompareMem (
                  DriverImageInfo->DriverImagePath,
                  LoadedImageDevicePath,
                  GetDevicePathSize (LoadedImageDevicePath)
                  ) == 0
                ) {
              ImageFound = TRUE;
              break;
            }
          }
        }

        if (ImageFound) {
          //
          // Find its related driver binding protocol
          // Driver binding handle may be different with its driver's Image Handle.
          //
          DriverBindingHandle = NULL;
          DriverBinding = GetBindingProtocolFromImageHandle (
                            ImageHandleBuffer[Index],
                            &DriverBindingHandle
                            );
          ASSERT (DriverBinding != NULL);
          DriverImageInfo->ImageHandle = ImageHandleBuffer[Index];
        } else if (GetCurrentTpl() <= TPL_CALLBACK){
          //
          // The driver image has not been loaded and started. Try to load and start it now.
          // Try to connect all device in the driver image path.
          //
          // Note: LoadImage() and  StartImage() should be called under CALLBACK TPL in theory, but
          // since many device need to be connected in  CALLBACK level environment( e.g. Usb devices )
          // and the Fat and Patition driver can endure executing in CALLBACK level in fact, so here permit
          // to use LoadImage() and  StartImage() in CALLBACK TPL.
          //
          Status = ConnectDevicePath (DriverImageInfo->DriverImagePath);
          //
          // check whether it points to a PCI Option Rom image,
          // and try to use bus override protocol to get its first option rom image driver
          //
          TempDriverImagePath = DriverImageInfo->DriverImagePath;
          gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &TempDriverImagePath, &Handle);
          //
          // Get the Bus Specific Driver Override Protocol instance on the Controller Handle
          //
          Status = gBS->HandleProtocol(
                          Handle,
                          &gEfiBusSpecificDriverOverrideProtocolGuid,
                          (VOID **) &BusSpecificDriverOverride
                          );
          if (!EFI_ERROR (Status) && (BusSpecificDriverOverride != NULL)) {
            ImageHandle = NULL;
            Status = BusSpecificDriverOverride->GetDriver (
                                                  BusSpecificDriverOverride,
                                                  &ImageHandle
                                                  );
            if (!EFI_ERROR (Status)) {
              //
              // Find its related driver binding protocol
              // Driver binding handle may be different with its driver's Image handle
              //
              DriverBindingHandle = NULL;
              DriverBinding = GetBindingProtocolFromImageHandle (
                                ImageHandle,
                                &DriverBindingHandle
                                );
              ASSERT (DriverBinding != NULL);
              DriverImageInfo->ImageHandle = ImageHandle;
            }
          }
          //
          // Skip if any device cannot be connected now, future passes through GetDriver() may be able to load that driver.
          // Only file path media or FwVol Device Path Node remain if all device is connected
          //
          TempDriverImagePath = DriverImageInfo->DriverImagePath;
          gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &TempDriverImagePath, &Handle);
          if (((DevicePathType (TempDriverImagePath) == MEDIA_DEVICE_PATH) &&
               (DevicePathSubType (TempDriverImagePath) == MEDIA_FILEPATH_DP)) ||
              (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) TempDriverImagePath) != NULL)
             ) {
            //
            // Try to load the driver
            //
            TempDriverImagePath = DriverImageInfo->DriverImagePath;
            Status = gBS->LoadImage (
                            FALSE,
                            CallerImageHandle,
                            TempDriverImagePath,
                            NULL,
                            0,
                            &ImageHandle
                            );
            if (!EFI_ERROR (Status)) {
              //
              // Try to start the driver
              //
              Status = gBS->StartImage (ImageHandle, NULL, NULL);
              if (EFI_ERROR (Status)){
                DriverImageInfo->UnStartable = TRUE;
                DriverImageInfo->ImageHandle = NULL;
              } else {
                //
                // Find its related driver binding protocol
                // Driver binding handle may be different with its driver's Image handle
                //
                DriverBindingHandle = NULL;
                DriverBinding = GetBindingProtocolFromImageHandle (
                                   ImageHandle,
                                   &DriverBindingHandle
                                   );
                ASSERT (DriverBinding != NULL);
                DriverImageInfo->ImageHandle = ImageHandle;
              }
            } else {
              DriverImageInfo->UnLoadable = TRUE;
              DriverImageInfo->ImageHandle = NULL;
            }
          }
        }
        FreePool (ImageHandleBuffer);
      }
    }
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
  }
  //
  // Finish try to load and start the override driver of a controller, popup the controller's device path
  //
  PopDevPathStack (NULL);

  //
  // return the DriverImageHandle for ControllerHandle
  //
  FoundLastReturned = FALSE;
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
    if (DriverImageInfo->ImageHandle != NULL) {
      if ((*DriverImageHandle == NULL) || FoundLastReturned) {
        //
        // If DriverImageHandle is NULL, then we just need to return the first driver.
        // If FoundLastReturned, this means we have just encountered the previously returned driver.
        // For both cases, we just return the image handle of this driver.
        //
        OverrideItem->LastReturnedImageHandle = DriverImageInfo->ImageHandle;
        *DriverImageHandle = DriverImageInfo->ImageHandle;
        return EFI_SUCCESS;
      } else if (*DriverImageHandle == DriverImageInfo->ImageHandle){
        //
        // We have found the previously returned driver.
        //
        FoundLastReturned = TRUE;
      }
    }
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
  }

  return EFI_NOT_FOUND;
}


/**
  Check mapping database whether already has the mapping info which
  records the input Controller to input DriverImage.

  @param  ControllerDevicePath     The controller device path is to be check.
  @param  DriverImageDevicePath    The driver image device path is to be check.
  @param  MappingDataBase          Mapping database list entry pointer
  @param  DriverInfoNum            the controller's total override driver number
  @param  DriverImageNO            The driver order number for the input DriverImage.
                                   If the DriverImageDevicePath is NULL, DriverImageNO is not set.

  @retval EFI_INVALID_PARAMETER    ControllerDevicePath or MappingDataBase is NULL.
  @retval EFI_NOT_FOUND            ControllerDevicePath is not found in MappingDataBase or
                                   DriverImageDevicePath is not found in the found DriverImage Info list. 
  @retval EFI_SUCCESS              The controller's total override driver number and 
                                   input DriverImage's order number is correctly return.
**/
EFI_STATUS
EFIAPI
CheckMapping (
  IN     EFI_DEVICE_PATH_PROTOCOL                       *ControllerDevicePath,
  IN     EFI_DEVICE_PATH_PROTOCOL                       *DriverImageDevicePath  OPTIONAL,
  IN     LIST_ENTRY                                     *MappingDataBase,
  OUT    UINT32                                         *DriverInfoNum  OPTIONAL,
  OUT    UINT32                                         *DriverImageNO  OPTIONAL
  )
{
  LIST_ENTRY                  *OverrideItemListIndex;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  LIST_ENTRY                  *ImageInfoListIndex;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  BOOLEAN                     Found;
  UINT32                      ImageNO;
  UINTN                       DevicePathSize;

  if (ControllerDevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Search ControllerDevicePath in MappingDataBase
  //
  Found = FALSE;
  OverrideItem = NULL;
  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
    DevicePathSize = GetDevicePathSize (ControllerDevicePath);
    if (DevicePathSize == GetDevicePathSize (OverrideItem->ControllerDevicePath)) {
      if (CompareMem (
            ControllerDevicePath,
            OverrideItem->ControllerDevicePath,
            DevicePathSize
            ) == 0
          ) {
        Found = TRUE;
        break;
      }
    }
    OverrideItemListIndex = GetNextNode (MappingDataBase, OverrideItemListIndex);
  }

  if (!Found) {
    //
    // ControllerDevicePath is not in MappingDataBase
    //
    return EFI_NOT_FOUND;
  }

  ASSERT (OverrideItem->DriverInfoNum != 0);
  if (DriverInfoNum != NULL) {
    *DriverInfoNum = OverrideItem->DriverInfoNum;
  }

  //
  // If DriverImageDevicePath is NULL, skip checking DriverImageDevicePath
  // in the controller's Driver Image Info List
  //
  if (DriverImageDevicePath == NULL) {
    return EFI_SUCCESS;
  }
  //
  // return the DriverImageHandle for ControllerHandle
  //
  ImageNO = 0;
  Found = FALSE;
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
    ImageNO++;
    DevicePathSize = GetDevicePathSize (DriverImageDevicePath);
    if (DevicePathSize == GetDevicePathSize (DriverImageInfo->DriverImagePath)) {
      if (CompareMem (
            DriverImageDevicePath,
            DriverImageInfo->DriverImagePath,
            GetDevicePathSize (DriverImageInfo->DriverImagePath)
            ) == 0
          ) {
        Found = TRUE;
        break;
      }
    }
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
  }

  if (!Found) {
    //
    // DriverImageDevicePath is not found in the controller's Driver Image Info List
    //
    return EFI_NOT_FOUND;
  } else {
    if (DriverImageNO != NULL) {
      *DriverImageNO = ImageNO;
    }
    return EFI_SUCCESS;
  }
}


/**
  Insert a driver image as a controller's override driver into the mapping database.
  The driver image's order number is indicated by DriverImageNO.

  @param  ControllerDevicePath     The controller device path need to add a
                                   override driver image item
  @param  DriverImageDevicePath    The driver image device path need to be insert
  @param  MappingDataBase          Mapping database list entry pointer
  @param  DriverImageNO            The inserted order number. If this number is taken, 
                                   the larger available number will be used.

  @retval EFI_INVALID_PARAMETER    ControllerDevicePath is NULL, or DriverImageDevicePath is NULL
                                   or MappingDataBase is NULL
  @retval EFI_ALREADY_STARTED      The input Controller to input DriverImage has been 
                                   recorded into the mapping database.
  @retval EFI_SUCCESS              The Controller and DriverImage are inserted into 
                                   the mapping database successfully.

**/
EFI_STATUS
EFIAPI
InsertDriverImage (
  IN     EFI_DEVICE_PATH_PROTOCOL                       *ControllerDevicePath,
  IN     EFI_DEVICE_PATH_PROTOCOL                       *DriverImageDevicePath,
  IN     LIST_ENTRY                                     *MappingDataBase,
  IN     UINT32                                         DriverImageNO
  )
{
  EFI_STATUS                  Status;
  LIST_ENTRY                  *OverrideItemListIndex;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  LIST_ENTRY                  *ImageInfoListIndex;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  BOOLEAN                     Found;
  UINT32                      ImageNO;
  UINTN                       DevicePathSize;

  if (ControllerDevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  if (DriverImageDevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If the driver is already in the controller's Driver Image Info List,
  // just return EFI_ALREADY_STARTED.
  //
  Status = CheckMapping (
             ControllerDevicePath,
             DriverImageDevicePath,
             MappingDataBase,
             NULL,
             NULL
             );
  if (Status == EFI_SUCCESS) {
    return EFI_ALREADY_STARTED;
  }

  //
  // Search the input ControllerDevicePath in MappingDataBase
  //
  Found = FALSE;
  OverrideItem = NULL;
  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
    DevicePathSize = GetDevicePathSize (ControllerDevicePath);
    if (DevicePathSize == GetDevicePathSize (OverrideItem->ControllerDevicePath)) {
      if (CompareMem (
            ControllerDevicePath,
            OverrideItem->ControllerDevicePath,
            DevicePathSize
            ) == 0
          ) {
        Found = TRUE;
        break;
      }
    }
    OverrideItemListIndex = GetNextNode (MappingDataBase, OverrideItemListIndex);
  }
  //
  // If cannot find, this is a new controller item
  // Add the Controller related PLATFORM_OVERRIDE_ITEM structrue in mapping data base
  //
  if (!Found) {
    OverrideItem = AllocateZeroPool (sizeof (PLATFORM_OVERRIDE_ITEM));
    ASSERT (OverrideItem != NULL);
    OverrideItem->Signature = PLATFORM_OVERRIDE_ITEM_SIGNATURE;
    OverrideItem->ControllerDevicePath = DuplicateDevicePath (ControllerDevicePath);
    InitializeListHead (&OverrideItem->DriverInfoList);
    InsertTailList (MappingDataBase, &OverrideItem->Link);
  }

  //
  // Prepare the driver image related DRIVER_IMAGE_INFO structure.
  //
  DriverImageInfo = AllocateZeroPool (sizeof (DRIVER_IMAGE_INFO));
  ASSERT (DriverImageInfo != NULL);
  DriverImageInfo->Signature = DRIVER_IMAGE_INFO_SIGNATURE;
  DriverImageInfo->DriverImagePath = DuplicateDevicePath (DriverImageDevicePath);
  //
  // Find the driver image wanted order location
  //
  ImageNO = 0;
  Found = FALSE;
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    if (ImageNO == (DriverImageNO - 1)) {
      //
      // find the wanted order location, insert it
      //
      InsertTailList (ImageInfoListIndex, &DriverImageInfo->Link);
      OverrideItem->DriverInfoNum ++;
      Found = TRUE;
      break;
    }
    ImageNO++;
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
  }

  if (!Found) {
    //
    // if not find the wanted order location, add it as last item of the controller mapping item
    //
    InsertTailList (&OverrideItem->DriverInfoList, &DriverImageInfo->Link);
    OverrideItem->DriverInfoNum ++;
  }

  return EFI_SUCCESS;
}


/**
  Delete a controller's override driver from the mapping database.

  @param  ControllerDevicePath     The controller device path will be deleted 
                                   when all drivers images on it are removed.
  @param  DriverImageDevicePath    The driver image device path will be delete.
                                   If NULL, all driver image will be delete.
  @param  MappingDataBase          Mapping database list entry pointer

  @retval EFI_INVALID_PARAMETER    ControllerDevicePath is NULL, or MappingDataBase is NULL
  @retval EFI_NOT_FOUND            ControllerDevicePath is not found in MappingDataBase or
                                   DriverImageDevicePath is not found in the found DriverImage Info list. 
  @retval EFI_SUCCESS              Delete the specified driver successfully.

**/
EFI_STATUS
EFIAPI
DeleteDriverImage (
  IN     EFI_DEVICE_PATH_PROTOCOL                       *ControllerDevicePath,
  IN     EFI_DEVICE_PATH_PROTOCOL                       *DriverImageDevicePath,
  IN     LIST_ENTRY                                     *MappingDataBase
  )
{
  EFI_STATUS                  Status;
  LIST_ENTRY                  *OverrideItemListIndex;
  PLATFORM_OVERRIDE_ITEM      *OverrideItem;
  LIST_ENTRY                  *ImageInfoListIndex;
  DRIVER_IMAGE_INFO           *DriverImageInfo;
  BOOLEAN                     Found;
  UINTN                       DevicePathSize;

  if (ControllerDevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (MappingDataBase == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If ControllerDevicePath is not found in mapping database, return EFI_NOT_FOUND.
  //
  Status = CheckMapping (
             ControllerDevicePath,
             DriverImageDevicePath,
             MappingDataBase,
             NULL,
             NULL
             );
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  //
  // Search ControllerDevicePath in MappingDataBase
  //
  Found = FALSE;
  OverrideItem = NULL;
  OverrideItemListIndex = GetFirstNode (MappingDataBase);
  while (!IsNull (MappingDataBase, OverrideItemListIndex)) {
    OverrideItem = CR(OverrideItemListIndex, PLATFORM_OVERRIDE_ITEM, Link, PLATFORM_OVERRIDE_ITEM_SIGNATURE);
    DevicePathSize = GetDevicePathSize (ControllerDevicePath);
    if (DevicePathSize == GetDevicePathSize (OverrideItem->ControllerDevicePath)) {
      if (CompareMem (
            ControllerDevicePath,
            OverrideItem->ControllerDevicePath,
            DevicePathSize
            ) == 0
          ) {
        Found = TRUE;
        break;
      }
    }
    OverrideItemListIndex = GetNextNode (MappingDataBase, OverrideItemListIndex);
  }

  ASSERT (Found);
  ASSERT (OverrideItem->DriverInfoNum != 0);

  Found = FALSE;
  ImageInfoListIndex = GetFirstNode (&OverrideItem->DriverInfoList);
  while (!IsNull (&OverrideItem->DriverInfoList, ImageInfoListIndex)) {
    DriverImageInfo = CR(ImageInfoListIndex, DRIVER_IMAGE_INFO, Link, DRIVER_IMAGE_INFO_SIGNATURE);
    ImageInfoListIndex = GetNextNode (&OverrideItem->DriverInfoList, ImageInfoListIndex);
    if (DriverImageDevicePath != NULL) {
      //
      // Search for the specified DriverImageDevicePath and remove it, then break.
      //
      DevicePathSize = GetDevicePathSize (DriverImageDevicePath);
      if (DevicePathSize == GetDevicePathSize (DriverImageInfo->DriverImagePath)) {
        if (CompareMem (
              DriverImageDevicePath,
              DriverImageInfo->DriverImagePath,
              GetDevicePathSize (DriverImageInfo->DriverImagePath)
              ) == 0
            ) {
          Found = TRUE;
          FreePool(DriverImageInfo->DriverImagePath);
          RemoveEntryList (&DriverImageInfo->Link);
          OverrideItem->DriverInfoNum --;
          break;
        }
      }
    } else {
      //
      // Remove all existing driver image info entries, so no break here.
      //
      Found = TRUE;
      FreePool(DriverImageInfo->DriverImagePath);
      RemoveEntryList (&DriverImageInfo->Link);
      OverrideItem->DriverInfoNum --;
    }
  }

  //
  // Confirm all driver image info entries have been removed,
  // if DriverImageDevicePath is NULL.
  //
  if (DriverImageDevicePath == NULL) {
    ASSERT (OverrideItem->DriverInfoNum == 0);
  }
  //
  // If Override Item has no driver image info entry, then delete this item.
  //
  if (OverrideItem->DriverInfoNum == 0) {
    FreePool(OverrideItem->ControllerDevicePath);
    RemoveEntryList (&OverrideItem->Link);
    FreePool (OverrideItem);
  }

  if (!Found) {
    //
    // DriverImageDevicePath is not NULL and cannot be found in the controller's
    // driver image info list.
    //
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}
