/** @file
 Function Service for L05 Setup Menu

;******************************************************************************
;* Copyright (c) 2013 - 2015, Insyde Software Corporation. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;* 
;******************************************************************************
*/ 

#include <Uefi.h>
#include <OemSwSmi.h>
#include <SmiTable.h>
#include <L05Config.h>
#include <SetupConfig.h>
#include <KernelSetupConfig.h>
#include <Guid/DebugMask.h>
#include <Guid/EventGroup.h>
#include <Guid/GlobalVariable.h>
#include <Guid/AdmiSecureBoot.h>
#include <Guid/LfcVariableGuid.h>
#include <Guid/H2OBdsCheckPoint.h>
#include <Protocol/BlockIo.h>
#include <Protocol/UsbIo.h>
#include <Protocol/UsbCore.h>
#include <Protocol/LegacyBios.h>
#include <Protocol/DevicePath.h>
#include <Protocol/L05SetupMenu.h>
#include <Protocol/LenovoVariable.h> 
#include <Protocol/L05SecureBoot.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/IoLib.h>
#include <Library/UefiLib.h>
#include <Library/LfcEcLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/PrintLib.h>
#include <Library/DevicePathLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/GenericBdsLib.h>
#include <Library/UefiBootServicesTableLib.h> 
#include <Library/UefiRuntimeServicesTableLib.h>

#define LCFC_BOOT_TYPE_EFI_NETWORK_IPV4  ((0xE0)* 256 + BBS_EMBED_NETWORK)
#define LCFC_BOOT_TYPE_EFI_NETWORK_IPV6  ((0xE0)* 256 + BBS_EMBED_NETWORK + 1)

EFI_OVERRIDE_DEFAULT_SETUP_SETTING                mOrgOverrideDefaultSetupSetting;
EFI_OVERRIDE_SETUP_SETTING_DURING_POST            mOrgOverrideSetupSettingDuringPost;
EFI_OVERRIDE_DEFAULT_PCH_SETUP_SETTING            mOrgOverrideDefaultPchSetupSetting;

STATIC DUMMY_BOOT_OPTION_INFO  mLcfcDummyBootOptionTable[] = {
  {DummyUsbBootOptionNum,     L"EFI USB Device"},
  {DummyCDBootOptionNum,      L"EFI DVD/CDROM"},
  {DummyNetwokrBootOptionNum, L"EFI Network"}
};

typedef enum {
  LcfcFirstBoot = 1,
  LcfcSetChgboot,
  LcfcF9LoadDefault
} LcfcOverrideDefaultReason;

VOID *
EFIAPI
LcfcBdsLibGetVariableAndSize (
  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) {
      *VariableSize = 0;
      return NULL;
    }
    //
    // Read variable into the allocated buffer.
    //
    Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
    if (EFI_ERROR (Status)) {
      BufferSize = 0;
      gBS->FreePool (Buffer);
      Buffer = NULL;
    }
  }

  *VariableSize = BufferSize;

  return Buffer;
}


EFI_STATUS
LcfcBootOrderSetDefault (
  VOID
  )
{
  EFI_STATUS                     Status            = EFI_SUCCESS;
  UINT16                         *BootOrderList    = NULL;
  UINTN                          BootOrderSize     = 0;
  UINT16                         *PhyBootOrderList = NULL;
  UINTN                          PhyBootOrderSize  = 0;


  BootOrderList = NULL;
  BootOrderSize = 0;
  Status = gRT->SetVariable (
                  L"BootOrder",
                  &gEfiGlobalVariableGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  BootOrderSize,
                  BootOrderList
                  );


  PhyBootOrderList = NULL;
  PhyBootOrderSize = 0;
  Status = gRT->SetVariable (
                  L"PhysicalBootOrder",
                  &gEfiGenericVariableGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  PhyBootOrderSize,
                  PhyBootOrderList
                  );


  return Status;
}


BOOLEAN
LcfcIsUsbMemoryDevice (
  IN  EFI_DEVICE_PATH_PROTOCOL           *DevicePathPtr
  )
{

  EFI_DEVICE_PATH_PROTOCOL               *TempDevicePath;
  EFI_DEVICE_PATH_PROTOCOL               *UsbDevicePath;
  EFI_STATUS                             Status;
  EFI_BLOCK_IO_PROTOCOL                  *BlkIo;
  EFI_HANDLE                             Handle;
  BOOLEAN                                IsUsbMemory;

  TempDevicePath = NULL;
  UsbDevicePath  = NULL;
  BlkIo          = NULL;
  IsUsbMemory    = FALSE;
  TempDevicePath = DevicePathPtr;
  UsbDevicePath  = DevicePathPtr;

  //
  // 1. Check the device whether it is usb device or not
  // 2. Use BlockIO to check Usb memory device
  // 3. if the device is usb memory, exit the loop
  //
  while (!IsDevicePathEndType (TempDevicePath) && IsUsbMemory == FALSE) {
    switch (DevicePathType (TempDevicePath)) {

    case MESSAGING_DEVICE_PATH:
     switch (DevicePathSubType(TempDevicePath)) {

       case MSG_USB_DP:
         Status = gBS->LocateDevicePath (
                         &gEfiDevicePathProtocolGuid,
                         &UsbDevicePath,
                         &Handle
                         );

         if (!EFI_ERROR(Status)) {
           Status = gBS->HandleProtocol (
                           Handle,
                           &gEfiBlockIoProtocolGuid,
                           &BlkIo
                           );
           if (!EFI_ERROR(Status)) {
             if (BlkIo->Media->RemovableMedia == TRUE) {
               IsUsbMemory = TRUE;
               break;
             }
           }
         }
         break;

      default:
        break;
      }
      break;

    default:
      break;
    }
    TempDevicePath = NextDevicePathNode (TempDevicePath);
  }

  if (IsUsbMemory) {
    return TRUE;
  } else {
    return FALSE;
  }
}


EFI_STATUS
LcfcDevicePathToBootType (
  IN  EFI_DEVICE_PATH_PROTOCOL           *DevicePathPtr,
  OUT UINT16                             *BootType
  )
{
  EFI_STATUS                Status = EFI_SUCCESS;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode = NULL;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath     = NULL;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePathTemp = NULL; 
  UINTN                     SATAPort = 0;
  BOOLEAN                   IsUsbDevice = FALSE;  
  BOOLEAN                   Found       = FALSE;
  UINTN                     HandleCount;
  EFI_HANDLE                *HandleBuffer;
  UINTN                     UsbMassDevicesCount;
  UINTN                     Index;
  UINTN                     Index2;
  EFI_USB_CORE_PROTOCOL     *UsbCore;
  USB_MASS_STORAGE_DEVICE   **UsbMassDevices;
  EFI_USB_IO_PROTOCOL       *UsbIoInstance;
  EFI_FILE_PROTOCOL         *FileSystem;  

  *BootType = L05_BOOT_TYPE_EFI_UNKNOWN;

  //
  //  Check the device path to find which type it is
  //
  DevicePathNode = DevicePathPtr;
  while (!IsDevicePathEnd (DevicePathNode)) {
    if ((DevicePathType (DevicePathNode) == BBS_DEVICE_PATH) &&
        (DevicePathSubType (DevicePathNode) == BBS_BBS_DP)) {
      *BootType = L05_BOOT_TYPE_NON_EFI;
      break;
    } else if ((DevicePathType (DevicePathNode) == MESSAGING_DEVICE_PATH) &&
               (DevicePathSubType (DevicePathNode) == MSG_USB_DP)) {
      IsUsbDevice = TRUE;
    } else if ((DevicePathType (DevicePathNode) == MESSAGING_DEVICE_PATH) &&
               (DevicePathSubType (DevicePathNode) == MSG_SATA_DP)) {
      SATAPort = ((SATA_DEVICE_PATH*)DevicePathNode)->HBAPortNumber;
    } else if (((DevicePathType (DevicePathNode) == MESSAGING_DEVICE_PATH) &&
                (DevicePathSubType (DevicePathNode) == MSG_IPv4_DP))) {
      *BootType = LCFC_BOOT_TYPE_EFI_NETWORK_IPV4;
    }else if  (((DevicePathType (DevicePathNode) == MESSAGING_DEVICE_PATH) &&
                (DevicePathSubType (DevicePathNode) == MSG_IPv6_DP))) {
      *BootType = LCFC_BOOT_TYPE_EFI_NETWORK_IPV6;
      break;
    } else if ((DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH) &&
               (DevicePathSubType (DevicePathNode) == MEDIA_HARDDRIVE_DP)) {
      if (IsUsbDevice) {
        *BootType = L05_BOOT_TYPE_EFI_USB_HDD;
        if (LcfcIsUsbMemoryDevice(DevicePathPtr)) {
          *BootType = L05_BOOT_TYPE_EFI_USB_MEMORY;
        }
      } else {
        *BootType = L05_BOOT_TYPE_EFI_HARDDISK;
      }
      break;
    } else if ((DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH) &&
               (DevicePathSubType (DevicePathNode) == MEDIA_CDROM_DP)) {
      if (IsUsbDevice) {
        *BootType = L05_BOOT_TYPE_EFI_USB_CDROM;
      } else {
        *BootType = L05_BOOT_TYPE_EFI_CDROM;
      }
      break;
    } else if ((DevicePathType (DevicePathNode) == ACPI_DEVICE_PATH) &&
               (EISA_ID_TO_NUM(((ACPI_HID_DEVICE_PATH*)DevicePathNode)->HID) == 0x0604)) {
      *BootType = L05_BOOT_TYPE_EFI_FLOPPY;
      break;
    }

    DevicePathNode = NextDevicePathNode (DevicePathNode);
  }


  //
  // Only for checking USB floppy
  //
  FileSystem = NULL;
  if (IsUsbDevice == TRUE && *BootType == L05_BOOT_TYPE_EFI_UNKNOWN) {
    Status = gBS->LocateProtocol (&gEfiUsbCoreProtocolGuid, NULL, &UsbCore);
    if (EFI_ERROR(Status)) {
      return Status;
    }

    Status = UsbCore->GetUsbDevices (
                        USB_CORE_USB_MASS_STORAGE,
                        &UsbMassDevicesCount,
                        (USB_DEVICE***) &UsbMassDevices
                        );
    if (EFI_ERROR(Status)) {
      return Status;
    }

    gBS->LocateHandleBuffer (
           ByProtocol,
           &gEfiUsbIoProtocolGuid,
           NULL,
           &HandleCount,
           &HandleBuffer
           );

    for (Index = 0; Index < HandleCount; Index ++)
    {
      gBS->HandleProtocol (
             HandleBuffer[Index],
             &gEfiUsbIoProtocolGuid,
             &UsbIoInstance
             );

      for (Index2 = 0; Index2 < UsbMassDevicesCount; Index2 ++)
      {
        if (UsbMassDevices[Index2]->UsbIo == UsbIoInstance) {

          Status = gBS->HandleProtocol (
                          HandleBuffer[Index],
                          &gEfiDevicePathProtocolGuid,
                          (VOID *) &DevicePath
                          );
          if (EFI_ERROR(Status)) {
            continue;
          }

          Status = gBS->HandleProtocol (
                          HandleBuffer[Index],
                          &gEfiSimpleFileSystemProtocolGuid,
                          (VOID *) &FileSystem
                          );
          if (EFI_ERROR(Status)) {
            continue;
          }

          DevicePathNode = DevicePathPtr;

          while (!IsDevicePathEnd (DevicePathNode)) {
            Found = FALSE;
            DevicePathTemp = DevicePath;
            while (!IsDevicePathEnd (DevicePathTemp)) {
              if (CompareMem (DevicePathNode, DevicePathTemp, *((UINT16 *)(DevicePathNode->Length))) == 0) {
                Found = TRUE;
                break;
              }
              DevicePathTemp = NextDevicePathNode (DevicePathTemp);
            }

            if (!Found) {
              break;
            }
            DevicePathNode = NextDevicePathNode (DevicePathNode);
          }

          if (Found) {
            if (UsbMassDevices[Index2]->Media->RemovableMedia &&
                      (UsbMassDevices[Index2]->Transport->Protocol == USB_MASS_STORE_CBI0 ||
                       UsbMassDevices[Index2]->Transport->Protocol == USB_MASS_STORE_CBI1)) {
              *BootType = L05_BOOT_TYPE_EFI_USB_FLOPPY;
            }
          }
        }
      }
    }
  }

  return Status;
}


EFI_STATUS
LcfcChgBootSetPxeToFirst (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
  )
{
  EFI_STATUS    Status                      = EFI_SUCCESS;
  UINT16        *BootOrderList              = NULL;
  UINTN         BootOrderSize               = 0;
  UINT8         BootOrderCount              = 0;
  UINT16        *TmpBootOrderList           = NULL;
  UINTN         TmpBootOrderSize            = 0;
  UINT8         TmpBootOrderCount           = 0;

  UINT16        *PhyBootOrderList           = NULL;
  UINTN         PhyBootOrderSize            = 0;
  UINT8         PhyBootOrderCount           = 0;
  UINT16        *TmpPhyBootOrderList        = NULL;
  UINTN         TmpPhyBootOrderSize         = 0;
  UINT8         TmpPhyBootOrderCount        = 0;

  CHAR16        BootString[10];
  UINT8         *Buffer                     = NULL;
  UINTN         BufferSize                  = 0;
  UINT8         *Ptr                        = NULL;
  UINT16        DevicePathSize              = 0;
  UINTN         Index                       = 0;
  UINTN         Index2                      = 0;
  BBS_TABLE     *BbsItem                    = NULL;
  UINT16        DeviceType                  = 0;
  CHAR16        *DeviceName                 = NULL;
  UINTN         DeviceNameSize              = 0;
  EFI_DEVICE_PATH_PROTOCOL   *DevicePathPtr = NULL;

  UINT8         BootOrderIndex              = 0x00;
  UINT8         TmpBootOrderIndex           = 0x00;
  UINT8         PhyBootOrderIndex           = 0x00;
  UINT8         TmpPhyBootOrderIndex        = 0x00;
  UINT8         UefiFirstIndex;
  UINT8         UefiLanIndex[2]; 
  UINT8         LegacyLanIndex;
  UINT8         LegacyFirstIndex;

  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  Status = gRT->GetVariable (
                  L"ChgBootSetPxeToFirst",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if(!EFI_ERROR (Status)) {
    *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm

    Status = gRT->SetVariable (
                    L"ChgBootSetPxeToFirst",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    );   
  } else {
    return EFI_UNSUPPORTED;
  }

  UefiFirstIndex              = 0xFF;
  UefiLanIndex[0]             = 0xFF;
  UefiLanIndex[1]             = 0xFF;
  LegacyLanIndex              = 0xFF;
  LegacyFirstIndex            = 0xFF;

  BootOrderList = LcfcBdsLibGetVariableAndSize (
                    L"BootOrder",
                    &gEfiGlobalVariableGuid,
                    &BootOrderSize
                    );

  if((BootOrderList == NULL) || (BootOrderSize == 0)) {
    return EFI_UNSUPPORTED;
  }

  PhyBootOrderList = LcfcBdsLibGetVariableAndSize (
                       L"PhysicalBootOrder",
                       &gEfiGenericVariableGuid,
                       &PhyBootOrderSize
                       );

  if((PhyBootOrderList == NULL) || (PhyBootOrderSize == 0)) {
    if(BootOrderList != NULL) {
      gBS->FreePool (BootOrderList);
      BootOrderList = NULL;
    }
    return EFI_UNSUPPORTED;
  }

  BootOrderCount = (UINT8)(BootOrderSize/sizeof(UINT16));

  for(BootOrderIndex = 0; BootOrderIndex < BootOrderCount; BootOrderIndex++) {
    ZeroMem(BootString, sizeof(BootString));
    CopyMem (BootString, L"Boot", 4 * sizeof (CHAR16));
    UnicodeValueToString (&(BootString[4]), PREFIX_ZERO | RADIX_HEX, BootOrderList[BootOrderIndex], 4);

    Buffer = LcfcBdsLibGetVariableAndSize (
                         BootString,
                         &gEfiGlobalVariableGuid,
                         &BufferSize
                         );

    if((Buffer == NULL) || (BufferSize == 0)) {
      continue;
    }

    Ptr = (UINT8 *) Buffer;
    Ptr += sizeof (UINT32);   // skip Load Option Attributes

    DevicePathSize = *((UINT16 *) Ptr);
    Ptr += sizeof (UINT16);   // skip DevicePathSize

    DeviceName = (CHAR16 *) Ptr;
    DeviceNameSize = StrSize ((UINT16 *) Ptr);

    Ptr += DeviceNameSize;   // skip Device Name String
    DevicePathPtr = (EFI_DEVICE_PATH_PROTOCOL*) Ptr;
    Ptr += DevicePathSize; // skip Device Path

    //
    //  Check if the device is EFI device or not.
    //
    Status = LcfcDevicePathToBootType (DevicePathPtr, &DeviceType);

    if (DeviceType == L05_BOOT_TYPE_NON_EFI) {    
      // Legacy Device
      BbsItem = (BBS_TABLE*)Ptr;
      DeviceType = BbsItem->DeviceType;

      Ptr += sizeof (BBS_TABLE);
      Index = *((UINT16*)Ptr);

      if(LegacyFirstIndex == 0xFF) {
        LegacyFirstIndex = BootOrderIndex;
      }    

      if((DeviceType == BBS_BEV_DEVICE) || (DeviceType == BBS_EMBED_NETWORK))  {
        for(Index2 = 0; Index2 < (DeviceNameSize - 2); Index2++) {
          if((DeviceName[Index2] == L'P') && (DeviceName[Index2 + 1] == L'X') && (DeviceName[Index2 + 2] == L'E')) {
            if(LegacyLanIndex == 0xFF) {
              LegacyLanIndex = BootOrderIndex;
              break;
            }
          }
        }
      }
    } else {
      // UEFI Device
      if(UefiFirstIndex == 0xFF) {
        UefiFirstIndex = BootOrderIndex;
      }

      for(Index2 = 0; Index2 < (sizeof(mLcfcDummyBootOptionTable)/sizeof(DUMMY_BOOT_OPTION_INFO)); Index2++) {
        if (StrnCmp (mLcfcDummyBootOptionTable[Index2].Description, DeviceName, StrLen (mLcfcDummyBootOptionTable[Index2].Description)) == 0) {
          if(BootOrderList[BootOrderIndex] == DummyNetwokrBootOptionNum) {
            if(UefiLanIndex[0] == 0xFF) {
              UefiLanIndex[0] = BootOrderIndex;
            }
          }
        }
      }
    }

    if(Buffer != NULL) {
      gBS->FreePool(Buffer);
      Buffer = NULL;
    }
  }

  TmpBootOrderList = NULL;
  TmpBootOrderSize = BootOrderSize;
  TmpBootOrderList = (UINT16 *)AllocateZeroPool (TmpBootOrderSize);

  if(TmpBootOrderList == NULL) {
    if(BootOrderList != NULL) {
      gBS->FreePool (BootOrderList);
      BootOrderList    = NULL;
    }

    if(PhyBootOrderList !=  NULL) {
      gBS->FreePool (PhyBootOrderList);
      PhyBootOrderList = NULL;
    }
    return EFI_UNSUPPORTED;
  }

  TmpBootOrderCount = (UINT8)(TmpBootOrderSize/sizeof(UINT16));
  BootOrderCount    = (UINT8)(BootOrderSize/sizeof(UINT16));

  for(TmpBootOrderIndex = 0; TmpBootOrderIndex < TmpBootOrderCount; TmpBootOrderIndex++) {
    for(BootOrderIndex = 0; BootOrderIndex < BootOrderCount; BootOrderIndex++) {

      if(BootOrderList[BootOrderIndex] == 0xFFFF) {
        continue;
      }

      if(TmpBootOrderIndex == UefiFirstIndex) {
        if(UefiLanIndex[0] != 0xFF) {
          TmpBootOrderList[TmpBootOrderIndex] = BootOrderList[UefiLanIndex[0]];
          BootOrderList[UefiLanIndex[0]]      = 0xFFFF;
          break;
        }
      }

      if(TmpBootOrderIndex == LegacyFirstIndex) {
        if(LegacyLanIndex != 0xFF) {
          TmpBootOrderList[TmpBootOrderIndex] = BootOrderList[LegacyLanIndex];
          BootOrderList[LegacyLanIndex]       = 0xFFFF;
          break;
        }
      }

      TmpBootOrderList[TmpBootOrderIndex]     = BootOrderList[BootOrderIndex];
      BootOrderList[BootOrderIndex]           = 0xFFFF;
      break;
    }
  }

  UefiFirstIndex              = 0xFF;
  UefiLanIndex[0]             = 0xFF;  // For IPv4 Network
  UefiLanIndex[1]             = 0xFF;  // For IPv6 Network
  LegacyLanIndex              = 0xFF;
  LegacyFirstIndex            = 0xFF;

  PhyBootOrderCount = (UINT8)(PhyBootOrderSize/sizeof(UINT16));

  for(PhyBootOrderIndex = 0; PhyBootOrderIndex < PhyBootOrderCount; PhyBootOrderIndex++) {
    ZeroMem(BootString, sizeof(BootString));
    CopyMem (BootString, L"Boot", 4 * sizeof (CHAR16));
    UnicodeValueToString (&(BootString[4]), PREFIX_ZERO | RADIX_HEX, PhyBootOrderList[PhyBootOrderIndex], 4);

    Buffer = LcfcBdsLibGetVariableAndSize (
                         BootString,
                         &gEfiGlobalVariableGuid,
                         &BufferSize
                         );

    if((Buffer == NULL) || (BufferSize == 0)) {
      continue;
    }

    Ptr = (UINT8 *) Buffer;
    Ptr += sizeof (UINT32);   // skip Load Option Attributes

    DevicePathSize = *((UINT16 *) Ptr);
    Ptr += sizeof (UINT16);   // skip DevicePathSize
    DeviceNameSize = StrSize ((UINT16 *) Ptr);

    DeviceName = (VOID*)Ptr;

    Ptr += DeviceNameSize;   // skip Device Name String
    DevicePathPtr = (VOID*)Ptr;
    Ptr += DevicePathSize; // skip Device Path

    //
    //  Check if the device is EFI device or not.
    //
    Status = LcfcDevicePathToBootType (DevicePathPtr, &DeviceType);

    if (DeviceType == L05_BOOT_TYPE_NON_EFI) {
      // Legacy Device      
      BbsItem = (BBS_TABLE*) Ptr;
      DeviceType = BbsItem->DeviceType;

      Ptr += sizeof (BBS_TABLE);
      Index = *((UINT16*)Ptr);

      if(LegacyFirstIndex == 0xFF) {
        LegacyFirstIndex = PhyBootOrderIndex;
      }

      if((DeviceType == BBS_BEV_DEVICE) || (DeviceType == BBS_EMBED_NETWORK))  {
        for(Index2 = 0; Index2 < (DeviceNameSize - 2); Index2++) {
          if((DeviceName[Index2] == L'P') && (DeviceName[Index2 + 1] == L'X') && (DeviceName[Index2 + 2] == L'E')) {
            if(LegacyLanIndex == 0xFF) {
              LegacyLanIndex = PhyBootOrderIndex;
              break;
            }
          }
        }
      }
    } else {
      // UEFI Device
      if(UefiFirstIndex == 0xFF) {
        UefiFirstIndex = PhyBootOrderIndex;
      }

      if(DeviceType == LCFC_BOOT_TYPE_EFI_NETWORK_IPV4) {
        if (UefiLanIndex[0] == 0xFF) {
          UefiLanIndex[0] = PhyBootOrderIndex;
        } 
      }

      if(DeviceType == LCFC_BOOT_TYPE_EFI_NETWORK_IPV6) {
        if (UefiLanIndex[1] == 0xFF) {
          UefiLanIndex[1] = PhyBootOrderIndex;
        }
      }
    }

    if(Buffer != NULL) {
      gBS->FreePool(Buffer);
      Buffer = NULL;
    }
  } 

  TmpPhyBootOrderList = NULL;
  TmpPhyBootOrderSize = PhyBootOrderSize;
  TmpPhyBootOrderList = (UINT16 *)AllocateZeroPool (TmpPhyBootOrderSize);

  if(TmpPhyBootOrderList == NULL) {
    if(BootOrderList != NULL) {
      gBS->FreePool (BootOrderList);
      BootOrderList    = NULL;
    }

    if(TmpBootOrderList != NULL) {
      gBS->FreePool (TmpBootOrderList);
      TmpBootOrderList = NULL;
    }

    if(PhyBootOrderList != NULL) {
      gBS->FreePool (PhyBootOrderList);
      PhyBootOrderList = NULL;
    }
    return EFI_UNSUPPORTED;
  }

  TmpPhyBootOrderCount = (UINT8)(TmpPhyBootOrderSize/sizeof(UINT16));
  PhyBootOrderCount    = (UINT8)(PhyBootOrderSize/sizeof(UINT16));

  for(TmpPhyBootOrderIndex = 0; TmpPhyBootOrderIndex < TmpPhyBootOrderCount; TmpPhyBootOrderIndex++) {
    for(PhyBootOrderIndex = 0; PhyBootOrderIndex < PhyBootOrderCount; PhyBootOrderIndex++) {

      if(PhyBootOrderList[PhyBootOrderIndex] == 0xFFFF) {
        continue;
      }

      if(TmpPhyBootOrderIndex == UefiFirstIndex) {
        if(SetupVariable->L05PxeIpv4First == 1) {
          if(UefiLanIndex[0] != 0xFF) {
            TmpPhyBootOrderList[TmpPhyBootOrderIndex] = PhyBootOrderList[UefiLanIndex[0]];
            PhyBootOrderList[UefiLanIndex[0]]         = 0xFFFF;
            break;
          }
        } else {
          if(UefiLanIndex[1] != 0xFF) {
            TmpPhyBootOrderList[TmpPhyBootOrderIndex] = PhyBootOrderList[UefiLanIndex[1]];
            PhyBootOrderList[UefiLanIndex[1]]         = 0xFFFF;
            break;
          }
        }
      }

      if(TmpPhyBootOrderIndex == (UefiFirstIndex + 1)) {
        if(SetupVariable->L05PxeIpv4First == 0) {
          if(UefiLanIndex[0] != 0xFF) {
            TmpPhyBootOrderList[TmpPhyBootOrderIndex] = PhyBootOrderList[UefiLanIndex[0]];
            PhyBootOrderList[UefiLanIndex[0]]         = 0xFFFF;
            break;
          }
        } else {
          if(UefiLanIndex[1] != 0xFF) {
            TmpPhyBootOrderList[TmpPhyBootOrderIndex] = PhyBootOrderList[UefiLanIndex[1]];
            PhyBootOrderList[UefiLanIndex[1]]         = 0xFFFF;
            break;
          }
        }
      }

      if(TmpPhyBootOrderIndex == LegacyFirstIndex) {
        if(LegacyLanIndex != 0xFF) {
          TmpPhyBootOrderList[TmpPhyBootOrderIndex]     = PhyBootOrderList[LegacyLanIndex];
          PhyBootOrderList[LegacyLanIndex]              = 0xFFFF;
          break;
        }
      }

      TmpPhyBootOrderList[TmpPhyBootOrderIndex]       = PhyBootOrderList[PhyBootOrderIndex];
      PhyBootOrderList[PhyBootOrderIndex]             = 0xFFFF;
      break;
    }
  }

  Status = gRT->SetVariable (
                  L"BootOrder",
                  &gEfiGlobalVariableGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  TmpBootOrderSize,
                  TmpBootOrderList
                  );

  Status = gRT->SetVariable (
                  L"PhysicalBootOrder",
                  &gEfiGenericVariableGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  TmpPhyBootOrderSize,
                  TmpPhyBootOrderList
                  );

  if(BootOrderList != NULL) {
    gBS->FreePool (BootOrderList);
    BootOrderList       = NULL;
  }

  if(TmpBootOrderList != NULL) {
    gBS->FreePool (TmpBootOrderList);
    TmpBootOrderList    = NULL;
  }

  if(PhyBootOrderList != NULL) {
    gBS->FreePool (PhyBootOrderList);
    PhyBootOrderList    = NULL;
  }

  if(TmpPhyBootOrderList != NULL) {
    gBS->FreePool (TmpPhyBootOrderList);
    TmpPhyBootOrderList = NULL;
  }

  return Status;
}


EFI_STATUS
LcfcChgBootSetEfiPxeOneTime (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
  )
{
  EFI_STATUS    Status                      = EFI_SUCCESS;

  UINT16        *PhyBootOrderList           = NULL;
  UINTN         PhyBootOrderSize            = 0;
  UINT8         PhyBootOrderCount           = 0;
  UINT8         PhyBootOrderIndex           = 0;
  
  
  UINT16        *LfcBootNext                = NULL;
  UINTN         LfcBootNextSize             = 0;

  CHAR16        BootString[10];
  UINT8         *Buffer                     = NULL;
  UINTN         BufferSize                  = 0;
  UINT8         *Ptr                        = NULL;
  UINT16        DevicePathSize              = 0;
  UINT16        DeviceType                  = 0;
  CHAR16        *DeviceName                 = NULL;
  UINTN         DeviceNameSize              = 0;
  EFI_DEVICE_PATH_PROTOCOL   *DevicePathPtr = NULL;
  
  UINT8         ChgBootDefaultTypeData      = 0;
  UINTN         ChgBootDefaultTypeDataSize  = sizeof (ChgBootDefaultTypeData);

  UINT8         UefiLanIndex[2];

  UefiLanIndex[0] = 0xFF;       // For EFI IpV4
  UefiLanIndex[1] = 0xFF;       // For EFI IpV6

  PhyBootOrderList = LcfcBdsLibGetVariableAndSize (
                       L"PhysicalBootOrder",
                       &gEfiGenericVariableGuid,
                       &PhyBootOrderSize
                       );

  if((PhyBootOrderList == NULL) || (PhyBootOrderSize == 0)) {
    return EFI_UNSUPPORTED;
  }
  
  Status = gRT->GetVariable (
                  L"ChgBootSetEfiPxeOneTime",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if(!EFI_ERROR (Status)) {
    gRT->SetVariable (
           L"ChgBootSetEfiPxeOneTime",
           &gLfcVariableGuid,
           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
           0,
           &ChgBootDefaultTypeData
           );   
  }
  
  //BootType = 0:Dual Boot, 2:EFI Boot
  if(EFI_ERROR (Status) || (SetupVariable->BootType != 2)) {
    if(PhyBootOrderList != NULL) {
      gBS->FreePool (PhyBootOrderList);
      PhyBootOrderList    = NULL;
    }
    return EFI_UNSUPPORTED;
  }
  
  PhyBootOrderCount = (UINT8)(PhyBootOrderSize/sizeof(UINT16));

  for(PhyBootOrderIndex = 0; PhyBootOrderIndex < PhyBootOrderCount; PhyBootOrderIndex++) {
    ZeroMem(BootString, sizeof(BootString));
    CopyMem (BootString, L"Boot", 4 * sizeof (CHAR16));
    UnicodeValueToString (&(BootString[4]), PREFIX_ZERO | RADIX_HEX, PhyBootOrderList[PhyBootOrderIndex], 4);

    Buffer = LcfcBdsLibGetVariableAndSize (
                         BootString,
                         &gEfiGlobalVariableGuid,
                         &BufferSize
                         );

    if((Buffer == NULL) || (BufferSize == 0)) {
      continue;
    }

    Ptr = (UINT8 *) Buffer;
    Ptr += sizeof (UINT32);   // skip Load Option Attributes

    DevicePathSize = *((UINT16 *) Ptr);
    Ptr += sizeof (UINT16);   // skip DevicePathSize
    DeviceNameSize = StrSize ((UINT16 *) Ptr);

    DeviceName = (VOID*)Ptr;

    Ptr += DeviceNameSize;   // skip Device Name String
    DevicePathPtr = (VOID*)Ptr;
    Ptr += DevicePathSize; // skip Device Path

    //
    //  Check if the device is EFI device or not.
    //
    Status = LcfcDevicePathToBootType (DevicePathPtr, &DeviceType);

    // UEFI Device
    if (DeviceType != L05_BOOT_TYPE_NON_EFI) {

      if(DeviceType == LCFC_BOOT_TYPE_EFI_NETWORK_IPV4) {
        if (UefiLanIndex[0] == 0xFF) {
          UefiLanIndex[0] = PhyBootOrderIndex;
        } 
      }

      if(DeviceType == LCFC_BOOT_TYPE_EFI_NETWORK_IPV6) {
        if (UefiLanIndex[1] == 0xFF) {
          UefiLanIndex[1] = PhyBootOrderIndex;
        }
      }
    }

    if(Buffer != NULL) {
      gBS->FreePool(Buffer);
      Buffer = NULL;
    }
  } 

  if((UefiLanIndex[0] != 0xFF) || (UefiLanIndex[1] != 0xFF)) {

    LfcBootNext = LcfcBdsLibGetVariableAndSize (
                         EFI_BOOT_NEXT_VARIABLE_NAME,
                         &gEfiGlobalVariableGuid,
                         &LfcBootNextSize
                         );

    if(LfcBootNext != NULL) {
      gBS->FreePool(LfcBootNext);
      LfcBootNextSize = 0;
      LfcBootNext = NULL;

      Status = gRT->SetVariable (
                      EFI_BOOT_NEXT_VARIABLE_NAME,
                      &gEfiGlobalVariableGuid,
                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                      LfcBootNextSize,
                      LfcBootNext
                      );
    }

    LfcBootNext     = (UINT16 *)AllocateZeroPool (sizeof(UINT16));
    LfcBootNextSize = sizeof(UINT16);

    if(LfcBootNext != NULL) {
      if(SetupVariable->L05PxeIpv4First == 1) {
        // IPv4 First
        if((UefiLanIndex[1] != 0xFF)) {
          *LfcBootNext = PhyBootOrderList[UefiLanIndex[1]];
       }
        if((UefiLanIndex[0] != 0xFF)) {
            *LfcBootNext = PhyBootOrderList[UefiLanIndex[0]];
        }
      } else {
        // IPv6 First
        if((UefiLanIndex[0] != 0xFF)) {
          *LfcBootNext = PhyBootOrderList[UefiLanIndex[0]];
        }
        if((UefiLanIndex[1] != 0xFF)) {
          *LfcBootNext = PhyBootOrderList[UefiLanIndex[1]];
        }
      }

      Status = gRT->SetVariable (
                      EFI_BOOT_NEXT_VARIABLE_NAME,
                      &gEfiGlobalVariableGuid,
                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                      LfcBootNextSize,
                      LfcBootNext
                      );

      gBS->FreePool (LfcBootNext);
      LfcBootNext         = NULL;
    }
  }

  if(PhyBootOrderList != NULL) {
    gBS->FreePool (PhyBootOrderList);
    PhyBootOrderList    = NULL;
  }

  return Status;
}


VOID
EFIAPI
LcfcRebootCallBack(
  IN EFI_EVENT                           Event,
  IN VOID                                *Context
  )
{
  UINT8               *pRebootType = NULL;
  UINT8                RebootType  = 0;

  gBS->CloseEvent (Event);

  pRebootType = (UINT8 *) Context;

  if(pRebootType == NULL) {
    return;
  }

  RebootType = *pRebootType;

  if(pRebootType != NULL) {
    gBS->FreePool (pRebootType);
    pRebootType = NULL;
  }

  LfcEcLibEnableEcPower ();

  // 1: EfiResetCold;  2: EfiResetWarm;
  switch(RebootType) {
    case 1:
      gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
      break;
    case 2:
      gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
      break;
    default:
      // Do Nothing
      break;
  }

}


EFI_STATUS
LcfcChangeSecureBootStatusCallBack (
  IN EFI_EVENT                           Event,
  IN VOID                                *Context
  )
{
  EFI_STATUS                             Status;
  EFI_L05_SECURE_BOOT_DATA               *MySecureBootData;
  EFI_L05_SECURE_BOOT_PROTOCOL           *L05SecureBootPtr = NULL;
  MySecureBootData = (EFI_L05_SECURE_BOOT_DATA *) Context;

  Status = gBS->LocateProtocol (
                  &gEfiL05SecureBootProtocolGuid,
                  NULL,
                  &L05SecureBootPtr
                  );

  if (!EFI_ERROR (Status)) {
    Status = L05SecureBootPtr->L05SecureBootCallback (MySecureBootData);
  }

  if(MySecureBootData) {
    gBS->FreePool (MySecureBootData);
    MySecureBootData = NULL;
  }

  return Status;
}


EFI_STATUS
LcfcChangeSecureBootStatus ( 
  IN EFI_L05_SECURE_BOOT_DATA            *MySecureBootData
  )
{
  EFI_STATUS                             Status;
  EFI_EVENT                              Event = NULL;
  VOID                                   *Registration = NULL;
  EFI_L05_SECURE_BOOT_PROTOCOL           *L05SecureBootPtr = NULL;
//  EFI_EVENT                              BdsCpInitEvent         = NULL;
//  VOID                                   *BdsCpInitRegistration = NULL;
//  
//  Status = gBS->CreateEvent (
//                  EVT_NOTIFY_SIGNAL,
//                  TPL_CALLBACK,
//                  LcfcChangeSecureBootStatusCallBack,
//                  MySecureBootData,
//                  &BdsCpInitEvent
//                  );
//
//  Status = gBS->RegisterProtocolNotify (
//                  &gH2OBdsCpInitProtocolGuid,
//                  BdsCpInitEvent,
//                  (VOID **) &BdsCpInitRegistration
//                  );

  Status = gBS->LocateProtocol (
                  &gEfiL05SecureBootProtocolGuid,
                  NULL,
                  &L05SecureBootPtr
                  );

  if (!EFI_ERROR (Status)) { 
    Status = L05SecureBootPtr->L05SecureBootCallback (MySecureBootData);
  } else {
    // Register Event for change secure boot status if gEfiL05SecureBootProtocolGuid is not ready yet.
    Status = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    LcfcChangeSecureBootStatusCallBack,
                    MySecureBootData,
                    &Event
                    );
    if (!EFI_ERROR (Status)) {
      Status = gBS->RegisterProtocolNotify (
                      &gEfiL05SecureBootProtocolGuid,
                      Event,
                      &Registration
                      ); 
    }
  }

  return Status;
}


EFI_STATUS
LcfcSecureBootRestoreFactory (
  IN OUT SYSTEM_CONFIGURATION            *SetupVariable
  )
{
  EFI_STATUS                             Status;
  EFI_L05_SECURE_BOOT_DATA               *MySecureBootData = NULL;

  MySecureBootData = (EFI_L05_SECURE_BOOT_DATA *) AllocateZeroPool (sizeof (EFI_L05_SECURE_BOOT_DATA));

  if(MySecureBootData == NULL) {
    return EFI_UNSUPPORTED;
  }

  SetupVariable->L05SecureBoot  = 1; // 0:Disable,   1:Enable

  MySecureBootData->Action = L05_SECURE_BOOT_ENABLE;
  MySecureBootData->RestoreFactoryKeys = L05_SECURE_BOOT_ENABLE;
  Status = LcfcChangeSecureBootStatus (MySecureBootData);

  return Status;
}


EFI_STATUS
LcfcSecureBootEnable (
    IN OUT SYSTEM_CONFIGURATION            *SetupVariable
  )
{
  EFI_STATUS                             Status;
  EFI_L05_SECURE_BOOT_DATA               *MySecureBootData = NULL;

  MySecureBootData = (EFI_L05_SECURE_BOOT_DATA *) AllocateZeroPool (sizeof (EFI_L05_SECURE_BOOT_DATA));

  if(MySecureBootData == NULL) {
    return EFI_UNSUPPORTED;
  }

  SetupVariable->L05SecureBoot  = 1; // 0:Disable,   1:Enable

  MySecureBootData->Action = L05_SECURE_BOOT_ENABLE;
  Status = LcfcChangeSecureBootStatus (MySecureBootData);

  return Status;
}


EFI_STATUS
LcfcSecureBootDisable (
    IN OUT SYSTEM_CONFIGURATION            *SetupVariable
  )
{
  EFI_STATUS                             Status;
  EFI_L05_SECURE_BOOT_DATA               *MySecureBootData = NULL;

  MySecureBootData = (EFI_L05_SECURE_BOOT_DATA *) AllocateZeroPool (sizeof (EFI_L05_SECURE_BOOT_DATA));

  if(MySecureBootData == NULL) {
    return EFI_UNSUPPORTED;
  }

  SetupVariable->L05SecureBoot  = 0; // 0:Disable,   1:Enable

  MySecureBootData->Action = L05_SECURE_BOOT_DISABLE;
  Status = LcfcChangeSecureBootStatus (MySecureBootData);

  return Status;
}


EFI_STATUS
LcfcChgBootUefiLoadDefault (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType,
  IN OUT UINT8                     *pLcfcChangeSecureBootReason
  )
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  Status = gRT->GetVariable (
                  L"ChgBootUefiLoadDefault",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if (!EFI_ERROR (Status)) {  
    // ChgBoot tool set Win8 default
    SetupVariable->L05DefaultType = 0;
    *pLcfcChangeSecureBootReason  = LcfcSetChgboot;

    Status = LcfcBootOrderSetDefault ();

    Status = gRT->SetVariable (
                    L"ChgBootUefiLoadDefault",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    ); //Delete it

    *pRebootType = 1 ; // 1: EfiResetCold;  2: EfiResetWarm
  }

  return Status;
}


EFI_STATUS
LcfcChgBootLegacyLoadDefault (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType,
  IN OUT UINT8                     *pLcfcChangeSecureBootReason
 )
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  Status = gRT->GetVariable (
                  L"ChgBootLegacyLoadDefault",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if (!EFI_ERROR (Status)) {
    // ChgBoot tool set legacy default
    SetupVariable->L05DefaultType = 1;
    *pLcfcChangeSecureBootReason  = LcfcSetChgboot;

    Status = LcfcBootOrderSetDefault();

    Status = gRT->SetVariable (
                    L"ChgBootLegacyLoadDefault",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    ); //Delete it

    *pRebootType = 1 ; // 1: EfiResetCold;  2: EfiResetWarm
  }

  return Status;
}


EFI_STATUS
LcfcChgBootSecureBootEnable (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType 
)
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  //If UEFI Mode do this, others do nothing
  if (SetupVariable->BootType != 2) {
    return EFI_UNSUPPORTED;
  }
  // Change Boot: Secure Boot Enable 
   Status = gRT->GetVariable (
                   L"ChgBootSecureBootEnable",
                   &gLfcVariableGuid,
                   NULL,
                   &ChgBootDefaultTypeDataSize,
                   &ChgBootDefaultTypeData
                   );

   if (!EFI_ERROR (Status)) {
     Status = LcfcSecureBootEnable (SetupVariable);

     if (!EFI_ERROR (Status)) {
       *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm
     }

     Status = gRT->SetVariable (
                     L"ChgBootSecureBootEnable",
                     &gLfcVariableGuid,
                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                     0,
                     &ChgBootDefaultTypeData
                     );
   }

   return Status;
}


EFI_STATUS
LcfcChgBootSecureBootDisable (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType 
  )
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);
  EFI_GUID                               LvarMfgModeFlagGuid = LVAR_MFG_MODE_FLAG_GUID;
  UINT8                                  MfgModeFlag =  0;
  UINT32                                 MfgModeFlagDataSize = 1;
  LENOVO_VARIABLE_PROTOCOL               *LenovoVariable;

  //If UEFI Mode do this, others do nothing
  if (SetupVariable->BootType != 2) {
    return EFI_UNSUPPORTED;
  }
	//Check MfgMod
	Status = gBS->LocateProtocol (&gLenovoVariableProtocolGuid, NULL, &LenovoVariable);
	if (!EFI_ERROR (Status)) {
    Status = LenovoVariable->GetVariable (
                               LenovoVariable, 
                               &LvarMfgModeFlagGuid, 
                               &MfgModeFlagDataSize, 
                               (VOID*) (&MfgModeFlag)
                               ) ;
  }
  // Change Boot: Secure Boot Disable 
  Status = gRT->GetVariable (
                  L"ChgBootSecureBootDisable",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

   if (!EFI_ERROR (Status) && (MfgModeFlag == 'Y')) {//Only chgboot command and Mfgmode both exist, do following thing.
     Status = LcfcSecureBootDisable (SetupVariable);

     if (!EFI_ERROR (Status)) {
       *pRebootType  = 1; // 1: EfiResetCold;  2: EfiResetWarm
     }

     Status = gRT->SetVariable (
                     L"ChgBootSecureBootDisable",
                     &gLfcVariableGuid,
                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                     0,
                     &ChgBootDefaultTypeData
                     );
   }

   return Status;
}


EFI_STATUS
LcfcChgBootRestoreFactory (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
  )
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  //If UEFI Mode do this, others do nothing
  if (SetupVariable->BootType != 2) {
    return EFI_UNSUPPORTED;
  }
  // Change Boot: Secure Boot Restore Factory Key 
  Status = gRT->GetVariable (
                  L"ChgBootRestoreFactory",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if (!EFI_ERROR (Status)) {
     Status = LcfcSecureBootRestoreFactory(SetupVariable);

     if (!EFI_ERROR (Status)) {
       *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm
     }

     Status = gRT->SetVariable (
                     L"ChgBootRestoreFactory",
                     &gLfcVariableGuid,
                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                     0,
                     &ChgBootDefaultTypeData
                     );
   }

   return Status;
}


EFI_STATUS
LcfcChgBootChangeLegacy (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
)
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  // Change Boot: Change to Legacy and Secure Boot Disable
  Status = gRT->GetVariable (
                  L"ChgBootChangeLegacy",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if (!EFI_ERROR (Status)) {
    Status = LcfcSecureBootDisable (SetupVariable);

    if (!EFI_ERROR (Status)) {
      *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm
    }

    Status = gRT->SetVariable (
                    L"ChgBootChangeLegacy",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    );
  }

  return Status;
}

EFI_STATUS
LcfcChgBootBootOrderSetDefault (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
)
{
  EFI_STATUS  Status;

  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);
 
  // Change Boot: Change to Legacy and Secure Boot Disable
  Status = gRT->GetVariable (
                  L"ChgBootBootOrderSetDefault",
                  &gLfcVariableGuid,
                  NULL,
                  &ChgBootDefaultTypeDataSize,
                  &ChgBootDefaultTypeData
                  );

  if (!EFI_ERROR (Status)) {
   *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm

    LcfcBootOrderSetDefault();

    Status = gRT->SetVariable (
                    L"ChgBootBootOrderSetDefault",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    );
  }

  return Status;
}

EFI_STATUS
LcfcChgBootFullReset (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
  )
{
  EFI_STATUS  Status;
  UINT8       ChgBootDefaultTypeData = 0;
  UINTN       ChgBootDefaultTypeDataSize = sizeof (ChgBootDefaultTypeData);

  Status = gRT->GetVariable (
                L"ChgBootFullReset",
                &gLfcVariableGuid,
                NULL,
                &ChgBootDefaultTypeDataSize,
                &ChgBootDefaultTypeData
                );

  if(!EFI_ERROR (Status)) {
    *pRebootType = 1; // 1: EfiResetCold;  2: EfiResetWarm

    Status = gRT->SetVariable (
                    L"ChgBootFullReset",
                    &gLfcVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    0,
                    &ChgBootDefaultTypeData
                    );
  }

  return Status;
}

EFI_STATUS
LcfcLvarShowComputraceHelpString (
  IN OUT SYSTEM_CONFIGURATION      *SetupVariable,
  IN OUT UINT8                     *pRebootType
  )
{
  EFI_STATUS  Status;
  EFI_GUID                               ComputraceStringFlagGuid = LVAR_COMPUTRACE_STRING_FLAG_GUID;
  UINT32                                 ComputraceStringFlagSize = 1;
  UINT8                                  ComputraceStringFlag = 0;
  LENOVO_VARIABLE_PROTOCOL               *LenovoVariable;

  if(SetupVariable->ComputraceState == 0) {
    return EFI_UNSUPPORTED;
  }

  Status = gBS->LocateProtocol (&gLenovoVariableProtocolGuid, NULL, &LenovoVariable);

  if (EFI_ERROR (Status)) {
    return Status;
  }

  ComputraceStringFlagSize = sizeof(UINT8);
  Status = LenovoVariable->GetVariable (
                             LenovoVariable,
                             &ComputraceStringFlagGuid,
                             &ComputraceStringFlagSize,
                             &ComputraceStringFlag
                             );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  // ComputraceStringFlag: 1 (Hide Computrace String)
  // ComputraceStringFlag: 0 (Show Computrace String)
  SetupVariable->L05ShowComputraceHelpStringInScu = !ComputraceStringFlag;

  return Status;
}

EFI_STATUS
LcfcOverrideDefaultSetupSetting (
  IN OUT UINT8                           *SetupBuffer
  )
{
  EFI_STATUS                Status;
  SYSTEM_CONFIGURATION      *SetupVariable;
  UINT8                     FirstBootFlag;
  UINTN                     FirstBootFlagSize;
  UINT8                     OriginalBootType;
  LENOVO_VARIABLE_PROTOCOL  *LenovoVariable = NULL;
  EFI_GUID                  VarGuid = LVAR_64BIT_CONFIGURATION_GUID;
  UINT64                    ConfigurationVariable = 0;
  UINT32                    ConfigVarDataSize = sizeof (ConfigurationVariable);
  EFI_EVENT                 EndOfDxeEvent = NULL;
  UINT8                     *pRebootType  = NULL;
  // 1: First Boot; 2: Set Change Boot CMD; 3: F9 Load Default;
  UINT8                     LcfcChangeSecureBootReason = LcfcF9LoadDefault;

  Status = mOrgOverrideDefaultSetupSetting(SetupBuffer);

  // 1: EfiResetCold;  2: EfiResetWarm
  pRebootType = (UINT8 *) AllocateZeroPool (sizeof (UINT8));

  if(pRebootType == NULL) {
    return EFI_UNSUPPORTED;
  }

  SetupVariable = (SYSTEM_CONFIGURATION *) SetupBuffer;

  OriginalBootType = SetupVariable->L05DefaultType;

  Status = gBS->LocateProtocol (
                  &gLenovoVariableProtocolGuid, 
                  NULL, 
                  &LenovoVariable
                  );

  FirstBootFlagSize = sizeof (FirstBootFlag);
  Status = gRT->GetVariable (
                  L"CheckFirstBoot",
                  &gEfiGenericVariableGuid,
                  NULL,
                  &FirstBootFlagSize,
                  &FirstBootFlag
                  );

  // First Boot, FirstBootFlag didn't exist in NVRam.
  if (Status == EFI_NOT_FOUND) {
    // First Boot after flash BIOS
    LcfcChangeSecureBootReason    = LcfcFirstBoot;
    Status = gRT->SetVariable (
                    L"CheckFirstBoot",
                    &gEfiGenericVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    sizeof (FirstBootFlag),
                    &FirstBootFlag
                    );

  } else if (Status == EFI_SUCCESS) {
    // F9 or Chgboot command
    Status = LcfcChgBootUefiLoadDefault (SetupVariable, pRebootType, &LcfcChangeSecureBootReason);

    Status = LcfcChgBootLegacyLoadDefault (SetupVariable, pRebootType, &LcfcChangeSecureBootReason);


  if (SetupVariable->L05DefaultType == 0) {
    //
    // UEFI O.S
    //
    SetupVariable->BootType         = 2; // 0:Dual Boot, 2:EFI Boot
    //SetupVariable->L05FastBoot      = 1; // Fast Boot   = Enabled    
    SetupVariable->L05BiosBackFlash = 0; // Back Flash  = Disabled

    // 1: If Change Boot CMD enable SecureBoot; 2: F9 Load Default skip this;
    if (LcfcChangeSecureBootReason != LcfcF9LoadDefault) {
      Status = LcfcSecureBootEnable (SetupVariable);
    }

  } else if (SetupVariable->L05DefaultType == 1) {
    //
    //  Legacy O.S.
    //
    SetupVariable->BootType         = 0; // 0:Dual Boot, 2:EFI Boot
    //SetupVariable->L05FastBoot      = 0; // Fast Boot   = Disabled    
    SetupVariable->L05BiosBackFlash = 0; // Back Flash  = Disabled

    // 1: If Change Boot CMD disable SecureBoot; 2: F9 Load Default skip this;
    if (LcfcChangeSecureBootReason != LcfcF9LoadDefault) {
      Status = LcfcSecureBootDisable (SetupVariable);
    }

    }

  }

  SetupVariable->L05DefaultType = OriginalBootType;

  if (LenovoVariable != NULL) {
    ConfigurationVariable = 0;
    ConfigVarDataSize     = sizeof (ConfigurationVariable);

    Status = LenovoVariable->GetVariable (
                               LenovoVariable,
                               &VarGuid,
                               &ConfigVarDataSize,
                               &ConfigurationVariable
                               );
    if (!EFI_ERROR(Status)) {
      switch ((ConfigurationVariable & LVAR_TPM_MASK) >> 8) {
        case LVAR_TPM_DISPLAY_ENABLE:
          SetupVariable->L05OdmTpmImplement = 1;
          #if (FixedPcdGet8 (PcdPlatformIntelOrAmd) == 1)
            SetupVariable->PTTEnable = 1;
          #else
            SetupVariable->EnableTPM = 2;
          #endif
          break;

        case LVAR_TPM_DISPLAY_DISABLE:
          SetupVariable->L05OdmTpmImplement = 1;
          #if (FixedPcdGet8 (PcdPlatformIntelOrAmd) == 1)
            SetupVariable->PTTEnable = 0;
          #else
            SetupVariable->EnableTPM = 0;
          #endif
          break;

        case LVAR_TPM_HIDE_ENABLE:
          SetupVariable->L05OdmTpmImplement = 0;  // Attention!!! If hidden, maybe always be disabled by L05, need double confirm!!!!
          #if (FixedPcdGet8 (PcdPlatformIntelOrAmd) == 1)
            SetupVariable->PTTEnable = 1;
          #else
            SetupVariable->EnableTPM = 2;
          #endif
          break;

        case LVAR_TPM_HIDE_DISABLE:
          SetupVariable->L05OdmTpmImplement = 0;
          #if (FixedPcdGet8 (PcdPlatformIntelOrAmd) == 1)
            SetupVariable->PTTEnable = 0;
          #else
            SetupVariable->EnableTPM = 0;
          #endif
          break;

        case LVAR_TPM_DO_NOTHING:
        default:
          // Do Nothing
          break;
      }
    }
  }

  if (*pRebootType) {
    Status = gBS->CreateEventEx (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    LcfcRebootCallBack,
                    pRebootType,
                    &gEfiEndOfDxeEventGroupGuid,
                    &EndOfDxeEvent
                    );
  } else {
    if(pRebootType != NULL) {
      gBS->FreePool (pRebootType);
      pRebootType = NULL;
    }
  }

  return Status;
}


EFI_STATUS
LcfcOverrideSetupSettingDuringPost (
  IN OUT UINT8                           *SetupBuffer
  )
{
  EFI_STATUS                Status;
  SYSTEM_CONFIGURATION      *SetupVariable;
  EFI_EVENT                 EndOfDxeEvent = NULL; 
  UINT8                     *pRebootType  = NULL; // 1: EfiResetCold;  2: EfiResetWarm

  Status = mOrgOverrideSetupSettingDuringPost (SetupBuffer);
  
  // 1: EfiResetCold;  2: EfiResetWarm
  pRebootType = (UINT8 *) AllocateZeroPool (sizeof (UINT8));
  
  if(pRebootType == NULL) {
    return EFI_UNSUPPORTED;
  }

  SetupVariable = (SYSTEM_CONFIGURATION *) SetupBuffer;

  Status = LcfcChgBootSecureBootEnable (SetupVariable, pRebootType);

  Status = LcfcChgBootSecureBootDisable (SetupVariable, pRebootType);

  Status = LcfcChgBootRestoreFactory (SetupVariable, pRebootType);

  Status = LcfcChgBootChangeLegacy (SetupVariable, pRebootType);

  Status = LcfcChgBootBootOrderSetDefault (SetupVariable, pRebootType);

  Status = LcfcChgBootSetPxeToFirst (SetupVariable, pRebootType);

  Status = LcfcChgBootSetEfiPxeOneTime (SetupVariable, pRebootType);

  Status = LcfcLvarShowComputraceHelpString(SetupVariable, pRebootType);

  Status = LcfcChgBootFullReset (SetupVariable, pRebootType);

  if(*pRebootType) {
    Status = gBS->CreateEventEx (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    LcfcRebootCallBack,
                    pRebootType,
                    &gEfiEndOfDxeEventGroupGuid,
                    &EndOfDxeEvent
                    );
  } else {
    if(pRebootType != NULL) {
      gBS->FreePool (pRebootType);
      pRebootType = NULL;
    }
  }

  return Status;
}

EFI_STATUS
LcfcOverrideDefaultPchSetupSetting (
  IN OUT UINT8                          *SetupBuffer,
  IN OUT UINT8                          *PchSetupBuffer
  )
{
  EFI_STATUS                Status;
  Status = mOrgOverrideDefaultPchSetupSetting(SetupBuffer,PchSetupBuffer);
#if (FixedPcdGet32 (PcdL05ChipsetName) == L05_CHIPSET_NAME_CANNONLAKE)
  SYSTEM_CONFIGURATION                  *SetupVariable;
  PCH_SETUP                             *PchSetup;
#ifdef L05_BIOS_OPTANE_SUPPORT_ENABLE
  //  EFI_STATUS                            Status;
  UINTN                                 BufferSize;
  L05_BACKUP_SATAMODE_ITEM                 L05BackupSetupItem;
  UINT32                                L05BackupSetupItemSize;
  UINT8                     FirstPchBootFlag;
  UINTN                     FirstPchBootFlagSize;
#endif

  SetupVariable = (SYSTEM_CONFIGURATION *) SetupBuffer;
  PchSetup      = (PCH_SETUP *) PchSetupBuffer;
/*
#ifdef L05_BIOS_OPTANE_SUPPORT_ENABLE
//
// According to Lenovo request, if system is in RST mode, after flash BIOS, should keep RST mode, unless user set it to AHCI
//
  EFI_GUID                  SataModeVarGuid = LVAR_SATAMODE_STRING_FLAG_GUID;
  LENOVO_VARIABLE_PROTOCOL  *LenovoVariable = NULL;
  BufferSize = sizeof (L05_BACKUP_SATAMODE_ITEM);
  L05BackupSetupItemSize = sizeof (L05_BACKUP_SATAMODE_ITEM);

  Status = gRT->GetVariable (
                  L"CheckPchFirstBoot",
                  &gEfiGenericVariableGuid,
                  NULL,
                  &FirstPchBootFlagSize,
                  &FirstPchBootFlag
                  );
  if (Status == EFI_NOT_FOUND) {
    Status = gBS->LocateProtocol (
                    &gLenovoVariableProtocolGuid, 
                    NULL, 
                    &LenovoVariable
                    );
    if (LenovoVariable != NULL) {

      Status = LenovoVariable->GetVariable (
                                 LenovoVariable,
                                 &SataModeVarGuid,
                                 &L05BackupSetupItemSize,
                                 &L05BackupSetupItem
                                 );
      if (!EFI_ERROR (Status)) {
        PchSetup->SataInterfaceMode   = L05BackupSetupItem.SataInterfaceMode;
        PchSetup->SataRaidR0          = L05BackupSetupItem.SataRaidR0;
        PchSetup->SataRaidR1          = L05BackupSetupItem.SataRaidR1;
        PchSetup->SataRaidR5          = L05BackupSetupItem.SataRaidR5;
        PchSetup->SataRaidR10         = L05BackupSetupItem.SataRaidR10;
        CopyMem (PchSetup->RstPcieRemapEnabled, L05BackupSetupItem.RstPcieRemapEnabled, PCH_MAX_PCIE_ROOT_PORTS);
        PchSetup->SataRstOptaneMemory = L05BackupSetupItem.SataRstOptaneMemory;
      //TG support
      //PchSetup->TetonGlacierMode  = L05BackupSetupItem.TetonGlacierMode;
      }
      else {
        LenovoVariable->SetVariable (
                          LenovoVariable, 
                          &SataModeVarGuid, 
                          L05BackupSetupItemSize,
                          &L05BackupSetupItem
                        );
      }
    }
    Status = gRT->SetVariable (
                    L"CheckPchFirstBoot",
                    &gEfiGenericVariableGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                    sizeof (FirstPchBootFlag),
                    &FirstPchBootFlag
                    );
   }


  // [Lenovo BIOS Optane Support Version 1.4 Page 13]
  //   OS Optimized Default
  //   When RST Mode is selected, BIOS keeps "UEFI/Legacy mode" "EFI only" even
  //   when "Optimized Default" is disabled.
  //
  if ((SetupVariable->L05DefaultType == 1)  // 0: Enable 1:Disable
    && (PchSetup->SataInterfaceMode == SATA_MODE_AHCI)
    ) {
    //
    //  Legacy O.S.
    //
    SetupVariable->BootType = 0;  // 0:Dual Boot, 2:EFI Boot
    SetupVariable->L05SecureBoot = 0;     // 0:Disable,   1:Enable
  } else if((SetupVariable->L05DefaultType == 0)  // 0: Enable 1:Disable
    && (PchSetup->SataInterfaceMode == SATA_MODE_AHCI)
    ){
    //
    // Do nothing. user can change Boot Type.
    //
 }else {
    //
    // Windows 8 64 bit
    //
    SetupVariable->BootType = 2;    // 0:Dual Boot, 2:EFI Boot
    SetupVariable->L05SecureBoot = 1;     // 0:Disable,   1:Enable
  }
#endif
*/
  //
  // When L05WlanSyncToCnvi is Enable (1),
  // system will synchronize the L05 WLAN switch (SYSTEM_CONFIGURATION.L05WirelessFunction) with Intel CNVi switch (PCH_SETUP.PchCnviMode).
  //
  if (SetupVariable->L05WlanSyncToCnvi == 1 && SetupVariable->L05ODMWirelessImplement == 1) {
    //
    // "CNVi Mode" item,    0:Disable Integrated, 1:Auto Detection
    // "Wireless LAN" item, 0:Disable,            1:Enable
    //
    PchSetup->PchCnviMode = SetupVariable->L05WirelessFunction;
  }
#endif

  return EFI_SUCCESS;
}

VOID
EFIAPI
LcfcOverrideSetupSettingCallBack (
  IN EFI_EVENT Event,
  IN VOID *Context
  )
{
  EFI_STATUS Status;
  EFI_L05_SETUP_MENU_PROTOCOL *L05SetupMenu;

  gBS->CloseEvent (Event);
  Status = gBS->LocateProtocol (
                    &gEfiL05SetupMenuProtocolGuid,
                    NULL,
                    &L05SetupMenu
                    );

  if (!EFI_ERROR (Status)) {
    mOrgOverrideDefaultSetupSetting = L05SetupMenu->OverrideDefaultSetupSetting;
    L05SetupMenu->OverrideDefaultSetupSetting = LcfcOverrideDefaultSetupSetting;
    
    mOrgOverrideSetupSettingDuringPost = L05SetupMenu->OverrideSetupSettingDuringPost;
    L05SetupMenu->OverrideSetupSettingDuringPost = LcfcOverrideSetupSettingDuringPost;
    
    mOrgOverrideDefaultPchSetupSetting = L05SetupMenu->OverrideDefaultPchSetupSetting;
    L05SetupMenu->OverrideDefaultPchSetupSetting = LcfcOverrideDefaultPchSetupSetting;
  }
}


EFI_STATUS
ChgBootDxeHookEntryPoint (
  IN EFI_HANDLE ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS Status;
  EFI_L05_SETUP_MENU_PROTOCOL *L05SetupMenu;
  EFI_EVENT Event;
  VOID *Registration;

  Status = gBS->LocateProtocol (
                  &gEfiL05SetupMenuProtocolGuid,
                  NULL,
                  &L05SetupMenu
                  );

  if (!EFI_ERROR (Status)) {
    mOrgOverrideDefaultSetupSetting = L05SetupMenu->OverrideDefaultSetupSetting;
    L05SetupMenu->OverrideDefaultSetupSetting = LcfcOverrideDefaultSetupSetting;

    mOrgOverrideSetupSettingDuringPost = L05SetupMenu->OverrideSetupSettingDuringPost;
    L05SetupMenu->OverrideSetupSettingDuringPost = LcfcOverrideSetupSettingDuringPost;
    
    mOrgOverrideDefaultPchSetupSetting = L05SetupMenu->OverrideDefaultPchSetupSetting;
    L05SetupMenu->OverrideDefaultPchSetupSetting = LcfcOverrideDefaultPchSetupSetting;
  } else {

    Status = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    LcfcOverrideSetupSettingCallBack ,
                    NULL,
                    &Event
                    );

    if (!EFI_ERROR (Status)) {
      Status = gBS->RegisterProtocolNotify (
                      &gEfiL05SetupMenuProtocolGuid,
                      Event,
                      &Registration
                      );
    }
  }

  return Status;
}
