public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Wu, Hao A" <hao.a.wu@intel.com>
To: devel@edk2.groups.io
Cc: Hao A Wu <hao.a.wu@intel.com>,
	David Woodhouse <dwmw2@infradead.org>, Ray Ni <ray.ni@intel.com>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Laszlo Ersek <lersek@redhat.com>,
	Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH v2 02/10] OvmfPkg: Copy the required CSM components from framework packages
Date: Mon, 27 May 2019 11:03:42 +0800	[thread overview]
Message-ID: <20190527030350.11996-3-hao.a.wu@intel.com> (raw)
In-Reply-To: <20190527030350.11996-1-hao.a.wu@intel.com>

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=1811

This commit copies the exact:

Drivers:
* VideoDxe
* LegacyBiosDxe

Libraries:
* LegacyBootMaintUiLib
* LegacyBootManagerLib

Guid header files:
* Legacy Bios Guid
* LegacyDevOrder Guid

Protocol header files:
* Firmware Volume Protocol
* ISA ACPI Protocol
* ISA I/O Protocol
* Legacy BIOS Protocol
* Legacy BIOS Platform Protocol
* Legacy Interrupt Protocol
* VGA Mini Port Protocol

Framework header files (within IntelFrameworkPkg):
* FrameworkDxe.h
* Framework/BootScript.h
* Framework/DxeCis.h
* Framework/FirmwareVolumeHeader.h
* Framework/FirmwareVolumeImageFormat.h
* Framework/FrameworkInternalFormRepresentation.h
* Framework/Hob.h
* Framework/StatusCode.h

from IntelFramework[Module]Pkg to OvmfPkg/Csm/ folder.

Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Hao A Wu <hao.a.wu@intel.com>
---
 OvmfPkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf                         |   80 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf                         |  131 +
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf           |   63 +
 OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.inf           |   58 +
 OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.h                          |  532 ++++
 OvmfPkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h                 |  459 +++
 OvmfPkg/Csm/Include/Framework/BootScript.h                          |   41 +
 OvmfPkg/Csm/Include/Framework/DxeCis.h                              |  170 +
 OvmfPkg/Csm/Include/Framework/FirmwareVolumeHeader.h                |   79 +
 OvmfPkg/Csm/Include/Framework/FirmwareVolumeImageFormat.h           |   32 +
 OvmfPkg/Csm/Include/Framework/FrameworkInternalFormRepresentation.h |  397 +++
 OvmfPkg/Csm/Include/Framework/Hob.h                                 |   28 +
 OvmfPkg/Csm/Include/Framework/StatusCode.h                          |  155 +
 OvmfPkg/Csm/Include/FrameworkDxe.h                                  |   26 +
 OvmfPkg/Csm/Include/Guid/LegacyBios.h                               |   29 +
 OvmfPkg/Csm/Include/Guid/LegacyDevOrder.h                           |   39 +
 OvmfPkg/Csm/Include/Protocol/FirmwareVolume.h                       |  340 ++
 OvmfPkg/Csm/Include/Protocol/IsaAcpi.h                              |  298 ++
 OvmfPkg/Csm/Include/Protocol/IsaIo.h                                |  356 +++
 OvmfPkg/Csm/Include/Protocol/LegacyBios.h                           | 1553 +++++++++
 OvmfPkg/Csm/Include/Protocol/LegacyBiosPlatform.h                   |  755 +++++
 OvmfPkg/Csm/Include/Protocol/LegacyInterrupt.h                      |  122 +
 OvmfPkg/Csm/Include/Protocol/VgaMiniPort.h                          |   88 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h                     | 1460 +++++++++
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.h                |  249 ++
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.h             |   79 +
 OvmfPkg/Csm/LegacyBootManagerLib/InternalLegacyBm.h                 |   60 +
 OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.c                          | 3289 ++++++++++++++++++++
 OvmfPkg/Csm/BiosThunk/VideoDxe/ComponentName.c                      |  306 ++
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBbs.c                               |  377 +++
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBda.c                               |   62 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c                              | 1214 ++++++++
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBootSupport.c                       | 2173 +++++++++++++
 OvmfPkg/Csm/LegacyBiosDxe/LegacyCmos.c                              |  117 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyIde.c                               |  310 ++
 OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c                               | 3083 ++++++++++++++++++
 OvmfPkg/Csm/LegacyBiosDxe/LegacySio.c                               |  477 +++
 OvmfPkg/Csm/LegacyBiosDxe/Thunk.c                                   |  419 +++
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.c                | 1505 +++++++++
 OvmfPkg/Csm/LegacyBootManagerLib/LegacyBm.c                         | 1530 +++++++++
 OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxe.uni                     |   17 +
 OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxeExtra.uni                |   14 +
 OvmfPkg/Csm/LegacyBiosDxe/IA32/InterruptTable.nasm                  |   63 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.uni                         |   16 +
 OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxeExtra.uni                    |   14 +
 OvmfPkg/Csm/LegacyBiosDxe/X64/InterruptTable.nasm                   |   64 +
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.uni           |   20 +
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiStrings.uni       |   43 +
 OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.Vfr           |   67 +
 OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.uni           |   20 +
 50 files changed, 22879 insertions(+)

diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf b/OvmfPkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf
new file mode 100644
index 0000000000..1526bc4cc2
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/VideoDxe.inf
@@ -0,0 +1,80 @@
+## @file
+# Video driver based on legacy bios.
+#
+# This driver by using Legacy Bios protocol service to support csm Video
+# and produce Graphics Output Protocol.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = BiosVideoDxe
+  MODULE_UNI_FILE                = BiosVideoDxe.uni
+  FILE_GUID                      = 0B04B2ED-861C-42cd-A22F-C3AAFACCB896
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = BiosVideoEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+#  DRIVER_BINDING                =  gBiosVideoDriverBinding
+#  COMPONENT_NAME                =  gBiosVideoComponentName
+#
+
+[Sources]
+  BiosVideo.c
+  BiosVideo.h
+  ComponentName.c
+  VesaBiosExtensions.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  IntelFrameworkPkg/IntelFrameworkPkg.dec
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+
+
+[LibraryClasses]
+  MemoryAllocationLib
+  DevicePathLib
+  UefiLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  ReportStatusCodeLib
+  DebugLib
+  PcdLib
+
+
+[Guids]
+  gEfiLegacyBiosGuid                            ## PRODUCES  ##GUID # Install Legacy BIOS GUID to mark this driver as a BIOS Thunk Driver
+  gEfiEventExitBootServicesGuid                 ## CONSUMES  ##Event
+
+[Protocols]
+  gEfiVgaMiniPortProtocolGuid                   ## BY_START
+  gEfiEdidDiscoveredProtocolGuid                ## BY_START
+  gEfiGraphicsOutputProtocolGuid                ## BY_START
+  gEfiEdidActiveProtocolGuid                    ## BY_START
+  gEfiLegacyBiosProtocolGuid                    ## CONSUMES
+  gEfiPciIoProtocolGuid                         ## TO_START
+  gEfiDevicePathProtocolGuid                    ## TO_START
+  gEfiDevicePathProtocolGuid                    ## BY_START
+  gEfiEdidOverrideProtocolGuid                  ## SOMETIMES_CONSUMES
+
+[Pcd]
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoSetTextVgaModeEnable  ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVbeEnable        ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBiosVideoCheckVgaEnable        ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution                 ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution                   ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+  BiosVideoDxeExtra.uni
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf
new file mode 100644
index 0000000000..471d37365c
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf
@@ -0,0 +1,131 @@
+## @file
+# Legacy Bios Module to support CSM.
+#
+# This driver installs Legacy Bios Protocol to support CSM module work in EFI system.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = LegacyBiosDxe
+  MODULE_UNI_FILE                = LegacyBiosDxe.uni
+  FILE_GUID                      = F122A15C-C10B-4d54-8F48-60F4F06DD1AD
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = LegacyBiosInstall
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  LegacyCmos.c
+  LegacyIde.c
+  LegacyBios.c
+  LegacyBda.c
+  LegacyBiosInterface.h
+  LegacyPci.c
+
+[Sources.Ia32]
+  IA32/InterruptTable.nasm
+  Thunk.c
+  LegacyBootSupport.c
+  LegacyBbs.c
+  LegacySio.c
+
+[Sources.X64]
+  X64/InterruptTable.nasm
+  Thunk.c
+  LegacyBootSupport.c
+  LegacyBbs.c
+  LegacySio.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  IntelFrameworkPkg/IntelFrameworkPkg.dec
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+
+
+[LibraryClasses]
+  DevicePathLib
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  UefiLib
+  DebugLib
+  DxeServicesTableLib
+  PcdLib
+  ReportStatusCodeLib
+  DebugAgentLib
+
+[LibraryClasses.IA32]
+  IoLib
+  HobLib
+  UefiRuntimeServicesTableLib
+  BaseLib
+
+[LibraryClasses.X64]
+  IoLib
+  HobLib
+  UefiRuntimeServicesTableLib
+  BaseLib
+
+[Guids]
+  gEfiDiskInfoIdeInterfaceGuid                  ## SOMETIMES_CONSUMES ##GUID #Used in LegacyBiosBuildIdeData() to assure device is a disk
+  gEfiSmbiosTableGuid                           ## SOMETIMES_CONSUMES ##SystemTable
+  gEfiLegacyBiosGuid                            ## SOMETIMES_CONSUMES ##GUID #Used in LegacyBiosInstallVgaRom() to locate handle buffer
+  gEfiEndOfDxeEventGroupGuid                    ## CONSUMES
+
+[Guids.IA32]
+  gEfiAcpi20TableGuid                           ## SOMETIMES_CONSUMES ##SystemTable
+  gEfiAcpi10TableGuid                           ## SOMETIMES_CONSUMES ##SystemTable
+
+[Guids.X64]
+  gEfiAcpi20TableGuid                           ## SOMETIMES_CONSUMES ##SystemTable
+  gEfiAcpi10TableGuid                           ## SOMETIMES_CONSUMES ##SystemTable
+
+
+[Protocols]
+  gEfiLoadedImageProtocolGuid                   ## SOMETIMES_CONSUMES
+  gEfiDevicePathProtocolGuid                    ## SOMETIMES_CONSUMES
+  gEfiPciRootBridgeIoProtocolGuid               ## SOMETIMES_CONSUMES
+  gEfiCpuArchProtocolGuid                       ## CONSUMES
+  gEfiTimerArchProtocolGuid                     ## CONSUMES
+  gEfiIsaIoProtocolGuid                         ## SOMETIMES_CONSUMES
+  gEfiBlockIoProtocolGuid                       ## SOMETIMES_CONSUMES
+  gEfiPciIoProtocolGuid                         ## SOMETIMES_CONSUMES
+  gEfiGenericMemTestProtocolGuid                ## CONSUMES
+  gEfiDiskInfoProtocolGuid                      ## SOMETIMES_CONSUMES
+  gEfiSimpleTextInProtocolGuid                  ## SOMETIMES_CONSUMES
+  gEfiLegacy8259ProtocolGuid                    ## CONSUMES
+  gEfiLegacyBiosPlatformProtocolGuid            ## CONSUMES
+  gEfiLegacyInterruptProtocolGuid               ## CONSUMES
+  gEfiLegacyRegion2ProtocolGuid                 ## CONSUMES
+  gEfiLegacyBiosProtocolGuid                    ## PRODUCES
+  gEfiSerialIoProtocolGuid                      ## CONSUMES
+  gEfiSioProtocolGuid                           ## CONSUMES
+  gEdkiiIoMmuProtocolGuid                       ## CONSUMES
+
+[Pcd]
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLegacyBiosCacheLegacyRegion  ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdEbdaReservedMemorySize       ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdEndOpromShadowAddress        ## SOMETIMES_CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLowPmmMemorySize             ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdHighPmmMemorySize            ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdOpromReservedMemoryBase      ## CONSUMES
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdOpromReservedMemorySize      ## CONSUMES
+
+[Depex]
+  gEfiLegacyRegion2ProtocolGuid AND gEfiLegacyInterruptProtocolGuid AND gEfiLegacyBiosPlatformProtocolGuid AND gEfiLegacy8259ProtocolGuid AND gEfiGenericMemTestProtocolGuid AND gEfiCpuArchProtocolGuid AND gEfiTimerArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+  LegacyBiosDxeExtra.uni
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
new file mode 100644
index 0000000000..557b8d8169
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
@@ -0,0 +1,63 @@
+## @file
+#  Legacy Boot Maintainence UI module is library for BDS phase.
+#
+#  Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = LegacyBootMaintUiLib
+  MODULE_UNI_FILE                = LegacyBootMaintUiLib.uni
+  FILE_GUID                      = e6f7f038-3ed9-401a-af1f-5ea7bf644d34
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = NULL|DXE_DRIVER UEFI_APPLICATION
+  CONSTRUCTOR                    = LegacyBootMaintUiLibConstructor
+  DESTRUCTOR                     = LegacyBootMaintUiLibDestructor
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  LegacyBootMaintUiVfr.h
+  LegacyBootMaintUi.h
+  LegacyBootMaintUiVfr.Vfr
+  LegacyBootMaintUiStrings.uni
+  LegacyBootMaintUi.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+  IntelFrameworkPkg/IntelFrameworkPkg.dec
+
+[LibraryClasses]
+  DevicePathLib
+  BaseLib
+  UefiRuntimeServicesTableLib
+  UefiBootServicesTableLib
+  DebugLib
+  HiiLib
+  MemoryAllocationLib
+  UefiBootManagerLib
+  UefiLib
+  PrintLib
+  BaseMemoryLib
+
+[Guids]
+  gEfiIfrTianoGuid                  ## SOMETIMES_PRODUCES ## UNDEFINED # Extended IFR Guid Opcode
+  gEfiIfrBootMaintenanceGuid        ## CONSUMES ## HII # BootMaint HII Package
+  gEfiLegacyDevOrderVariableGuid    ## PRODUCES ## Variable:L"LegacyDevOrder"
+
+[Protocols]
+  gEfiHiiConfigAccessProtocolGuid             ## PRODUCES
+  gEfiLegacyBiosProtocolGuid                  ## CONSUMES
+  gEfiHiiConfigRoutingProtocolGuid            ## CONSUMES
+
+[Depex]
+  gEfiHiiDatabaseProtocolGuid
+
diff --git a/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.inf b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.inf
new file mode 100644
index 0000000000..eaf7b7235d
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.inf
@@ -0,0 +1,58 @@
+## @file
+#  Legacy Boot Manager module is library for BDS phase.
+#
+#  Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = LegacyBootManagerLib
+  MODULE_UNI_FILE                = LegacyBootManagerLib.uni
+  FILE_GUID                      = F1B87BE4-0ACC-409A-A52B-7BFFABCC96A0
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = NULL|DXE_DRIVER UEFI_APPLICATION
+  CONSTRUCTOR                    = LegacyBootManagerLibConstructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  LegacyBm.c
+  InternalLegacyBm.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  IntelFrameworkPkg/IntelFrameworkPkg.dec
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  UefiBootServicesTableLib
+  UefiRuntimeServicesTableLib
+  DevicePathLib
+  MemoryAllocationLib
+  UefiLib
+  DebugLib
+  PrintLib
+  PerformanceLib
+  UefiBootManagerLib
+
+[Guids]
+  gEfiGlobalVariableGuid                        ## SOMETIMES_PRODUCES ## Variable:L"Boot####" (Boot option variable)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"BootOrder" (The boot option array)
+  gEfiLegacyDevOrderVariableGuid
+
+[Protocols]
+  gEfiLegacyBiosProtocolGuid                    ## SOMETIMES_CONSUMES
+
+[FeaturePcd]
+
+[Pcd]
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.h b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.h
new file mode 100644
index 0000000000..951f4e6f76
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.h
@@ -0,0 +1,532 @@
+/** @file
+
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _BIOS_GRAPHICS_OUTPUT_H_
+#define _BIOS_GRAPHICS_OUTPUT_H_
+
+#include <FrameworkDxe.h>
+
+#include <Protocol/PciIo.h>
+#include <Protocol/EdidActive.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/EdidDiscovered.h>
+#include <Protocol/LegacyBios.h>
+#include <Protocol/VgaMiniPort.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/EdidOverride.h>
+
+#include <Guid/StatusCodeDataTypeId.h>
+#include <Guid/LegacyBios.h>
+#include <Guid/EventGroup.h>
+
+#include <Library/PcdLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <IndustryStandard/Pci.h>
+#include "VesaBiosExtensions.h"
+
+//
+// Packed format support: The number of bits reserved for each of the colors and the actual
+// position of RGB in the frame buffer is specified in the VBE Mode information
+//
+typedef struct {
+  UINT8 Position; // Position of the color
+  UINT8 Mask;     // The number of bits expressed as a mask
+} BIOS_VIDEO_COLOR_PLACEMENT;
+
+//
+// BIOS Graphics Output Graphical Mode Data
+//
+typedef struct {
+  UINT16                      VbeModeNumber;
+  UINT16                      BytesPerScanLine;
+  VOID                        *LinearFrameBuffer;
+  UINTN                       FrameBufferSize;
+  UINT32                      HorizontalResolution;
+  UINT32                      VerticalResolution;
+  UINT32                      ColorDepth;
+  UINT32                      RefreshRate;
+  UINT32                      BitsPerPixel;
+  BIOS_VIDEO_COLOR_PLACEMENT  Red;
+  BIOS_VIDEO_COLOR_PLACEMENT  Green;
+  BIOS_VIDEO_COLOR_PLACEMENT  Blue;
+  BIOS_VIDEO_COLOR_PLACEMENT  Reserved;
+  EFI_GRAPHICS_PIXEL_FORMAT   PixelFormat;
+  EFI_PIXEL_BITMASK           PixelBitMask;
+} BIOS_VIDEO_MODE_DATA;
+
+//
+// BIOS video child handle private data Structure
+//
+#define BIOS_VIDEO_DEV_SIGNATURE    SIGNATURE_32 ('B', 'V', 'M', 'p')
+
+typedef struct {
+  UINTN                                       Signature;
+  EFI_HANDLE                                  Handle;
+
+  //
+  // Consumed Protocols
+  //
+  EFI_PCI_IO_PROTOCOL                         *PciIo;
+  EFI_LEGACY_BIOS_PROTOCOL                    *LegacyBios;
+
+  //
+  // Produced Protocols
+  //
+  EFI_GRAPHICS_OUTPUT_PROTOCOL                GraphicsOutput;
+  EFI_EDID_DISCOVERED_PROTOCOL                EdidDiscovered;
+  EFI_EDID_ACTIVE_PROTOCOL                    EdidActive;
+  EFI_VGA_MINI_PORT_PROTOCOL                  VgaMiniPort;
+
+  //
+  // General fields
+  //
+  BOOLEAN                                     VgaCompatible;
+  BOOLEAN                                     ProduceGraphicsOutput;
+
+  //
+  // Graphics Output Protocol related fields
+  //
+  BOOLEAN                                     HardwareNeedsStarting;
+  UINTN                                       CurrentMode;
+  UINTN                                       MaxMode;
+  BIOS_VIDEO_MODE_DATA                        *ModeData;
+  UINT8                                       *LineBuffer;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL               *VbeFrameBuffer;
+  UINT8                                       *VgaFrameBuffer;
+
+  //
+  // VESA Bios Extensions related fields
+  //
+  UINTN                                       NumberOfPagesBelow1MB;     // Number of 4KB pages in PagesBelow1MB
+  EFI_PHYSICAL_ADDRESS                        PagesBelow1MB;             // Buffer for all VBE Information Blocks
+  VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK      *VbeInformationBlock;      // 0x200 bytes.  Must be allocated below 1MB
+  VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK *VbeModeInformationBlock;  // 0x100 bytes.  Must be allocated below 1MB
+  VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK        *VbeEdidDataBlock;         // 0x80  bytes.  Must be allocated below 1MB
+  VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK *VbeCrtcInformationBlock;  // 59 bytes.  Must be allocated below 1MB
+  UINTN                                       VbeSaveRestorePages;       // Number of 4KB pages in VbeSaveRestoreBuffer
+  EFI_PHYSICAL_ADDRESS                        VbeSaveRestoreBuffer;      // Must be allocated below 1MB
+  //
+  // Status code
+  //
+  EFI_DEVICE_PATH_PROTOCOL                    *GopDevicePath;
+
+  EFI_EVENT                                   ExitBootServicesEvent;
+} BIOS_VIDEO_DEV;
+
+#define BIOS_VIDEO_DEV_FROM_PCI_IO_THIS(a)           CR (a, BIOS_VIDEO_DEV, PciIo, BIOS_VIDEO_DEV_SIGNATURE)
+#define BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS(a)  CR (a, BIOS_VIDEO_DEV, GraphicsOutput, BIOS_VIDEO_DEV_SIGNATURE)
+#define BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS(a)    CR (a, BIOS_VIDEO_DEV, VgaMiniPort, BIOS_VIDEO_DEV_SIGNATURE)
+
+#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER  0xffff
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL   gBiosVideoDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL   gBiosVideoComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL  gBiosVideoComponentName2;
+
+//
+// Driver Binding Protocol functions
+//
+
+/**
+  Supported.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  RemainingDevicePath    A pointer to the remaining portion of a device
+                                 path
+
+  @retval EFI_STATUS             EFI_SUCCESS:This controller can be managed by this
+                                 driver, Otherwise, this controller cannot be
+                                 managed by this driver
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+
+/**
+  Install Graphics Output Protocol onto VGA device handles.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  RemainingDevicePath    A pointer to the remaining portion of a device
+                                 path
+
+  @return EFI_STATUS
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+
+/**
+  Stop.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  NumberOfChildren       Number of children handle created by this driver
+  @param  ChildHandleBuffer      Buffer containing child handle created
+
+  @retval EFI_SUCCESS            Driver disconnected successfully from controller
+  @retval EFI_UNSUPPORTED        Cannot find BIOS_VIDEO_DEV structure
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+//
+// Private worker functions
+//
+
+/**
+  Check for VBE device.
+
+  @param  BiosVideoPrivate       Pointer to BIOS_VIDEO_DEV structure
+
+  @retval EFI_SUCCESS            VBE device found
+
+**/
+EFI_STATUS
+BiosVideoCheckForVbe (
+  IN OUT BIOS_VIDEO_DEV  *BiosVideoPrivate
+  );
+
+
+/**
+  Check for VGA device.
+
+  @param  BiosVideoPrivate       Pointer to BIOS_VIDEO_DEV structure
+
+  @retval EFI_SUCCESS            Standard VGA device found
+
+**/
+EFI_STATUS
+BiosVideoCheckForVga (
+  IN OUT BIOS_VIDEO_DEV  *BiosVideoPrivate
+  );
+
+
+
+
+/**
+  Release resource for biso video instance.
+
+  @param  BiosVideoPrivate       Video child device private data structure
+
+**/
+VOID
+BiosVideoDeviceReleaseResource (
+  BIOS_VIDEO_DEV  *BiosVideoPrivate
+  );
+
+//
+// BIOS Graphics Output Protocol functions
+//
+
+/**
+  Graphics Output protocol interface to get video mode.
+
+  @param  This                   Protocol instance pointer.
+  @param  ModeNumber             The mode number to return information on.
+  @param  SizeOfInfo             A pointer to the size, in bytes, of the Info
+                                 buffer.
+  @param  Info                   Caller allocated buffer that returns information
+                                 about ModeNumber.
+
+  @retval EFI_SUCCESS            Mode information returned.
+  @retval EFI_BUFFER_TOO_SMALL   The Info buffer was too small.
+  @retval EFI_DEVICE_ERROR       A hardware error occurred trying to retrieve the
+                                 video mode.
+  @retval EFI_NOT_STARTED        Video display is not initialized. Call SetMode ()
+  @retval EFI_INVALID_PARAMETER  One of the input args was NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputQueryMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+  IN  UINT32                                ModeNumber,
+  OUT UINTN                                 *SizeOfInfo,
+  OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
+  );
+
+
+/**
+  Graphics Output protocol interface to set video mode.
+
+  @param  This                   Protocol instance pointer.
+  @param  ModeNumber             The mode number to be set.
+
+  @retval EFI_SUCCESS            Graphics mode was changed.
+  @retval EFI_DEVICE_ERROR       The device had an error and could not complete the
+                                 request.
+  @retval EFI_UNSUPPORTED        ModeNumber is not supported by this device.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputSetMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL * This,
+  IN  UINT32                       ModeNumber
+  );
+
+
+/**
+  Graphics Output protocol instance to block transfer for VBE device.
+
+  @param  This                   Pointer to Graphics Output protocol instance
+  @param  BltBuffer              The data to transfer to screen
+  @param  BltOperation           The operation to perform
+  @param  SourceX                The X coordinate of the source for BltOperation
+  @param  SourceY                The Y coordinate of the source for BltOperation
+  @param  DestinationX           The X coordinate of the destination for
+                                 BltOperation
+  @param  DestinationY           The Y coordinate of the destination for
+                                 BltOperation
+  @param  Width                  The width of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Height                 The height of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Delta                  Not used for EfiBltVideoFill and
+                                 EfiBltVideoToVideo operation. If a Delta of 0 is
+                                 used, the entire BltBuffer will be operated on. If
+                                 a subrectangle of the BltBuffer is used, then
+                                 Delta represents the number of bytes in a row of
+                                 the BltBuffer.
+
+  @retval EFI_INVALID_PARAMETER  Invalid parameter passed in
+  @retval EFI_SUCCESS            Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputVbeBlt (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL       *This,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN  UINTN                              SourceX,
+  IN  UINTN                              SourceY,
+  IN  UINTN                              DestinationX,
+  IN  UINTN                              DestinationY,
+  IN  UINTN                              Width,
+  IN  UINTN                              Height,
+  IN  UINTN                              Delta
+  );
+
+
+/**
+  Grahpics Output protocol instance to block transfer for VGA device.
+
+  @param  This                   Pointer to Grahpics Output protocol instance
+  @param  BltBuffer              The data to transfer to screen
+  @param  BltOperation           The operation to perform
+  @param  SourceX                The X coordinate of the source for BltOperation
+  @param  SourceY                The Y coordinate of the source for BltOperation
+  @param  DestinationX           The X coordinate of the destination for
+                                 BltOperation
+  @param  DestinationY           The Y coordinate of the destination for
+                                 BltOperation
+  @param  Width                  The width of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Height                 The height of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Delta                  Not used for EfiBltVideoFill and
+                                 EfiBltVideoToVideo operation. If a Delta of 0 is
+                                 used, the entire BltBuffer will be operated on. If
+                                 a subrectangle of the BltBuffer is used, then
+                                 Delta represents the number of bytes in a row of
+                                 the BltBuffer.
+
+  @retval EFI_INVALID_PARAMETER  Invalid parameter passed in
+  @retval EFI_SUCCESS            Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputVgaBlt (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL       *This,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN  UINTN                              SourceX,
+  IN  UINTN                              SourceY,
+  IN  UINTN                              DestinationX,
+  IN  UINTN                              DestinationY,
+  IN  UINTN                              Width,
+  IN  UINTN                              Height,
+  IN  UINTN                              Delta
+  );
+
+//
+// BIOS VGA Mini Port Protocol functions
+//
+
+/**
+  VgaMiniPort protocol interface to set mode.
+
+  @param  This                   Pointer to VgaMiniPort protocol instance
+  @param  ModeNumber             The index of the mode
+
+  @retval EFI_UNSUPPORTED        The requested mode is not supported
+  @retval EFI_SUCCESS            The requested mode is set successfully
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoVgaMiniPortSetMode (
+  IN  EFI_VGA_MINI_PORT_PROTOCOL  *This,
+  IN  UINTN                       ModeNumber
+  );
+
+/**
+  Event handler for Exit Boot Service.
+
+  @param  Event       The event that be siganlled when exiting boot service.
+  @param  Context     Pointer to instance of BIOS_VIDEO_DEV.
+
+**/
+VOID
+EFIAPI
+BiosVideoNotifyExitBootServices (
+  IN  EFI_EVENT Event,
+  IN  VOID      *Context
+  );
+
+//
+// Standard VGA Definitions
+//
+#define VGA_HORIZONTAL_RESOLUTION                         640
+#define VGA_VERTICAL_RESOLUTION                           480
+#define VGA_NUMBER_OF_BIT_PLANES                          4
+#define VGA_PIXELS_PER_BYTE                               8
+#define VGA_BYTES_PER_SCAN_LINE                           (VGA_HORIZONTAL_RESOLUTION / VGA_PIXELS_PER_BYTE)
+#define VGA_BYTES_PER_BIT_PLANE                           (VGA_VERTICAL_RESOLUTION * VGA_BYTES_PER_SCAN_LINE)
+
+#define VGA_GRAPHICS_CONTROLLER_ADDRESS_REGISTER          0x3ce
+#define VGA_GRAPHICS_CONTROLLER_DATA_REGISTER             0x3cf
+
+#define VGA_GRAPHICS_CONTROLLER_SET_RESET_REGISTER        0x00
+
+#define VGA_GRAPHICS_CONTROLLER_ENABLE_SET_RESET_REGISTER 0x01
+
+#define VGA_GRAPHICS_CONTROLLER_COLOR_COMPARE_REGISTER    0x02
+
+#define VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER      0x03
+#define VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE          0x00
+#define VGA_GRAPHICS_CONTROLLER_FUNCTION_AND              0x08
+#define VGA_GRAPHICS_CONTROLLER_FUNCTION_OR               0x10
+#define VGA_GRAPHICS_CONTROLLER_FUNCTION_XOR              0x18
+
+#define VGA_GRAPHICS_CONTROLLER_READ_MAP_SELECT_REGISTER  0x04
+
+#define VGA_GRAPHICS_CONTROLLER_MODE_REGISTER             0x05
+#define VGA_GRAPHICS_CONTROLLER_READ_MODE_0               0x00
+#define VGA_GRAPHICS_CONTROLLER_READ_MODE_1               0x08
+#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_0              0x00
+#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_1              0x01
+#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2              0x02
+#define VGA_GRAPHICS_CONTROLLER_WRITE_MODE_3              0x03
+
+#define VGA_GRAPHICS_CONTROLLER_MISCELLANEOUS_REGISTER    0x06
+
+#define VGA_GRAPHICS_CONTROLLER_COLOR_DONT_CARE_REGISTER  0x07
+
+#define VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER         0x08
+
+/**
+  Install child handles if the Handle supports MBR format.
+
+  @param  This                   Calling context.
+  @param  ParentHandle           Parent Handle
+  @param  ParentPciIo            Parent PciIo interface
+  @param  ParentLegacyBios       Parent LegacyBios interface
+  @param  ParentDevicePath       Parent Device Path
+  @param  RemainingDevicePath    Remaining Device Path
+
+  @retval EFI_SUCCESS            If a child handle was added
+  @retval other                  A child handle was not added
+
+**/
+EFI_STATUS
+BiosVideoChildHandleInstall (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ParentHandle,
+  IN  EFI_PCI_IO_PROTOCOL          *ParentPciIo,
+  IN  EFI_LEGACY_BIOS_PROTOCOL     *ParentLegacyBios,
+  IN  EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Deregister an video child handle and free resources.
+
+  @param  This                   Protocol instance pointer.
+  @param  Controller             Video controller handle
+  @param  Handle                 Video child handle
+
+  @return EFI_STATUS
+
+**/
+EFI_STATUS
+BiosVideoChildHandleUninstall (
+  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  EFI_HANDLE                     Controller,
+  EFI_HANDLE                     Handle
+  );
+
+/**
+  Release resource for biso video instance.
+
+  @param  BiosVideoPrivate       Video child device private data structure
+
+**/
+VOID
+BiosVideoDeviceReleaseResource (
+  BIOS_VIDEO_DEV  *BiosVideoPrivate
+  );
+
+/**
+  Check if all video child handles have been uninstalled.
+
+  @param  Controller             Video controller handle
+
+  @return TRUE                   Child handles exist.
+  @return FALSE                  All video child handles have been uninstalled.
+
+**/
+BOOLEAN
+HasChildHandle (
+  IN EFI_HANDLE  Controller
+  );
+#endif
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h b/OvmfPkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h
new file mode 100644
index 0000000000..dbf706179f
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/VesaBiosExtensions.h
@@ -0,0 +1,459 @@
+/** @file
+
+Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VESA_BIOS_EXTENSIONS_H_
+#define _VESA_BIOS_EXTENSIONS_H_
+
+//
+// Turn on byte packing of data structures
+//
+#pragma pack(1)
+//
+// VESA BIOS Extensions status codes
+//
+#define VESA_BIOS_EXTENSIONS_STATUS_SUCCESS 0x004f
+
+//
+// VESA BIOS Extensions Services
+//
+#define VESA_BIOS_EXTENSIONS_RETURN_CONTROLLER_INFORMATION  0x4f00
+
+/*++
+
+  Routine Description:
+    Function 00 : Return Controller Information
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f00
+      ES:DI = Pointer to buffer to place VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK structure
+    Outputs:
+      AX    = Return Status
+
+--*/
+#define VESA_BIOS_EXTENSIONS_RETURN_MODE_INFORMATION  0x4f01
+
+/*++
+
+  Routine Description:
+    Function 01 : Return Mode Information
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f01
+      CX    = Mode Number
+      ES:DI = Pointer to buffer to place VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK structure
+    Outputs:
+      AX    = Return Status
+
+--*/
+#define VESA_BIOS_EXTENSIONS_SET_MODE 0x4f02
+
+/*++
+
+  Routine Description:
+    Function 02 : Set Mode
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f02
+      BX    = Desired mode to set
+        D0-D8   = Mode Number
+        D9-D10  = Reserved (must be 0)
+        D11     = 0 - Use current default refresh rate
+                = 1 - Use user specfieid CRTC values for refresh rate
+        D12-D13 = Reserved (must be 0)
+        D14     = 0 - Use windowed frame buffer model
+                = 1 - Use linear/flat frame buffer model
+        D15     = 0 - Clear display memory
+                = 1 - Don't clear display memory
+      ES:DI = Pointer to buffer to the VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK structure
+    Outputs:
+      AX    = Return Status
+
+--*/
+#define VESA_BIOS_EXTENSIONS_RETURN_CURRENT_MODE  0x4f03
+
+/*++
+
+  Routine Description:
+    Function 03 : Return Current Mode
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f03
+    Outputs:
+      AX    = Return Status
+      BX    = Current mode
+        D0-D13  = Mode Number
+        D14     = 0 - Windowed frame buffer model
+                = 1 - Linear/flat frame buffer model
+        D15     = 0 - Memory cleared at last mode set
+                = 1 - Memory not cleared at last mode set
+
+--*/
+#define VESA_BIOS_EXTENSIONS_SAVE_RESTORE_STATE 0x4f04
+
+/*++
+
+  Routine Description:
+    Function 04 : Save/Restore State
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f03
+      DL    = 0x00 - Return Save/Restore State buffer size
+            = 0x01 - Save State
+            = 0x02 - Restore State
+      CX    = Requested Status
+        D0  = Save/Restore controller hardware state
+        D1  = Save/Restore BIOS data state
+        D2  = Save/Restore DAC state
+        D3  = Save/Restore Regsiter state
+      ES:BX = Pointer to buffer if DL=1 or DL=2
+    Outputs:
+      AX    = Return Status
+      BX    = Number of 64 byte blocks to hold the state buffer if DL=0
+
+--*/
+#define VESA_BIOS_EXTENSIONS_EDID  0x4f15
+
+/*++
+
+  Routine Description:
+    Function 15 : implement VBE/DDC service
+
+  Arguments:
+    Inputs:
+      AX    = 0x4f15
+      BL    = 0x00 - Report VBE/DDC Capabilities
+      CX    = 0x00 - Controller unit number (00 = primary controller)
+      ES:DI = Null pointer, must be 0:0 in version 1.0
+    Outputs:
+      AX    = Return Status
+      BH    = Approx. time in seconds, rounded up, to transfer one EDID block(128 bytes)
+      BL    = DDC level supported
+        D0  = 0 DDC1 not supported
+            = 1 DDC1 supported
+        D1  = 0 DDC2 not supported
+            = 1 DDC2 supported
+        D2  = 0 Screen not blanked during data transfer
+            = 1 Screen blanked during data transfer
+
+    Inputs:
+      AX    = 0x4f15
+      BL    = 0x01 - Read EDID
+      CX    = 0x00 - Controller unit number (00 = primary controller)
+      DX    = 0x00 - EDID block number
+      ES:DI = Pointer to buffer in which the EDID block is returned
+    Outputs:
+      AX    = Return Status
+--*/
+
+//
+// Timing data from EDID data block
+//
+#define VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE                    128
+#define VESA_BIOS_EXTENSIONS_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17
+
+//
+// Established Timings: 24 possible resolutions
+// Standard Timings: 8 possible resolutions
+// Detailed Timings: 4 possible resolutions
+//
+#define VESA_BIOS_EXTENSIONS_EDID_TIMING_MAX_NUMBER             36
+
+//
+// Timing data size for Established Timings, Standard Timings and Detailed Timings
+//
+#define VESA_BIOS_EXTENSIONS_ESTABLISHED_TIMING_SIZE                  3
+#define VESA_BIOS_EXTENSIONS_STANDARD_TIMING_SIZE                     16
+#define VESA_BIOS_EXTENSIONS_DETAILED_TIMING_EACH_DESCRIPTOR_SIZE     18
+#define VESA_BIOS_EXTENSIONS_DETAILED_TIMING_DESCRIPTOR_MAX_SIZE      72
+
+typedef struct {
+  UINT16  HorizontalResolution;
+  UINT16  VerticalResolution;
+  UINT16  RefreshRate;
+} VESA_BIOS_EXTENSIONS_EDID_TIMING;
+
+typedef struct {
+  UINT32  ValidNumber;
+  UINT32  Key[VESA_BIOS_EXTENSIONS_EDID_TIMING_MAX_NUMBER];
+} VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING;
+
+typedef struct {
+  UINT8   Header[8];                        //EDID header "00 FF FF FF FF FF FF 00"
+  UINT16  ManufactureName;                  //EISA 3-character ID
+  UINT16  ProductCode;                      //Vendor assigned code
+  UINT32  SerialNumber;                     //32-bit serial number
+  UINT8   WeekOfManufacture;                //Week number
+  UINT8   YearOfManufacture;                //Year
+  UINT8   EdidVersion;                      //EDID Structure Version
+  UINT8   EdidRevision;                     //EDID Structure Revision
+  UINT8   VideoInputDefinition;
+  UINT8   MaxHorizontalImageSize;           //cm
+  UINT8   MaxVerticalImageSize;             //cm
+  UINT8   DisplayTransferCharacteristic;
+  UINT8   FeatureSupport;
+  UINT8   RedGreenLowBits;                  //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0
+  UINT8   BlueWhiteLowBits;                 //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0
+  UINT8   RedX;                             //Red-x Bits 9 - 2
+  UINT8   RedY;                             //Red-y Bits 9 - 2
+  UINT8   GreenX;                           //Green-x Bits 9 - 2
+  UINT8   GreenY;                           //Green-y Bits 9 - 2
+  UINT8   BlueX;                            //Blue-x Bits 9 - 2
+  UINT8   BlueY;                            //Blue-y Bits 9 - 2
+  UINT8   WhiteX;                           //White-x Bits 9 - 2
+  UINT8   WhiteY;                           //White-x Bits 9 - 2
+  UINT8   EstablishedTimings[VESA_BIOS_EXTENSIONS_ESTABLISHED_TIMING_SIZE];
+  UINT8   StandardTimingIdentification[VESA_BIOS_EXTENSIONS_STANDARD_TIMING_SIZE];
+  UINT8   DetailedTimingDescriptions[VESA_BIOS_EXTENSIONS_DETAILED_TIMING_DESCRIPTOR_MAX_SIZE];
+  UINT8   ExtensionFlag;                    //Number of (optional) 128-byte EDID extension blocks to follow
+  UINT8   Checksum;
+} VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK;
+
+//
+// Super VGA Information Block
+//
+typedef struct {
+  UINT32  VESASignature;      // 'VESA' 4 byte signature
+  UINT16  VESAVersion;        // VBE version number
+  UINT32  OEMStringPtr;      // Pointer to OEM string
+  UINT32  Capabilities;       // Capabilities of video card
+  UINT32  VideoModePtr;      // Pointer to an array of 16-bit supported modes values terminated by 0xFFFF
+  UINT16  TotalMemory;        // Number of 64kb memory blocks
+  UINT16  OemSoftwareRev;     // VBE implementation Software revision
+  UINT32  OemVendorNamePtr;  // VbeFarPtr to Vendor Name String
+  UINT32  OemProductNamePtr; // VbeFarPtr to Product Name String
+  UINT32  OemProductRevPtr;  // VbeFarPtr to Product Revision String
+  UINT8   Reserved[222];      // Reserved for VBE implementation scratch area
+  UINT8   OemData[256];       // Data area for OEM strings.  Pad to 512 byte block size
+} VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK;
+
+//
+// Super VGA Information Block VESASignature values
+//
+#define VESA_BIOS_EXTENSIONS_VESA_SIGNATURE SIGNATURE_32 ('V', 'E', 'S', 'A')
+#define VESA_BIOS_EXTENSIONS_VBE2_SIGNATURE SIGNATURE_32 ('V', 'B', 'E', '2')
+
+//
+// Super VGA Information Block VESAVersion values
+//
+#define VESA_BIOS_EXTENSIONS_VERSION_1_2  0x0102
+#define VESA_BIOS_EXTENSIONS_VERSION_2_0  0x0200
+#define VESA_BIOS_EXTENSIONS_VERSION_3_0  0x0300
+
+//
+// Super VGA Information Block Capabilities field bit defintions
+//
+#define VESA_BIOS_EXTENSIONS_CAPABILITY_8_BIT_DAC 0x01  // 0: DAC width is fixed at 6 bits/color
+// 1: DAC width switchable to 8 bits/color
+//
+#define VESA_BIOS_EXTENSIONS_CAPABILITY_NOT_VGA 0x02  // 0: Controller is VGA compatible
+// 1: Controller is not VGA compatible
+//
+#define VESA_BIOS_EXTENSIONS_CAPABILITY_NOT_NORMAL_RAMDAC 0x04  // 0: Normal RAMDAC operation
+// 1: Use blank bit in function 9 to program RAMDAC
+//
+#define VESA_BIOS_EXTENSIONS_CAPABILITY_STEREOSCOPIC  0x08  // 0: No hardware stereoscopic signal support
+// 1: Hardware stereoscopic signal support
+//
+#define VESA_BIOS_EXTENSIONS_CAPABILITY_VESA_EVC  0x10  // 0: Stero signaling supported via external VESA stereo connector
+// 1: Stero signaling supported via VESA EVC connector
+//
+// Super VGA mode number bite field definitions
+//
+#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_VESA 0x0100  // 0: Not a VESA defined VBE mode
+// 1: A VESA defined VBE mode
+//
+#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_REFRESH_CONTROL_USER 0x0800  // 0: Use current BIOS default referesh rate
+// 1: Use the user specified CRTC values for refresh rate
+//
+#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_LINEAR_FRAME_BUFFER  0x4000  // 0: Use a banked/windowed frame buffer
+// 1: Use a linear/flat frame buffer
+//
+#define VESA_BIOS_EXTENSIONS_MODE_NUMBER_PRESERVE_MEMORY  0x8000  // 0: Clear display memory
+// 1: Preseve display memory
+//
+// Super VGA Information Block mode list terminator value
+//
+#define VESA_BIOS_EXTENSIONS_END_OF_MODE_LIST 0xffff
+
+//
+// Window Function
+//
+typedef
+VOID
+(*VESA_BIOS_EXTENSIONS_WINDOW_FUNCTION) (
+  VOID
+  );
+
+//
+// Super VGA Mode Information Block
+//
+typedef struct {
+  //
+  // Manadory fields for all VESA Bios Extensions revisions
+  //
+  UINT16                                ModeAttributes;   // Mode attributes
+  UINT8                                 WinAAttributes;   // Window A attributes
+  UINT8                                 WinBAttributes;   // Window B attributes
+  UINT16                                WinGranularity;   // Window granularity in k
+  UINT16                                WinSize;          // Window size in k
+  UINT16                                WinASegment;      // Window A segment
+  UINT16                                WinBSegment;      // Window B segment
+  UINT32                                WindowFunction;   // Pointer to window function
+  UINT16                                BytesPerScanLine; // Bytes per scanline
+  //
+  // Manadory fields for VESA Bios Extensions 1.2 and above
+  //
+  UINT16                                XResolution;          // Horizontal resolution
+  UINT16                                YResolution;          // Vertical resolution
+  UINT8                                 XCharSize;            // Character cell width
+  UINT8                                 YCharSize;            // Character cell height
+  UINT8                                 NumberOfPlanes;       // Number of memory planes
+  UINT8                                 BitsPerPixel;         // Bits per pixel
+  UINT8                                 NumberOfBanks;        // Number of CGA style banks
+  UINT8                                 MemoryModel;          // Memory model type
+  UINT8                                 BankSize;             // Size of CGA style banks
+  UINT8                                 NumberOfImagePages;   // Number of images pages
+  UINT8                                 Reserved1;            // Reserved
+  UINT8                                 RedMaskSize;          // Size of direct color red mask
+  UINT8                                 RedFieldPosition;     // Bit posn of lsb of red mask
+  UINT8                                 GreenMaskSize;        // Size of direct color green mask
+  UINT8                                 GreenFieldPosition;   // Bit posn of lsb of green mask
+  UINT8                                 BlueMaskSize;         // Size of direct color blue mask
+  UINT8                                 BlueFieldPosition;    // Bit posn of lsb of blue mask
+  UINT8                                 RsvdMaskSize;         // Size of direct color res mask
+  UINT8                                 RsvdFieldPosition;    // Bit posn of lsb of res mask
+  UINT8                                 DirectColorModeInfo;  // Direct color mode attributes
+  //
+  // Manadory fields for VESA Bios Extensions 2.0 and above
+  //
+  UINT32                                PhysBasePtr;  // Physical Address for flat memory frame buffer
+  UINT32                                Reserved2;    // Reserved
+  UINT16                                Reserved3;    // Reserved
+  //
+  // Manadory fields for VESA Bios Extensions 3.0 and above
+  //
+  UINT16                                LinBytesPerScanLine;    // Bytes/scan line for linear modes
+  UINT8                                 BnkNumberOfImagePages;  // Number of images for banked modes
+  UINT8                                 LinNumberOfImagePages;  // Number of images for linear modes
+  UINT8                                 LinRedMaskSize;         // Size of direct color red mask (linear mode)
+  UINT8                                 LinRedFieldPosition;    // Bit posiiton of lsb of red mask (linear modes)
+  UINT8                                 LinGreenMaskSize;       // Size of direct color green mask (linear mode)
+  UINT8                                 LinGreenFieldPosition;  // Bit posiiton of lsb of green mask (linear modes)
+  UINT8                                 LinBlueMaskSize;        // Size of direct color blue mask (linear mode)
+  UINT8                                 LinBlueFieldPosition;   // Bit posiiton of lsb of blue mask (linear modes)
+  UINT8                                 LinRsvdMaskSize;        // Size of direct color reserved mask (linear mode)
+  UINT8                                 LinRsvdFieldPosition;   // Bit posiiton of lsb of reserved mask (linear modes)
+  UINT32                                MaxPixelClock;          // Maximum pixel clock (in Hz) for graphics mode
+  UINT8                                 Pad[190];               // Pad to 256 byte block size
+} VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK;
+
+//
+// Super VGA Mode Information Block ModeAttributes field bit defintions
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_HARDWARE  0x0001  // 0: Mode not supported in handware
+// 1: Mode supported in handware
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_TTY 0x0004  // 0: TTY Output functions not supported by BIOS
+// 1: TTY Output functions supported by BIOS
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_COLOR 0x0008  // 0: Monochrome mode
+// 1: Color mode
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_GRAPHICS  0x0010  // 0: Text mode
+// 1: Graphics mode
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NOT_VGA 0x0020  // 0: VGA compatible mode
+// 1: Not a VGA compatible mode
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NOT_WINDOWED  0x0040  // 0: VGA compatible windowed memory mode
+// 1: Not a VGA compatible windowed memory mode
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER 0x0080  // 0: No linear fram buffer mode available
+// 1: Linear frame buffer mode available
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_DOUBLE_SCAN 0x0100  // 0: No double scan mode available
+// 1: Double scan mode available
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_INTERLACED  0x0200  // 0: No interlaced mode is available
+// 1: Interlaced mode is available
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_NO_TRIPPLE_BUFFER 0x0400  // 0: No hardware triple buffer mode support available
+// 1: Hardware triple buffer mode support available
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_STEREOSCOPIC  0x0800  // 0: No hardware steroscopic display support
+// 1: Hardware steroscopic display support
+//
+#define VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_DUAL_DISPLAY  0x1000  // 0: No dual display start address support
+// 1: Dual display start address support
+//
+// Super VGA Mode Information Block WinAAttribite/WinBAttributes field bit defintions
+//
+#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_RELOCATABLE 0x01  // 0: Single non-relocatable window only
+// 1: Relocatable window(s) are supported
+//
+#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_READABLE  0x02  // 0: Window is not readable
+// 1: Window is readable
+//
+#define VESA_BIOS_EXTENSIONS_WINX_ATTRIBUTE_WRITABLE  0x04  // 0: Window is not writable
+// 1: Window is writable
+//
+// Super VGA Mode Information Block DirectColorMode field bit defintions
+//
+#define VESA_BIOS_EXTENSIONS_DIRECT_COLOR_MODE_PROG_COLOR_RAMP  0x01  // 0: Color ram is fixed
+// 1: Color ramp is programmable
+//
+#define VESA_BIOS_EXTENSIONS_DIRECT_COLOR_MODE_RSVD_USABLE  0x02  // 0: Bits in Rsvd field are reserved
+// 1: Bits in Rsdv field are usable
+//
+// Super VGA Memory Models
+//
+typedef enum {
+  MemPL = 3,  // Planar memory model
+  MemPK = 4,  // Packed pixel memory model
+  MemRGB= 6,  // Direct color RGB memory model
+  MemYUV= 7   // Direct color YUV memory model
+} VESA_BIOS_EXTENSIONS_MEMORY_MODELS;
+
+//
+// Super VGA CRTC Information Block
+//
+typedef struct {
+  UINT16  HorizontalTotal;      // Horizontal total in pixels
+  UINT16  HorizontalSyncStart;  // Horizontal sync start in pixels
+  UINT16  HorizontalSyncEnd;    // Horizontal sync end in pixels
+  UINT16  VericalTotal;         // Vertical total in pixels
+  UINT16  VericalSyncStart;     // Vertical sync start in pixels
+  UINT16  VericalSyncEnd;       // Vertical sync end in pixels
+  UINT8   Flags;                // Flags (Interlaced/DoubleScan/etc).
+  UINT32  PixelClock;           // Pixel clock in units of Hz
+  UINT16  RefreshRate;          // Refresh rate in units of 0.01 Hz
+  UINT8   Reserved[40];         // Pad
+} VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK;
+
+#define VESA_BIOS_EXTENSIONS_CRTC_FLAGS_DOUBLE_SCAN 0x01  // 0: Graphics mode is not souble scanned
+// 1: Graphics mode is double scanned
+//
+#define VESA_BIOS_EXTENSIONS_CRTC_FLAGSINTERLACED 0x02  // 0: Graphics mode is not interlaced
+// 1: Graphics mode is interlaced
+//
+#define VESA_BIOS_EXTENSIONS_CRTC_HORIZONTAL_SYNC_NEGATIVE  0x04  // 0: Horizontal sync polarity is positive(+)
+// 0: Horizontal sync polarity is negative(-)
+//
+#define VESA_BIOS_EXTENSIONS_CRTC_VERITICAL_SYNC_NEGATIVE 0x08  // 0: Verical sync polarity is positive(+)
+// 0: Verical sync polarity is negative(-)
+//
+// Turn off byte packing of data structures
+//
+#pragma pack()
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Framework/BootScript.h b/OvmfPkg/Csm/Include/Framework/BootScript.h
new file mode 100644
index 0000000000..cb7220c1a7
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/BootScript.h
@@ -0,0 +1,41 @@
+/** @file
+  This file contains the boot script defintions that are shared between the
+  Boot Script Executor PPI and the Boot Script Save Protocol.
+
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _BOOT_SCRIPT_H_
+#define _BOOT_SCRIPT_H_
+
+#include <PiDxe.h>
+///
+/// The framework implementation defines follow opcode that are different from the PI specification:
+/// Add FRAMEWORK_ prefix to avoid naming conflict.
+///
+/// S3 Boot Script Table identifier.
+///
+#define FRAMEWORK_EFI_ACPI_S3_RESUME_SCRIPT_TABLE               0x00
+///
+/// The opcode is used to add a record for memory reads of the memory location and continues when the
+/// exit criteria is satisfied, or after a defined duration.
+///
+#define FRAMEWORK_EFI_BOOT_SCRIPT_MEM_POLL_OPCODE               0x09
+///
+/// The opcode is used to add a record for dispatching specified arbitrary code into a specified
+/// boot script table.
+///
+#define FRAMEWORK_EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE             0x0D
+///
+/// The opcode indicates the start of the boot script table.
+///
+#define FRAMEWORK_EFI_BOOT_SCRIPT_TABLE_OPCODE                  0xAA
+///
+/// The opcode indicates the end of the boot script table.
+///
+#define FRAMEWORK_EFI_BOOT_SCRIPT_TERMINATE_OPCODE              0xFF
+
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Framework/DxeCis.h b/OvmfPkg/Csm/Include/Framework/DxeCis.h
new file mode 100644
index 0000000000..98a947e420
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/DxeCis.h
@@ -0,0 +1,170 @@
+/** @file
+  Include file for definitions in the Intel Platform Innovation Framework for EFI
+  Driver Execution Environment Core Interface Specification (DXE CIS) Version 0.91.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _DXECIS_H_
+#define _DXECIS_H_
+
+#include <Protocol/StatusCode.h>
+
+/**
+  Functions of this type are used with the Framework MP Services Protocol and
+  the  SMM Services Table to execute a procedure on enabled APs.  The context
+  the AP should use durng execution is specified by Buffer.
+
+  @param[in]  Buffer   The pointer to the procedure's argument.
+
+**/
+typedef
+VOID
+(EFIAPI *FRAMEWORK_EFI_AP_PROCEDURE)(
+  IN  VOID  *Buffer
+  );
+
+///
+/// The Framework EFI Runtime Services Table as an extension to the EFI 1.10 Runtime Services Table.
+///
+typedef struct {
+  //
+  // Table header for the Framework EFI Runtime Services Table
+  //
+  EFI_TABLE_HEADER                  Hdr;
+  //
+  // Time services
+  //
+  EFI_GET_TIME                      GetTime;
+  EFI_SET_TIME                      SetTime;
+  EFI_GET_WAKEUP_TIME               GetWakeupTime;
+  EFI_SET_WAKEUP_TIME               SetWakeupTime;
+  //
+  // Virtual memory services
+  //
+  EFI_SET_VIRTUAL_ADDRESS_MAP       SetVirtualAddressMap;
+  EFI_CONVERT_POINTER               ConvertPointer;
+  //
+  // Variable services
+  //
+  EFI_GET_VARIABLE                  GetVariable;
+  EFI_GET_NEXT_VARIABLE_NAME        GetNextVariableName;
+  EFI_SET_VARIABLE                  SetVariable;
+  //
+  // Misc
+  //
+  EFI_GET_NEXT_HIGH_MONO_COUNT      GetNextHighMonotonicCount;
+  EFI_RESET_SYSTEM                  ResetSystem;
+  ///
+  /// A Framework extension to the EFI 1.10 runtime table.
+  /// It was moved to a protocol to avoid conflict with UEFI 2.0.
+  ///
+  EFI_REPORT_STATUS_CODE            ReportStatusCode;
+} FRAMEWORK_EFI_RUNTIME_SERVICES;
+
+///
+/// The Framework EFI Boot Services Table. Complies with the DxeCis specification.
+///
+typedef struct {
+  ///
+  /// The table header for the EFI Boot Services Table.
+  ///
+  EFI_TABLE_HEADER                Hdr;
+
+  //
+  // Task Priority Services
+  //
+  EFI_RAISE_TPL                   RaiseTPL;
+  EFI_RESTORE_TPL                 RestoreTPL;
+
+  //
+  // Memory Services
+  //
+  EFI_ALLOCATE_PAGES              AllocatePages;
+  EFI_FREE_PAGES                  FreePages;
+  EFI_GET_MEMORY_MAP              GetMemoryMap;
+  EFI_ALLOCATE_POOL               AllocatePool;
+  EFI_FREE_POOL                   FreePool;
+
+  //
+  // Event & Timer Services
+  //
+  EFI_CREATE_EVENT                  CreateEvent;
+  EFI_SET_TIMER                     SetTimer;
+  EFI_WAIT_FOR_EVENT                WaitForEvent;
+  EFI_SIGNAL_EVENT                  SignalEvent;
+  EFI_CLOSE_EVENT                   CloseEvent;
+  EFI_CHECK_EVENT                   CheckEvent;
+
+  //
+  // Protocol Handler Services
+  //
+  EFI_INSTALL_PROTOCOL_INTERFACE    InstallProtocolInterface;
+  EFI_REINSTALL_PROTOCOL_INTERFACE  ReinstallProtocolInterface;
+  EFI_UNINSTALL_PROTOCOL_INTERFACE  UninstallProtocolInterface;
+  EFI_HANDLE_PROTOCOL               HandleProtocol;
+  EFI_HANDLE_PROTOCOL               PcHandleProtocol;
+  EFI_REGISTER_PROTOCOL_NOTIFY      RegisterProtocolNotify;
+  EFI_LOCATE_HANDLE                 LocateHandle;
+  EFI_LOCATE_DEVICE_PATH            LocateDevicePath;
+  EFI_INSTALL_CONFIGURATION_TABLE   InstallConfigurationTable;
+
+  //
+  // Image Services
+  //
+  EFI_IMAGE_LOAD                    LoadImage;
+  EFI_IMAGE_START                   StartImage;
+  EFI_EXIT                          Exit;
+  EFI_IMAGE_UNLOAD                  UnloadImage;
+  EFI_EXIT_BOOT_SERVICES            ExitBootServices;
+
+  //
+  // Miscellaneous Services
+  //
+  EFI_GET_NEXT_MONOTONIC_COUNT      GetNextMonotonicCount;
+  EFI_STALL                         Stall;
+  EFI_SET_WATCHDOG_TIMER            SetWatchdogTimer;
+
+  //
+  // DriverSupport Services
+  //
+  EFI_CONNECT_CONTROLLER            ConnectController;
+  EFI_DISCONNECT_CONTROLLER         DisconnectController;
+
+  //
+  // Open and Close Protocol Services
+  //
+  EFI_OPEN_PROTOCOL                 OpenProtocol;
+  EFI_CLOSE_PROTOCOL                CloseProtocol;
+  EFI_OPEN_PROTOCOL_INFORMATION     OpenProtocolInformation;
+
+  //
+  // Library Services
+  //
+  EFI_PROTOCOLS_PER_HANDLE          ProtocolsPerHandle;
+  EFI_LOCATE_HANDLE_BUFFER          LocateHandleBuffer;
+  EFI_LOCATE_PROTOCOL               LocateProtocol;
+  EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES    InstallMultipleProtocolInterfaces;
+  EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES  UninstallMultipleProtocolInterfaces;
+
+  //
+  // 32-bit CRC Services
+  //
+  EFI_CALCULATE_CRC32               CalculateCrc32;
+
+  //
+  // Miscellaneous Services
+  //
+  EFI_COPY_MEM                      CopyMem;
+  EFI_SET_MEM                       SetMem;
+} FRAMEWORK_EFI_BOOT_SERVICES;
+
+#define EFI_EVENT_RUNTIME_CONTEXT       0x20000000
+#define EFI_EVENT_NOTIFY_SIGNAL_ALL     0x00000400
+#define EFI_EVENT_SIGNAL_READY_TO_BOOT  0x00000203
+#define EFI_EVENT_SIGNAL_LEGACY_BOOT    0x00000204
+
+#endif
+
diff --git a/OvmfPkg/Csm/Include/Framework/FirmwareVolumeHeader.h b/OvmfPkg/Csm/Include/Framework/FirmwareVolumeHeader.h
new file mode 100644
index 0000000000..e2b5a28b8b
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/FirmwareVolumeHeader.h
@@ -0,0 +1,79 @@
+/** @file
+  Defines the data structure that is the volume header found at the beginning of
+  all firmware volumes that are either memory mapped or have an
+  associated FirmwareVolumeBlock protocol.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  These definitions are from the Firmware Volume Block Spec 0.9.
+
+**/
+
+#ifndef __EFI_FIRMWARE_VOLUME_HEADER_H__
+#define __EFI_FIRMWARE_VOLUME_HEADER_H__
+
+///
+/// Firmware Volume Block Attributes bit definitions.
+///@{
+#define EFI_FVB_READ_DISABLED_CAP   0x00000001
+#define EFI_FVB_READ_ENABLED_CAP    0x00000002
+#define EFI_FVB_READ_STATUS         0x00000004
+
+#define EFI_FVB_WRITE_DISABLED_CAP  0x00000008
+#define EFI_FVB_WRITE_ENABLED_CAP   0x00000010
+#define EFI_FVB_WRITE_STATUS        0x00000020
+
+#define EFI_FVB_LOCK_CAP            0x00000040
+#define EFI_FVB_LOCK_STATUS         0x00000080
+
+#define EFI_FVB_STICKY_WRITE        0x00000200
+#define EFI_FVB_MEMORY_MAPPED       0x00000400
+#define EFI_FVB_ERASE_POLARITY      0x00000800
+
+#define EFI_FVB_ALIGNMENT_CAP       0x00008000
+#define EFI_FVB_ALIGNMENT_2         0x00010000
+#define EFI_FVB_ALIGNMENT_4         0x00020000
+#define EFI_FVB_ALIGNMENT_8         0x00040000
+#define EFI_FVB_ALIGNMENT_16        0x00080000
+#define EFI_FVB_ALIGNMENT_32        0x00100000
+#define EFI_FVB_ALIGNMENT_64        0x00200000
+#define EFI_FVB_ALIGNMENT_128       0x00400000
+#define EFI_FVB_ALIGNMENT_256       0x00800000
+#define EFI_FVB_ALIGNMENT_512       0x01000000
+#define EFI_FVB_ALIGNMENT_1K        0x02000000
+#define EFI_FVB_ALIGNMENT_2K        0x04000000
+#define EFI_FVB_ALIGNMENT_4K        0x08000000
+#define EFI_FVB_ALIGNMENT_8K        0x10000000
+#define EFI_FVB_ALIGNMENT_16K       0x20000000
+#define EFI_FVB_ALIGNMENT_32K       0x40000000
+#define EFI_FVB_ALIGNMENT_64K       0x80000000
+///@}
+
+/// This is a simple macro defined as the set of all FV Block Attributes signifying capabilities.
+#define EFI_FVB_CAPABILITIES  ( EFI_FVB_READ_DISABLED_CAP  | \
+                                EFI_FVB_READ_ENABLED_CAP   | \
+                                EFI_FVB_WRITE_DISABLED_CAP | \
+                                EFI_FVB_WRITE_ENABLED_CAP  | \
+                                EFI_FVB_LOCK_CAP \
+                              )
+
+/** A parameterized macro defining a boolean expression that tests the state of a particular bit.
+  *
+  * @param FvbAttributes  Indicates a test for CLEAR if EFI_FVB_ERASE_POLARITY is 1, else test for SET.
+  *
+  * @param TestAttributes The set of bits to test.
+  *
+  * @param Bit            A value indicating the bit(s) to test.
+  *                       If multiple bits are set, the logical OR of their tests is the expression's value.
+**/
+#define EFI_TEST_FFS_ATTRIBUTES_BIT( FvbAttributes, TestAttributes, Bit) \
+    ((BOOLEAN) \
+      ((FvbAttributes & EFI_FVB_ERASE_POLARITY) ? (((~TestAttributes) & Bit) == Bit) : ((TestAttributes & Bit) == Bit)) \
+    )
+
+/// A simple macro defined as the set of all FV Block Attribute bits that indicate status.
+#define EFI_FVB_STATUS    (EFI_FVB_READ_STATUS | EFI_FVB_WRITE_STATUS | EFI_FVB_LOCK_STATUS)
+
+#endif  /* __EFI_FIRMWARE_VOLUME_HEADER_H__ */
diff --git a/OvmfPkg/Csm/Include/Framework/FirmwareVolumeImageFormat.h b/OvmfPkg/Csm/Include/Framework/FirmwareVolumeImageFormat.h
new file mode 100644
index 0000000000..c77e39b4c0
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/FirmwareVolumeImageFormat.h
@@ -0,0 +1,32 @@
+/** @file
+  This file defines the data structures that are architecturally defined for file
+  images loaded via the FirmwareVolume protocol.  The Firmware Volume specification
+  is the basis for these definitions.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  These definitions are from the Firmware Volume Spec 0.9.
+
+**/
+
+#ifndef __FIRMWARE_VOLUME_IMAGE_FORMAT_H__
+#define __FIRMWARE_VOLUME_IMAGE_FORMAT_H__
+
+//
+// Bit values for AuthenticationStatus
+//
+#define EFI_AGGREGATE_AUTH_STATUS_PLATFORM_OVERRIDE 0x000001
+#define EFI_AGGREGATE_AUTH_STATUS_IMAGE_SIGNED      0x000002
+#define EFI_AGGREGATE_AUTH_STATUS_NOT_TESTED        0x000004
+#define EFI_AGGREGATE_AUTH_STATUS_TEST_FAILED       0x000008
+#define EFI_AGGREGATE_AUTH_STATUS_ALL               0x00000f
+
+#define EFI_LOCAL_AUTH_STATUS_PLATFORM_OVERRIDE     0x010000
+#define EFI_LOCAL_AUTH_STATUS_IMAGE_SIGNED          0x020000
+#define EFI_LOCAL_AUTH_STATUS_NOT_TESTED            0x040000
+#define EFI_LOCAL_AUTH_STATUS_TEST_FAILED           0x080000
+#define EFI_LOCAL_AUTH_STATUS_ALL                   0x0f0000
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Framework/FrameworkInternalFormRepresentation.h b/OvmfPkg/Csm/Include/Framework/FrameworkInternalFormRepresentation.h
new file mode 100644
index 0000000000..04cbae1ef5
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/FrameworkInternalFormRepresentation.h
@@ -0,0 +1,397 @@
+/** @file
+  This file defines the encoding for the VFR (Visual Form Representation) language.
+  Framework IFR is primarily consumed by the EFI presentation engine, and produced by EFI
+  internal application and drivers as well as all add-in card option-ROM drivers
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  These definitions are from the Framework Specification HII 0.92.
+
+**/
+
+#ifndef __FRAMEWORK_INTERNAL_FORMREPRESENTATION_H__
+#define __FRAMEWORK_INTERNAL_FORMREPRESENTATION_H__
+
+typedef UINT16  STRING_REF;
+
+//
+// IFR Op codes
+//
+#define FRAMEWORK_EFI_IFR_FORM_OP                 0x01
+#define FRAMEWORK_EFI_IFR_SUBTITLE_OP             0x02
+#define FRAMEWORK_EFI_IFR_TEXT_OP                 0x03
+#define EFI_IFR_GRAPHIC_OP                        0x04
+#define FRAMEWORK_EFI_IFR_ONE_OF_OP               0x05
+#define FRAMEWORK_EFI_IFR_CHECKBOX_OP             0x06
+#define FRAMEWORK_EFI_IFR_NUMERIC_OP              0x07
+#define FRAMEWORK_EFI_IFR_PASSWORD_OP             0x08
+#define FRAMEWORK_EFI_IFR_ONE_OF_OPTION_OP        0x09  ///< ONEOF OPTION field.
+#define FRAMEWORK_EFI_IFR_SUPPRESS_IF_OP          0x0A
+#define EFI_IFR_END_FORM_OP                       0x0B
+#define EFI_IFR_HIDDEN_OP                         0x0C
+#define EFI_IFR_END_FORM_SET_OP                   0x0D
+#define FRAMEWORK_EFI_IFR_FORM_SET_OP             0x0E
+#define FRAMEWORK_EFI_IFR_REF_OP                  0x0F
+#define EFI_IFR_END_ONE_OF_OP                     0x10
+#define FRAMEWORK_EFI_IFR_END_OP                  EFI_IFR_END_ONE_OF_OP
+#define FRAMEWORK_EFI_IFR_INCONSISTENT_IF_OP      0x11
+#define FRAMEWORK_EFI_IFR_EQ_ID_VAL_OP            0x12
+#define FRAMEWORK_EFI_IFR_EQ_ID_ID_OP             0x13
+#define FRAMEWORK_EFI_IFR_EQ_ID_LIST_OP           0x14
+#define FRAMEWORK_EFI_IFR_AND_OP                  0x15
+#define FRAMEWORK_EFI_IFR_OR_OP                   0x16
+#define FRAMEWORK_EFI_IFR_NOT_OP                  0x17
+#define EFI_IFR_END_IF_OP                         0x18  ///< For endif of inconsistentif, suppressif, grayoutif.
+#define EFI_IFR_GRAYOUT_IF_OP                     0x19
+#define FRAMEWORK_EFI_IFR_DATE_OP                 0x1A
+#define FRAMEWORK_EFI_IFR_TIME_OP                 0x1B
+#define FRAMEWORK_EFI_IFR_STRING_OP               0x1C
+#define EFI_IFR_LABEL_OP                          0x1D
+#define EFI_IFR_SAVE_DEFAULTS_OP                  0x1E
+#define EFI_IFR_RESTORE_DEFAULTS_OP               0x1F
+#define EFI_IFR_BANNER_OP                         0x20
+#define EFI_IFR_INVENTORY_OP                      0x21
+#define EFI_IFR_EQ_VAR_VAL_OP                     0x22
+#define FRAMEWORK_EFI_IFR_ORDERED_LIST_OP         0x23
+#define FRAMEWORK_EFI_IFR_VARSTORE_OP             0x24
+#define EFI_IFR_VARSTORE_SELECT_OP                0x25
+#define EFI_IFR_VARSTORE_SELECT_PAIR_OP           0x26
+#define EFI_IFR_LAST_OPCODE                       EFI_IFR_VARSTORE_SELECT_PAIR_OP
+#define EFI_IFR_OEM_OP                            0xFE
+#define EFI_IFR_NV_ACCESS_COMMAND                 0xFF
+
+//
+// Define values for the flags fields in some VFR opcodes. These are
+// bitmasks.
+//
+#define EFI_IFR_FLAG_DEFAULT            0x01
+#define EFI_IFR_FLAG_MANUFACTURING      0x02
+#define EFI_IFR_FLAG_INTERACTIVE        0x04
+#define EFI_IFR_FLAG_NV_ACCESS          0x08
+#define EFI_IFR_FLAG_RESET_REQUIRED     0x10
+#define EFI_IFR_FLAG_LATE_CHECK         0x20
+
+#define EFI_NON_DEVICE_CLASS              0x00  ///< Useful when you do not want something in the Device Manager.
+#define EFI_DISK_DEVICE_CLASS             0x01
+#define EFI_VIDEO_DEVICE_CLASS            0x02
+#define EFI_NETWORK_DEVICE_CLASS          0x04
+#define EFI_INPUT_DEVICE_CLASS            0x08
+#define EFI_ON_BOARD_DEVICE_CLASS         0x10
+#define EFI_OTHER_DEVICE_CLASS            0x20
+
+#define EFI_SETUP_APPLICATION_SUBCLASS    0x00
+#define EFI_GENERAL_APPLICATION_SUBCLASS  0x01
+#define EFI_FRONT_PAGE_SUBCLASS           0x02
+#define EFI_SINGLE_USE_SUBCLASS           0x03  ///< Used to display a single entity ,and then exit.
+
+///
+/// Used to flag dynamically created op-codes. This is meaningful to the IFR Library set
+/// and the browser because we need to distinguish between compiled NV map data and created data.
+/// We do not allow new entries to be created in the NV map dynamically, but we do need
+/// to display this information correctly.  To dynamically create op-codes and assume that their
+/// data will be saved, ensure that the NV starting location they refer to is pre-defined in the
+/// NV map.
+///
+#define EFI_IFR_FLAG_CREATED  128
+
+
+#pragma pack(1)
+//
+// IFR Structure definitions
+//
+typedef struct {
+  UINT8                             OpCode;
+  UINT8                             Length;
+} FRAMEWORK_EFI_IFR_OP_HEADER;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  EFI_GUID                          Guid;
+  STRING_REF                        FormSetTitle;
+  STRING_REF                        Help;
+  EFI_PHYSICAL_ADDRESS              CallbackHandle;
+  UINT16                            Class;
+  UINT16                            SubClass;
+  UINT16                            NvDataSize; ///< Set once; the size of the NV data as defined in the script.
+} FRAMEWORK_EFI_IFR_FORM_SET;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            FormId;
+  STRING_REF                        FormTitle;
+} FRAMEWORK_EFI_IFR_FORM;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            LabelId;
+} EFI_IFR_LABEL;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        SubTitle;
+} FRAMEWORK_EFI_IFR_SUBTITLE;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        Help;
+  STRING_REF                        Text;
+  STRING_REF                        TextTwo;
+  UINT8                             Flags;  ///< This is included solely for purposes of interactive/dynamic support.
+  UINT16                            Key;    ///< The value to be passed to the caller to identify this particular op-code.
+} FRAMEWORK_EFI_IFR_TEXT;
+
+//
+// goto
+//
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            FormId;
+  STRING_REF                        Prompt;
+  STRING_REF                        Help;   ///< The string Token for the context-help.
+  UINT8                             Flags;  ///< This is included solely for purposes of interactive/dynamic support.
+  UINT16                            Key;    ///< The value to be passed to the caller to identify this particular op-code.
+} FRAMEWORK_EFI_IFR_REF;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} EFI_IFR_END_FORM;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} EFI_IFR_END_FORM_SET;
+
+//
+// Also notice that the IFR_ONE_OF and IFR_CHECK_BOX are identical in structure......
+// code assumes this to be true, if this ever changes we need to revisit the InitializeTagStructures code
+//
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId; ///< The ID designating what the question is about...
+  UINT8                             Width;      ///< The Size of the Data being saved.
+  STRING_REF                        Prompt;     ///< The String Token for the Prompt.
+  STRING_REF                        Help;       ///< The string Token for the context-help.
+} FRAMEWORK_EFI_IFR_ONE_OF;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId; ///< The offset in NV for storage of the data.
+  UINT8                             MaxEntries; ///< The maximum number of options in the ordered list (=size of NVStore).
+  STRING_REF                        Prompt;     ///< The string token for the prompt.
+  STRING_REF                        Help;       ///< The string token for the context-help.
+} FRAMEWORK_EFI_IFR_ORDERED_LIST;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId; ///< The ID designating what the question is about...
+  UINT8                             Width;      ///< The Size of the Data being saved.
+  STRING_REF                        Prompt;     ///< The String Token for the Prompt.
+  STRING_REF                        Help;       ///< The string Token for the context-help.
+  UINT8                             Flags;      ///< If non-zero, it means that it is the default option.
+  UINT16                            Key;        ///< Value to be passed to caller to identify this particular op-code.
+} FRAMEWORK_EFI_IFR_CHECKBOX, EFI_IFR_CHECK_BOX;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        Option;     ///< The string token describing the option.
+  UINT16                            Value;      ///< The value associated with this option that is stored in the NVRAM.
+  UINT8                             Flags;      ///< If non-zero, it means that it is the default option.
+  UINT16                            Key;        ///< Value to be passed to caller to identify this particular op-code.
+} FRAMEWORK_EFI_IFR_ONE_OF_OPTION;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId; ///< The ID designating what the question is about...
+  UINT8                             Width;      ///< The Size of the Data being saved.
+  STRING_REF                        Prompt;     ///< The String Token for the Prompt.
+  STRING_REF                        Help;       ///< The string Token for the context-help.
+  UINT8                             Flags;      ///< This is included solely for purposes of interactive/dynamic support.
+  UINT16                            Key;        ///< The value to be passed to caller to identify this particular op-code.
+  UINT16                            Minimum;
+  UINT16                            Maximum;
+  UINT16                            Step;       ///< Zero means manual input. Otherwise, arrow selection is called for.
+  UINT16                            Default;
+} FRAMEWORK_EFI_IFR_NUMERIC;
+
+//
+// There is an interesting twist with regards to Time and Date.  This is one of the few items which can accept input
+// from a user, and may or may not need to use storage in the NVRAM space.  The decided method for determining
+// if NVRAM space will be used (only for a TimeOp or DateOp) is:  If .QuestionId == 0 && .Width == 0 (normally an
+// impossibility) then use system resources to store the data away and not NV resources.  In other words, the setup
+// engine will call gRT->SetTime, and gRT->SetDate for the saving of data, and the values displayed will be from the
+// gRT->GetXXXX series of calls.
+//
+typedef struct {
+  FRAMEWORK_EFI_IFR_NUMERIC         Hour;
+  FRAMEWORK_EFI_IFR_NUMERIC         Minute;
+  FRAMEWORK_EFI_IFR_NUMERIC         Second;
+} FRAMEWORK_EFI_IFR_TIME;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_NUMERIC         Year;
+  FRAMEWORK_EFI_IFR_NUMERIC         Month;
+  FRAMEWORK_EFI_IFR_NUMERIC         Day;
+} FRAMEWORK_EFI_IFR_DATE;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId;///< The ID designating what the question is about...
+  UINT8                             Width;     ///< The Size of the Data being saved.
+  STRING_REF                        Prompt;    ///< The String Token for the Prompt.
+  STRING_REF                        Help;      ///< The string Token for the context-help.
+  UINT8                             Flags;     ///< This is included solely for purposes of interactive/dynamic support.
+  UINT16                            Key;       ///< The value to be passed to caller to identify this particular op-code.
+  UINT8                             MinSize;   ///< Minimum allowable sized password.
+  UINT8                             MaxSize;   ///< Maximum allowable sized password.
+  UINT16                            Encoding;
+} FRAMEWORK_EFI_IFR_PASSWORD;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId; ///< The ID designating what the question is about...
+  UINT8                             Width;      ///< The Size of the Data being saved.
+  STRING_REF                        Prompt;     ///< The String Token for the Prompt.
+  STRING_REF                        Help;       ///< The string Token for the context-help.
+  UINT8                             Flags;      ///< This is included solely for purposes of interactive/dynamic support.
+  UINT16                            Key;        ///< The value to be passed to caller to identify this particular op-code.
+  UINT8                             MinSize;    ///< Minimum allowable sized password.
+  UINT8                             MaxSize;    ///< Maximum allowable sized password.
+} FRAMEWORK_EFI_IFR_STRING;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} EFI_IFR_END_ONE_OF;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            Value;
+  UINT16                            Key;
+} EFI_IFR_HIDDEN;
+
+///
+/// Inconsistent with specification here:
+/// The following defintion may not comply with Framework Specification HII 0.92. To
+/// keep the inconsistant is for implementation needed.
+///@{
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT8                             Flags;
+} EFI_IFR_SUPPRESS;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT8                             Flags;
+} EFI_IFR_GRAY_OUT;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        Popup;
+  UINT8                             Flags;
+} EFI_IFR_INCONSISTENT;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId;   ///< The offset into variable storage.
+  UINT8                             Width;        ///< The size of variable storage.
+  UINT16                            Value;        ///< The value to compare against.
+} FRAMEWORK_EFI_IFR_EQ_ID_VAL;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId;   ///< The offset into variable storage.
+  UINT8                             Width;        ///< The size of variable storage.
+  UINT16                            ListLength;
+  UINT16                            ValueList[1];
+} FRAMEWORK_EFI_IFR_EQ_ID_LIST;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            QuestionId1;  ///< The offset into variable storage for first value to compare.
+  UINT8                             Width;        ///< The size of variable storage (must be same for both).
+  UINT16                            QuestionId2;  ///< The offset into variable storage for second value to compare.
+} FRAMEWORK_EFI_IFR_EQ_ID_ID;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            VariableId;   ///< The offset into variable storage.
+  UINT16                            Value;        ///< The value to compare against.
+} EFI_IFR_EQ_VAR_VAL;
+///@}
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} FRAMEWORK_EFI_IFR_AND;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} FRAMEWORK_EFI_IFR_OR;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} FRAMEWORK_EFI_IFR_NOT;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+} EFI_IFR_END_EXPR, EFI_IFR_END_IF;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            FormId;
+  STRING_REF                        Prompt;
+  STRING_REF                        Help;
+  UINT8                             Flags;
+  UINT16                            Key;
+} EFI_IFR_SAVE_DEFAULTS;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        Help;
+  STRING_REF                        Text;
+  STRING_REF                        TextTwo;    ///< Optional text.
+} EFI_IFR_INVENTORY;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  EFI_GUID                          Guid;       ///< GUID for the variable.
+  UINT16                            VarId;      ///< The variable store ID, as referenced elsewhere in the form.
+  UINT16                            Size;       ///< The size of the variable storage.
+} FRAMEWORK_EFI_IFR_VARSTORE;
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            VarId;      ///< The variable store ID, as referenced elsewhere in the form.
+} EFI_IFR_VARSTORE_SELECT;
+
+///
+/// Used for the ideqid VFR statement where two variable stores may be referenced in the
+/// same VFR statement.
+/// A browser should treat this as an FRAMEWORK_EFI_IFR_VARSTORE_SELECT statement and assume that all following
+/// IFR opcodes use the VarId as defined here.
+///
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  UINT16                            VarId;          ///< The variable store ID, as referenced elsewhere in the form.
+  UINT16                            SecondaryVarId; ///< The variable store ID, as referenced elsewhere in the form.
+} EFI_IFR_VARSTORE_SELECT_PAIR;
+
+///
+/// Save defaults and restore defaults have same structure.
+///
+#define EFI_IFR_RESTORE_DEFAULTS  EFI_IFR_SAVE_DEFAULTS
+
+typedef struct {
+  FRAMEWORK_EFI_IFR_OP_HEADER       Header;
+  STRING_REF                        Title;        ///< The string token for the banner title.
+  UINT16                            LineNumber;   ///< 1-based line number.
+  UINT8                             Alignment;    ///< Left, center, or right-aligned.
+} EFI_IFR_BANNER;
+
+#define EFI_IFR_BANNER_ALIGN_LEFT   0
+#define EFI_IFR_BANNER_ALIGN_CENTER 1
+#define EFI_IFR_BANNER_ALIGN_RIGHT  2
+#define EFI_IFR_BANNER_TIMEOUT      0xFF
+
+#pragma pack()
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Framework/Hob.h b/OvmfPkg/Csm/Include/Framework/Hob.h
new file mode 100644
index 0000000000..f6a71fabe4
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/Hob.h
@@ -0,0 +1,28 @@
+/** @file
+  This file defines the data structures per HOB specification v0.9.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  These definitions are from the HOB Spec 0.9 that were not adopted by the PI specifications.
+
+**/
+
+#ifndef _HOB_H_
+#define _HOB_H_
+
+///
+/// Capsule volume HOB -- identical to a firmware volume.
+/// This macro is defined to comply with the hob Framework Spec. And the marco was
+/// retired in the PI1.0 specification.
+///
+#define EFI_HOB_TYPE_CV           0x0008
+
+typedef struct {
+  EFI_HOB_GENERIC_HEADER            Header;
+  EFI_PHYSICAL_ADDRESS              BaseAddress;
+  UINT64                            Length;
+} EFI_HOB_CAPSULE_VOLUME;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Framework/StatusCode.h b/OvmfPkg/Csm/Include/Framework/StatusCode.h
new file mode 100644
index 0000000000..753029c13c
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Framework/StatusCode.h
@@ -0,0 +1,155 @@
+/** @file
+  Status Code Definitions, according to Intel Platform Innovation Framework
+  for EFI Status Codes Specification
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  Intel Platform Innovation Framework for EFI Status Codes Specification
+  Version 0.92.
+
+**/
+
+#ifndef _FRAMEWORK_STATUS_CODE_H_
+#define _FRAMEWORK_STATUS_CODE_H_
+
+//
+// Required for X64 defines for CPU exception types
+//
+#include <Protocol/DebugSupport.h>
+
+///
+/// Software Class DXE BS Driver Subclass Progress Code definitions.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS     (EFI_SUBCLASS_SPECIFIC | 0x00000005)
+#define EFI_SW_DXE_BS_PC_VERIFYING_PASSWORD           (EFI_SUBCLASS_SPECIFIC | 0x00000006)
+///@}
+
+///
+/// Software Class DXE RT Driver Subclass Progress Code definitions.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_SW_DXE_RT_PC_S0 (EFI_SUBCLASS_SPECIFIC | 0x00000000)
+#define EFI_SW_DXE_RT_PC_S1 (EFI_SUBCLASS_SPECIFIC | 0x00000001)
+#define EFI_SW_DXE_RT_PC_S2 (EFI_SUBCLASS_SPECIFIC | 0x00000002)
+#define EFI_SW_DXE_RT_PC_S3 (EFI_SUBCLASS_SPECIFIC | 0x00000003)
+#define EFI_SW_DXE_RT_PC_S4 (EFI_SUBCLASS_SPECIFIC | 0x00000004)
+#define EFI_SW_DXE_RT_PC_S5 (EFI_SUBCLASS_SPECIFIC | 0x00000005)
+///@}
+
+///
+/// Software Subclass definitions.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+#define EFI_SOFTWARE_X64_EXCEPTION        (EFI_SOFTWARE | 0x00130000)
+
+///
+/// Software Class X64 Exception Subclass Error Code definitions.
+/// These exceptions are derived from the debug protocol definitions in the EFI
+/// specification.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_SW_EC_X64_DIVIDE_ERROR      EXCEPT_X64_DIVIDE_ERROR
+#define EFI_SW_EC_X64_DEBUG             EXCEPT_X64_DEBUG
+#define EFI_SW_EC_X64_NMI               EXCEPT_X64_NMI
+#define EFI_SW_EC_X64_BREAKPOINT        EXCEPT_X64_BREAKPOINT
+#define EFI_SW_EC_X64_OVERFLOW          EXCEPT_X64_OVERFLOW
+#define EFI_SW_EC_X64_BOUND             EXCEPT_X64_BOUND
+#define EFI_SW_EC_X64_INVALID_OPCODE    EXCEPT_X64_INVALID_OPCODE
+#define EFI_SW_EC_X64_DOUBLE_FAULT      EXCEPT_X64_DOUBLE_FAULT
+#define EFI_SW_EC_X64_INVALID_TSS       EXCEPT_X64_INVALID_TSS
+#define EFI_SW_EC_X64_SEG_NOT_PRESENT   EXCEPT_X64_SEG_NOT_PRESENT
+#define EFI_SW_EC_X64_STACK_FAULT       EXCEPT_X64_STACK_FAULT
+#define EFI_SW_EC_X64_GP_FAULT          EXCEPT_X64_GP_FAULT
+#define EFI_SW_EC_X64_PAGE_FAULT        EXCEPT_X64_PAGE_FAULT
+#define EFI_SW_EC_X64_FP_ERROR          EXCEPT_X64_FP_ERROR
+#define EFI_SW_EC_X64_ALIGNMENT_CHECK   EXCEPT_X64_ALIGNMENT_CHECK
+#define EFI_SW_EC_X64_MACHINE_CHECK     EXCEPT_X64_MACHINE_CHECK
+#define EFI_SW_EC_X64_SIMD              EXCEPT_X64_SIMD
+///@}
+
+///
+/// Software Class EFI After Life Subclass Progress Code definitions.
+///
+///@{
+#define EFI_SW_AL_PC_ENTRY_POINT    (EFI_SUBCLASS_SPECIFIC | 0x00000000)
+#define EFI_SW_AL_PC_RETURN_TO_LAST (EFI_SUBCLASS_SPECIFIC | 0x00000001)
+///@}
+
+///
+/// Software Class DXE Core Subclass Error Code definitions.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+#define EFI_SW_CSM_LEGACY_ROM_INIT                (EFI_SUBCLASS_SPECIFIC | 0x00000000)
+
+///
+/// IO Bus Class ATA/ATAPI Subclass Progress Code definitions.
+///
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_IOB_ATA_BUS_SMART_ENABLE          (EFI_SUBCLASS_SPECIFIC | 0x00000000)
+#define EFI_IOB_ATA_BUS_SMART_DISABLE         (EFI_SUBCLASS_SPECIFIC | 0x00000001)
+#define EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD   (EFI_SUBCLASS_SPECIFIC | 0x00000002)
+#define EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD  (EFI_SUBCLASS_SPECIFIC | 0x00000003)
+///@}
+
+///
+/// IO Bus Class ATA/ATAPI Subclass Error Code definitions.
+///
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED  (EFI_SUBCLASS_SPECIFIC | 0x00000000)
+#define EFI_IOB_ATA_BUS_SMART_DISABLED      (EFI_SUBCLASS_SPECIFIC | 0x00000001)
+///@}
+
+///
+/// The reason that the processor was disabled.
+///
+/// Inconsistent with specification here:
+/// The Framework Specification, StatusCodes 0.92, does not define the macros.
+///
+///@{
+#define EFI_CPU_CAUSE_NOT_DISABLED              0x0000
+///@}
+
+///
+/// Software Class PEI Module Subclass Progress Code definitions.
+///
+///@{
+#define EFI_SW_PEIM_PC_RECOVERY_BEGIN  EFI_SW_PEI_PC_RECOVERY_BEGIN
+#define EFI_SW_PEIM_PC_CAPSULE_LOAD    EFI_SW_PEI_PC_CAPSULE_LOAD
+#define EFI_SW_PEIM_PC_CAPSULE_START   EFI_SW_PEI_PC_CAPSULE_START
+#define EFI_SW_PEIM_PC_RECOVERY_USER   EFI_SW_PEI_PC_RECOVERY_USER
+#define EFI_SW_PEIM_PC_RECOVERY_AUTO   EFI_SW_PEI_PC_RECOVERY_AUTO
+///@}
+
+///
+/// Software Class PEI Core Subclass Error Code definitions.
+///
+///@{
+#define EFI_SW_PEIM_CORE_EC_DXE_CORRUPT       EFI_SW_PEI_CORE_EC_DXE_CORRUPT
+#define EFI_SW_PEIM_CORE_EC_DXEIPL_NOT_FOUND  EFI_SW_PEI_CORE_EC_DXEIPL_NOT_FOUND
+///@}
+
+#endif
diff --git a/OvmfPkg/Csm/Include/FrameworkDxe.h b/OvmfPkg/Csm/Include/FrameworkDxe.h
new file mode 100644
index 0000000000..1d801960fe
--- /dev/null
+++ b/OvmfPkg/Csm/Include/FrameworkDxe.h
@@ -0,0 +1,26 @@
+/** @file
+  The root header file that provides Framework extension to UEFI/PI for modules. It can be included by
+  DXE, RUNTIME and SMM type modules that use Framework definitions.
+
+
+  This header file includes Framework extension definitions common to DXE
+  modules.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _FRAMEWORK_DXE_H_
+#define _FRAMEWORK_DXE_H_
+
+#include <PiDxe.h>
+
+#include <Framework/FrameworkInternalFormRepresentation.h>
+#include <Framework/FirmwareVolumeImageFormat.h>
+#include <Framework/FirmwareVolumeHeader.h>
+#include <Framework/Hob.h>
+#include <Framework/BootScript.h>
+#include <Framework/StatusCode.h>
+#include <Framework/DxeCis.h>
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Guid/LegacyBios.h b/OvmfPkg/Csm/Include/Guid/LegacyBios.h
new file mode 100644
index 0000000000..e35fbff8d5
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Guid/LegacyBios.h
@@ -0,0 +1,29 @@
+/** @file
+  Defines a Tag GUID used to mark a UEFI legacy BIOS thunk driver based
+  on legacy BIOS services and legacy option ROM. This Tag GUID must be installed on
+  the ImageHandle of any module that follows the EFI Driver Model and uses
+  the Int86() or FarCall() services of the Legacy Bios Protocol to produce
+  a standard UEFI I/O Protocol.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _LEGACY_BIOS_H_
+#define _LEGACY_BIOS_H_
+
+///
+/// The Global ID for the Legacy BIOS GUID that must be installed onto the ImageHandle
+/// of any module follows the EFI Driver Model and uses the Int86() or FarCall()
+/// services of the Legacy BIOS Protocol to produce a standard UEFI I/O Protocol.
+///
+#define EFI_LEGACY_BIOS_GUID \
+  { \
+    0x2e3044ac, 0x879f, 0x490f, {0x97, 0x60, 0xbb, 0xdf, 0xaf, 0x69, 0x5f, 0x50 } \
+  }
+
+extern EFI_GUID gEfiLegacyBiosGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Guid/LegacyDevOrder.h b/OvmfPkg/Csm/Include/Guid/LegacyDevOrder.h
new file mode 100644
index 0000000000..8ec3de5e38
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Guid/LegacyDevOrder.h
@@ -0,0 +1,39 @@
+/** @file
+  Guid of a NV Variable which store the information about the
+  FD/HD/CD/NET/BEV order.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __LEGACY_DEV_ORDER_VARIABLE_GUID_H__
+#define __LEGACY_DEV_ORDER_VARIABLE_GUID_H__
+
+///
+/// Name and Guid of a NV Variable which stores the information about the
+/// FD/HD/CD/NET/BEV order
+///
+#define EFI_LEGACY_DEV_ORDER_VARIABLE_GUID \
+  { \
+  0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52} \
+  }
+
+typedef UINT8 BBS_TYPE;
+
+#pragma pack(1)
+typedef struct {
+  BBS_TYPE  BbsType;
+  ///
+  /// Length = sizeof (UINT16) + sizeof (Data)
+  ///
+  UINT16    Length;
+  UINT16    Data[1];
+} LEGACY_DEV_ORDER_ENTRY;
+#pragma pack()
+
+#define VAR_LEGACY_DEV_ORDER L"LegacyDevOrder"
+
+extern EFI_GUID gEfiLegacyDevOrderVariableGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/FirmwareVolume.h b/OvmfPkg/Csm/Include/Protocol/FirmwareVolume.h
new file mode 100644
index 0000000000..5e0216c765
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/FirmwareVolume.h
@@ -0,0 +1,340 @@
+/** @file
+  This file declares the Firmware Volume Protocol.
+
+  The Firmware Volume Protocol provides file-level access to the firmware volume.
+  Each firmware volume driver must produce an instance of the Firmware Volume
+  Protocol if the firmware volume is to be visible to the system. The Firmware
+  Volume Protocol also provides mechanisms for determining and modifying some
+  attributes of the firmware volume.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  This protocol is defined in Firmware Volume specification.
+  Version 0.9.
+
+**/
+
+#ifndef _FIRMWARE_VOLUME_H_
+#define _FIRMWARE_VOLUME_H_
+
+
+//
+// Firmware Volume Protocol GUID definition
+//
+#define EFI_FIRMWARE_VOLUME_PROTOCOL_GUID \
+  { \
+    0x389F751F, 0x1838, 0x4388, {0x83, 0x90, 0xCD, 0x81, 0x54, 0xBD, 0x27, 0xF8 } \
+  }
+
+#define FV_DEVICE_SIGNATURE SIGNATURE_32 ('_', 'F', 'V', '_')
+
+typedef struct _EFI_FIRMWARE_VOLUME_PROTOCOL  EFI_FIRMWARE_VOLUME_PROTOCOL;
+
+//
+// FRAMEWORK_EFI_FV_ATTRIBUTES bit definitions
+//
+typedef UINT64  FRAMEWORK_EFI_FV_ATTRIBUTES;
+
+//
+// ************************************************************
+// FRAMEWORK_EFI_FV_ATTRIBUTES bit definitions
+// ************************************************************
+//
+#define EFI_FV_READ_DISABLE_CAP       0x0000000000000001ULL
+#define EFI_FV_READ_ENABLE_CAP        0x0000000000000002ULL
+#define EFI_FV_READ_STATUS            0x0000000000000004ULL
+
+#define EFI_FV_WRITE_DISABLE_CAP      0x0000000000000008ULL
+#define EFI_FV_WRITE_ENABLE_CAP       0x0000000000000010ULL
+#define EFI_FV_WRITE_STATUS           0x0000000000000020ULL
+
+#define EFI_FV_LOCK_CAP               0x0000000000000040ULL
+#define EFI_FV_LOCK_STATUS            0x0000000000000080ULL
+#define EFI_FV_WRITE_POLICY_RELIABLE  0x0000000000000100ULL
+
+#define EFI_FV_ALIGNMENT_CAP          0x0000000000008000ULL
+#define EFI_FV_ALIGNMENT_2            0x0000000000010000ULL
+#define EFI_FV_ALIGNMENT_4            0x0000000000020000ULL
+#define EFI_FV_ALIGNMENT_8            0x0000000000040000ULL
+#define EFI_FV_ALIGNMENT_16           0x0000000000080000ULL
+#define EFI_FV_ALIGNMENT_32           0x0000000000100000ULL
+#define EFI_FV_ALIGNMENT_64           0x0000000000200000ULL
+#define EFI_FV_ALIGNMENT_128          0x0000000000400000ULL
+#define EFI_FV_ALIGNMENT_256          0x0000000000800000ULL
+#define EFI_FV_ALIGNMENT_512          0x0000000001000000ULL
+#define EFI_FV_ALIGNMENT_1K           0x0000000002000000ULL
+#define EFI_FV_ALIGNMENT_2K           0x0000000004000000ULL
+#define EFI_FV_ALIGNMENT_4K           0x0000000008000000ULL
+#define EFI_FV_ALIGNMENT_8K           0x0000000010000000ULL
+#define EFI_FV_ALIGNMENT_16K          0x0000000020000000ULL
+#define EFI_FV_ALIGNMENT_32K          0x0000000040000000ULL
+#define EFI_FV_ALIGNMENT_64K          0x0000000080000000ULL
+
+//
+// Protocol API definitions
+//
+
+/**
+  Retrieves attributes, insures positive polarity of attribute bits, and returns
+  resulting attributes in an output parameter.
+
+  @param  This                  Indicates the EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  Attributes            Output buffer containing attributes.
+
+  @retval EFI_SUCCESS           The firmware volume attributes were returned.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_GET_ATTRIBUTES)(
+  IN  EFI_FIRMWARE_VOLUME_PROTOCOL            *This,
+  OUT FRAMEWORK_EFI_FV_ATTRIBUTES             *Attributes
+  );
+
+/**
+  Sets volume attributes
+
+  @param  This                  Indicates the EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  Attributes            On input, Attributes is a pointer to an
+                                EFI_FV_ATTRIBUTES containing the desired firmware
+                                volume settings. On successful return, it contains
+                                the new settings of the firmware volume. On
+                                unsuccessful return, Attributes is not modified
+                                and the firmware volume settings are not changed.
+
+  @retval EFI_INVALID_PARAMETER A bit in Attributes was invalid.
+  @retval EFI_SUCCESS           The requested firmware volume attributes were set
+                                and the resulting EFI_FV_ATTRIBUTES is returned in
+                                Attributes.
+  @retval EFI_ACCESS_DENIED     The Device is locked and does not permit modification.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_SET_ATTRIBUTES)(
+  IN EFI_FIRMWARE_VOLUME_PROTOCOL       *This,
+  IN OUT FRAMEWORK_EFI_FV_ATTRIBUTES    *Attributes
+  );
+
+/**
+  Read the requested file (NameGuid) or file information from the firmware volume
+  and returns data in Buffer.
+
+  @param  This                  The EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  NameGuid              The pointer to EFI_GUID, which is the filename of
+                                the file to read.
+  @param  Buffer                The pointer to pointer to buffer in which contents of file are returned.
+                                <br>
+                                If Buffer is NULL, only type, attributes, and size
+                                are returned as there is no output buffer.
+                                <br>
+                                If Buffer != NULL and *Buffer == NULL, the output
+                                buffer is allocated from BS pool by ReadFile.
+                                <br>
+                                If Buffer != NULL and *Buffer != NULL, the output
+                                buffer has been allocated by the caller and is being
+                                passed in.
+  @param  BufferSize            On input: The buffer size. On output: The size
+                                required to complete the read.
+  @param  FoundType             The pointer to the type of the file whose data
+                                is returned.
+  @param  FileAttributes        The pointer to attributes of the file whose data
+                                is returned.
+  @param  AuthenticationStatus  The pointer to the authentication status of the data.
+
+  @retval EFI_SUCCESS               The call completed successfully.
+  @retval EFI_WARN_BUFFER_TOO_SMALL The buffer is too small to contain the requested output.
+                                    The buffer filled, and the output is truncated.
+  @retval EFI_NOT_FOUND             NameGuid was not found in the firmware volume.
+  @retval EFI_DEVICE_ERROR          A hardware error occurred when attempting to
+                                    access the firmware volume.
+  @retval EFI_ACCESS_DENIED         The firmware volume is configured to disallow reads.
+  @retval EFI_OUT_OF_RESOURCES      An allocation failure occurred.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_READ_FILE)(
+  IN EFI_FIRMWARE_VOLUME_PROTOCOL   *This,
+  IN EFI_GUID                       *NameGuid,
+  IN OUT VOID                       **Buffer,
+  IN OUT UINTN                      *BufferSize,
+  OUT EFI_FV_FILETYPE               *FoundType,
+  OUT EFI_FV_FILE_ATTRIBUTES        *FileAttributes,
+  OUT UINT32                        *AuthenticationStatus
+  );
+
+/**
+  Read the requested section from the specified file and returns data in Buffer.
+
+  @param  This                  Indicates the EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  NameGuid              Filename identifying the file from which to read.
+  @param  SectionType           The section type to retrieve.
+  @param  SectionInstance       The instance of SectionType to retrieve.
+  @param  Buffer                Pointer to pointer to buffer in which contents of
+                                a file are returned.
+                                <br>
+                                If Buffer is NULL, only type, attributes, and size
+                                are returned as there is no output buffer.
+                                <br>
+                                If Buffer != NULL and *Buffer == NULL, the output
+                                buffer is allocated from BS pool by ReadFile.
+                                <br>
+                                If Buffer != NULL and *Buffer != NULL, the output
+                                buffer has been allocated by the caller and is being
+                                passed in.
+  @param  BufferSize            The pointer to the buffer size passed in, and on
+                                output the size required to complete the read.
+  @param  AuthenticationStatus  The pointer to the authentication status of the data.
+
+  @retval EFI_SUCCESS                The call completed successfully.
+  @retval EFI_WARN_BUFFER_TOO_SMALL  The buffer is too small to contain the requested output.
+                                     The buffer is filled and the output is truncated.
+  @retval EFI_OUT_OF_RESOURCES       An allocation failure occurred.
+  @retval EFI_NOT_FOUND              The name was not found in the firmware volume.
+  @retval EFI_DEVICE_ERROR           A hardware error occurred when attempting to
+                                     access the firmware volume.
+  @retval EFI_ACCESS_DENIED          The firmware volume is configured to disallow reads.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_READ_SECTION)(
+  IN EFI_FIRMWARE_VOLUME_PROTOCOL   *This,
+  IN EFI_GUID                       *NameGuid,
+  IN EFI_SECTION_TYPE               SectionType,
+  IN UINTN                          SectionInstance,
+  IN OUT VOID                       **Buffer,
+  IN OUT UINTN                      *BufferSize,
+  OUT UINT32                        *AuthenticationStatus
+  );
+
+typedef UINT32  FRAMEWORK_EFI_FV_WRITE_POLICY;
+
+#define FRAMEWORK_EFI_FV_UNRELIABLE_WRITE 0x00000000
+#define FRAMEWORK_EFI_FV_RELIABLE_WRITE   0x00000001
+
+typedef struct {
+  EFI_GUID                *NameGuid;
+  EFI_FV_FILETYPE         Type;
+  EFI_FV_FILE_ATTRIBUTES  FileAttributes;
+  VOID                    *Buffer;
+  UINT32                  BufferSize;
+} FRAMEWORK_EFI_FV_WRITE_FILE_DATA;
+
+/**
+  Write the supplied file (NameGuid) to the FV.
+
+  @param  This                  Indicates the EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  NumberOfFiles         Indicates the number of file records pointed to
+                                by FileData.
+  @param  WritePolicy           Indicates the level of reliability of the write
+                                with respect to things like power failure events.
+  @param  FileData              A pointer to an array of EFI_FV_WRITE_FILE_DATA
+                                structures. Each element in the array indicates
+                                a file to write, and there are NumberOfFiles
+                                elements in the input array.
+
+  @retval EFI_SUCCESS           The write completed successfully.
+  @retval EFI_OUT_OF_RESOURCES  The firmware volume does not have enough free
+                                space to store file(s).
+  @retval EFI_DEVICE_ERROR      A hardware error occurred when attempting to
+                                access the firmware volume.
+  @retval EFI_WRITE_PROTECTED   The firmware volume is configured to disallow writes.
+  @retval EFI_NOT_FOUND         A delete was requested, but the requested file was
+                                not found in the firmware volume.
+  @retval EFI_INVALID_PARAMETER A delete was requested with a multiple file write.
+                                An unsupported WritePolicy was requested.
+                                An unknown file type was specified.
+                                A file system specific error has occurred.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_WRITE_FILE)(
+  IN EFI_FIRMWARE_VOLUME_PROTOCOL             *This,
+  IN UINT32                                   NumberOfFiles,
+  IN FRAMEWORK_EFI_FV_WRITE_POLICY            WritePolicy,
+  IN FRAMEWORK_EFI_FV_WRITE_FILE_DATA         *FileData
+  );
+
+/**
+  Given the input key, search for the next matching file in the volume.
+
+  @param  This                  Indicates the EFI_FIRMWARE_VOLUME_PROTOCOL instance.
+  @param  Key                   Pointer to a caller allocated buffer that contains
+                                an implementation-specific key that is used to track
+                                where to begin searching on successive calls.
+  @param  FileType              The pointer to the file type to filter for.
+  @param  NameGuid              The pointer to Guid filename of the file found.
+  @param  Attributes            The pointer to Attributes of the file found.
+  @param  Size                  The pointer to Size in bytes of the file found.
+
+  @retval EFI_SUCCESS           The output parameters are filled with data obtained from
+                                the first matching file that was found.
+  @retval EFI_NOT_FOUND         No files of type FileType were found.
+  @retval EFI_DEVICE_ERROR      A hardware error occurred when attempting to access
+                                the firmware volume.
+  @retval EFI_ACCESS_DENIED     The firmware volume is configured to disallow reads.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FRAMEWORK_EFI_FV_GET_NEXT_FILE)(
+  IN EFI_FIRMWARE_VOLUME_PROTOCOL   *This,
+  IN OUT VOID                       *Key,
+  IN OUT EFI_FV_FILETYPE            *FileType,
+  OUT EFI_GUID                      *NameGuid,
+  OUT EFI_FV_FILE_ATTRIBUTES        *Attributes,
+  OUT UINTN                         *Size
+  );
+
+//
+// Protocol interface structure
+//
+struct _EFI_FIRMWARE_VOLUME_PROTOCOL {
+  ///
+  /// Retrieves volume capabilities and current settings.
+  ///
+  FRAMEWORK_EFI_FV_GET_ATTRIBUTES GetVolumeAttributes;
+
+  ///
+  /// Modifies the current settings of the firmware volume.
+  ///
+  FRAMEWORK_EFI_FV_SET_ATTRIBUTES SetVolumeAttributes;
+
+  ///
+  /// Reads an entire file from the firmware volume.
+  ///
+  FRAMEWORK_EFI_FV_READ_FILE      ReadFile;
+
+  ///
+  /// Reads a single section from a file into a buffer.
+  ///
+  FRAMEWORK_EFI_FV_READ_SECTION   ReadSection;
+
+  ///
+  /// Writes an entire file into the firmware volume.
+  ///
+  FRAMEWORK_EFI_FV_WRITE_FILE     WriteFile;
+
+  ///
+  /// Provides service to allow searching the firmware volume.
+  ///
+  FRAMEWORK_EFI_FV_GET_NEXT_FILE  GetNextFile;
+
+  ///
+  ///  Data field that indicates the size in bytes of the Key input buffer for
+  ///  the GetNextFile() API.
+  ///
+  UINT32                KeySize;
+
+  ///
+  ///  Handle of the parent firmware volume.
+  ///
+  EFI_HANDLE            ParentHandle;
+};
+
+extern EFI_GUID gEfiFirmwareVolumeProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/IsaAcpi.h b/OvmfPkg/Csm/Include/Protocol/IsaAcpi.h
new file mode 100644
index 0000000000..12aeb1227c
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/IsaAcpi.h
@@ -0,0 +1,298 @@
+/** @file
+  EFI ISA ACPI Protocol is used to enumerate and manage all the ISA controllers on
+  the platform's ISA Bus.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __ISA_ACPI_H_
+#define __ISA_ACPI_H_
+
+///
+/// Global ID for the EFI ISA ACPI Protocol.
+///
+#define EFI_ISA_ACPI_PROTOCOL_GUID \
+  { \
+    0x64a892dc, 0x5561, 0x4536, { 0x92, 0xc7, 0x79, 0x9b, 0xfc, 0x18, 0x33, 0x55 } \
+  }
+
+///
+/// Forward declaration fo the EFI ISA ACPI Protocol
+///
+typedef struct _EFI_ISA_ACPI_PROTOCOL EFI_ISA_ACPI_PROTOCOL;
+
+///
+/// ISA ACPI Protocol interrupt resource attributes.
+///
+#define EFI_ISA_ACPI_IRQ_TYPE_HIGH_TRUE_EDGE_SENSITIVE   0x01   ///< Edge triggered interrupt on a rising edge.
+#define EFI_ISA_ACPI_IRQ_TYPE_LOW_TRUE_EDGE_SENSITIVE    0x02   ///< Edge triggered interrupt on a falling edge.
+#define EFI_ISA_ACPI_IRQ_TYPE_HIGH_TRUE_LEVEL_SENSITIVE  0x04   ///< Level sensitive interrupt active high.
+#define EFI_ISA_ACPI_IRQ_TYPE_LOW_TRUE_LEVEL_SENSITIVE   0x08   ///< Level sensitive interrupt active low.
+
+///
+/// ISA ACPI Protocol DMA resource attributes.
+///
+#define EFI_ISA_ACPI_DMA_SPEED_TYPE_MASK                 0x03   ///< Bit mask of supported DMA speed attributes.
+#define EFI_ISA_ACPI_DMA_SPEED_TYPE_COMPATIBILITY        0x00   ///< ISA controller supports compatibility mode DMA transfers.
+#define EFI_ISA_ACPI_DMA_SPEED_TYPE_A                    0x01   ///< ISA controller supports type A DMA transfers.
+#define EFI_ISA_ACPI_DMA_SPEED_TYPE_B                    0x02   ///< ISA controller supports type B DMA transfers.
+#define EFI_ISA_ACPI_DMA_SPEED_TYPE_F                    0x03   ///< ISA controller supports type F DMA transfers.
+#define EFI_ISA_ACPI_DMA_COUNT_BY_BYTE                   0x04   ///< ISA controller increments DMA address by bytes (8-bit).
+#define EFI_ISA_ACPI_DMA_COUNT_BY_WORD                   0x08   ///< ISA controller increments DMA address by words (16-bit).
+#define EFI_ISA_ACPI_DMA_BUS_MASTER                      0x10   ///< ISA controller is a DMA bus master.
+#define EFI_ISA_ACPI_DMA_TRANSFER_TYPE_8_BIT             0x20   ///< ISA controller only supports 8-bit DMA transfers.
+#define EFI_ISA_ACPI_DMA_TRANSFER_TYPE_8_BIT_AND_16_BIT  0x40   ///< ISA controller both 8-bit and 16-bit DMA transfers.
+#define EFI_ISA_ACPI_DMA_TRANSFER_TYPE_16_BIT            0x80   ///< ISA controller only supports 16-bit DMA transfers.
+
+///
+/// ISA ACPI Protocol MMIO resource attributes
+///
+#define EFI_ISA_ACPI_MEMORY_WIDTH_MASK                   0x03   ///< Bit mask of supported ISA memory width attributes.
+#define EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT                  0x00   ///< ISA MMIO region only supports 8-bit access.
+#define EFI_ISA_ACPI_MEMORY_WIDTH_16_BIT                 0x01   ///< ISA MMIO region only supports 16-bit access.
+#define EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT_AND_16_BIT       0x02   ///< ISA MMIO region supports both 8-bit and 16-bit access.
+#define EFI_ISA_ACPI_MEMORY_WRITEABLE                    0x04   ///< ISA MMIO region supports write transactions.
+#define EFI_ISA_ACPI_MEMORY_CACHEABLE                    0x08   ///< ISA MMIO region supports being cached.
+#define EFI_ISA_ACPI_MEMORY_SHADOWABLE                   0x10   ///< ISA MMIO region may be shadowed.
+#define EFI_ISA_ACPI_MEMORY_EXPANSION_ROM                0x20   ///< ISA MMIO region is an expansion ROM.
+
+///
+/// ISA ACPI Protocol I/O resource attributes
+///
+#define EFI_ISA_ACPI_IO_DECODE_10_BITS                   0x01    ///< ISA controllers uses a 10-bit address decoder for I/O cycles.
+#define EFI_ISA_ACPI_IO_DECODE_16_BITS                   0x02    ///< ISA controllers uses a 16-bit address decoder for I/O cycles.
+
+///
+/// EFI ISA ACPI resource type
+///
+typedef enum {
+  EfiIsaAcpiResourceEndOfList,    ///< Marks the end if a resource list.
+  EfiIsaAcpiResourceIo,           ///< ISA I/O port resource range.
+  EfiIsaAcpiResourceMemory,       ///< ISA MMIO resource range.
+  EfiIsaAcpiResourceDma,          ///< ISA DMA resource.
+  EfiIsaAcpiResourceInterrupt     ///< ISA interrupt resource.
+} EFI_ISA_ACPI_RESOURCE_TYPE;
+
+///
+/// EFI ISA ACPI generic resource structure
+///
+typedef struct {
+  EFI_ISA_ACPI_RESOURCE_TYPE  Type;         ///< The type of resource (I/O, MMIO, DMA, Interrupt).
+  UINT32                      Attribute;    ///< Bit mask of attributes associated with this resource.  See EFI_ISA_ACPI_xxx macros for valid combinations.
+  UINT32                      StartRange;   ///< The start of the resource range.
+  UINT32                      EndRange;     ///< The end of the resource range.
+} EFI_ISA_ACPI_RESOURCE;
+
+///
+/// EFI ISA ACPI resource device identifier
+///
+typedef struct {
+  UINT32  HID;   ///< The ACPI Hardware Identifier value associated with an ISA controller.  Matchs ACPI DSDT contents.
+  UINT32  UID;   ///< The ACPI Unique Identifier value associated with an ISA controller.  Matches ACPI DSDT contents.
+} EFI_ISA_ACPI_DEVICE_ID;
+
+///
+/// EFI ISA ACPI resource list
+///
+typedef struct {
+  EFI_ISA_ACPI_DEVICE_ID  Device;          ///< The ACPI HID/UID associated with an ISA controller.
+  EFI_ISA_ACPI_RESOURCE   *ResourceItem;   ///< A pointer to the list of resources associated with an ISA controller.
+} EFI_ISA_ACPI_RESOURCE_LIST;
+
+/**
+  Enumerates the ISA controllers on an ISA bus.
+
+  This service allows all the ISA controllers on an ISA bus to be enumerated.  If
+  Device is a pointer to a NULL value, then the first ISA controller on the ISA
+  bus is returned in Device and EFI_SUCCESS is returned.  If Device is a pointer
+  to a value that was returned on a prior call to DeviceEnumerate(), then the next
+  ISA controller on the ISA bus is returned in Device and EFI_SUCCESS is returned.
+  If Device is a pointer to the last ISA controller on the ISA bus, then
+  EFI_NOT_FOUND is returned.
+
+  @param[in]  This     The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[out] Device   The pointer to an ISA controller named by ACPI HID/UID.
+
+  @retval EFI_SUCCESS    The next ISA controller on the ISA bus was returned.
+  @retval EFI_NOT_FOUND  No device found.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_DEVICE_ENUMERATE)(
+  IN  EFI_ISA_ACPI_PROTOCOL   *This,
+  OUT EFI_ISA_ACPI_DEVICE_ID  **Device
+  );
+
+/**
+  Sets the power state of an ISA controller.
+
+  This services sets the power state of the ISA controller specified by Device to
+  the power state specified by OnOff.  TRUE denotes on, FALSE denotes off.
+  If the power state is sucessfully set on the ISA Controller, then
+  EFI_SUCCESS is returned.
+
+  @param[in] This     The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in] Device   The pointer to an ISA controller named by ACPI HID/UID.
+  @param[in] OnOff    TRUE denotes on, FALSE denotes off.
+
+  @retval EFI_SUCCESS   Successfully set the power state of the ISA controller.
+  @retval Other         The ISA controller could not be placed in the requested power state.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_SET_DEVICE_POWER)(
+  IN EFI_ISA_ACPI_PROTOCOL   *This,
+  IN EFI_ISA_ACPI_DEVICE_ID  *Device,
+  IN BOOLEAN                 OnOff
+  );
+
+/**
+  Retrieves the current set of resources associated with an ISA controller.
+
+  Retrieves the set of I/O, MMIO, DMA, and interrupt resources currently
+  assigned to the ISA controller specified by Device.  These resources
+  are returned in ResourceList.
+
+  @param[in]  This          The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in]  Device        The pointer to an ISA controller named by ACPI HID/UID.
+  @param[out] ResourceList  The pointer to the current resource list for Device.
+
+  @retval EFI_SUCCESS    Successfully retrieved the current resource list.
+  @retval EFI_NOT_FOUND  The resource list could not be retrieved.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_GET_CUR_RESOURCE)(
+  IN  EFI_ISA_ACPI_PROTOCOL       *This,
+  IN  EFI_ISA_ACPI_DEVICE_ID      *Device,
+  OUT EFI_ISA_ACPI_RESOURCE_LIST  **ResourceList
+  );
+
+/**
+  Retrieves the set of possible resources that may be assigned to an ISA controller
+  with SetResource().
+
+  Retrieves the possible sets of I/O, MMIO, DMA, and interrupt resources for the
+  ISA controller specified by Device.  The sets are returned in ResourceList.
+
+  @param[in]  This           The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in]  Device         The pointer to an ISA controller named by ACPI HID/UID.
+  @param[out] ResourceList   The pointer to the returned list of resource lists.
+
+  @retval EFI_UNSUPPORTED  This service is not supported.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_GET_POS_RESOURCE)(
+  IN EFI_ISA_ACPI_PROTOCOL        *This,
+  IN EFI_ISA_ACPI_DEVICE_ID       *Device,
+  OUT EFI_ISA_ACPI_RESOURCE_LIST  **ResourceList
+  );
+
+/**
+  Assigns resources to an ISA controller.
+
+  Assigns the I/O, MMIO, DMA, and interrupt resources specified by ResourceList
+  to the ISA controller specified by Device.  ResourceList must match a resource list returned by GetPosResource() for the same ISA controller.
+
+  @param[in] This           The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in] Device         The pointer to an ISA controller named by ACPI HID/UID.
+  @param[in] ResourceList   The pointer to a resources list that must be one of the
+                            resource lists returned by GetPosResource() for the
+                            ISA controller specified by Device.
+
+  @retval EFI_SUCCESS  Successfully set resources on the ISA controller.
+  @retval Other        The resources could not be set for the ISA controller.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_SET_RESOURCE)(
+  IN EFI_ISA_ACPI_PROTOCOL       *This,
+  IN EFI_ISA_ACPI_DEVICE_ID      *Device,
+  IN EFI_ISA_ACPI_RESOURCE_LIST  *ResourceList
+  );
+
+/**
+  Enables or disables an ISA controller.
+
+  @param[in] This     The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in] Device   The pointer to the ISA controller to enable/disable.
+  @param[in] Enable   TRUE to enable the ISA controller.  FALSE to disable the
+                      ISA controller.
+
+  @retval EFI_SUCCESS   Successfully enabled/disabled the ISA controller.
+  @retval Other         The ISA controller could not be placed in the requested state.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_ENABLE_DEVICE)(
+  IN EFI_ISA_ACPI_PROTOCOL   *This,
+  IN EFI_ISA_ACPI_DEVICE_ID  *Device,
+  IN BOOLEAN                 Enable
+  );
+
+/**
+  Initializes an ISA controller, so that it can be used.  This service must be called
+  before SetResource(), EnableDevice(), or SetPower() will behave as expected.
+
+  @param[in] This     The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+  @param[in] Device   The pointer to an ISA controller named by ACPI HID/UID.
+
+  @retval EFI_SUCCESS   Successfully initialized an ISA controller.
+  @retval Other         The ISA controller could not be initialized.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_INIT_DEVICE)(
+  IN EFI_ISA_ACPI_PROTOCOL   *This,
+  IN EFI_ISA_ACPI_DEVICE_ID  *Device
+  );
+
+/**
+  Initializes all the HW states required for the ISA controllers on the ISA bus
+  to be enumerated and managed by the rest of the services in this prorotol.
+  This service must be called before any of the other services in this
+  protocol will function as expected.
+
+  @param[in] This  The pointer to the EFI_ISA_ACPI_PROTOCOL instance.
+
+  @retval EFI_SUCCESS   Successfully initialized all required hardware states.
+  @retval Other         The ISA interface could not be initialized.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_ACPI_INTERFACE_INIT)(
+  IN EFI_ISA_ACPI_PROTOCOL  *This
+  );
+
+///
+/// The EFI_ISA_ACPI_PROTOCOL provides the services to enumerate and manage
+/// ISA controllers on an ISA bus.  These services include the ability to initialize,
+/// enable, disable, and manage the power state of ISA controllers.  It also
+/// includes services to query current resources, query possible resources,
+/// and assign resources to an ISA controller.
+///
+struct _EFI_ISA_ACPI_PROTOCOL {
+  EFI_ISA_ACPI_DEVICE_ENUMERATE  DeviceEnumerate;
+  EFI_ISA_ACPI_SET_DEVICE_POWER  SetPower;
+  EFI_ISA_ACPI_GET_CUR_RESOURCE  GetCurResource;
+  EFI_ISA_ACPI_GET_POS_RESOURCE  GetPosResource;
+  EFI_ISA_ACPI_SET_RESOURCE      SetResource;
+  EFI_ISA_ACPI_ENABLE_DEVICE     EnableDevice;
+  EFI_ISA_ACPI_INIT_DEVICE       InitDevice;
+  EFI_ISA_ACPI_INTERFACE_INIT    InterfaceInit;
+};
+
+extern EFI_GUID gEfiIsaAcpiProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/IsaIo.h b/OvmfPkg/Csm/Include/Protocol/IsaIo.h
new file mode 100644
index 0000000000..30000305fb
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/IsaIo.h
@@ -0,0 +1,356 @@
+/** @file
+  ISA I/O Protocol is used by ISA device drivers to perform I/O, MMIO and DMA
+  operations on the ISA controllers they manage.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_ISA_IO_H_
+#define _EFI_ISA_IO_H_
+
+#include <Protocol/IsaAcpi.h>
+
+///
+/// Global ID for the EFI_ISA_IO_PROTOCOL
+///
+#define EFI_ISA_IO_PROTOCOL_GUID \
+  { \
+    0x7ee2bd44, 0x3da0, 0x11d4, { 0x9a, 0x38, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
+  }
+
+///
+/// Forward declaration for the EFI_ISA_IO_PROTOCOL.
+///
+typedef struct _EFI_ISA_IO_PROTOCOL EFI_ISA_IO_PROTOCOL;
+
+///
+/// Width of EFI_ISA_IO_PROTOCOL I/O Port and MMIO operations.
+///
+typedef enum {
+  EfiIsaIoWidthUint8 = 0,      ///< 8-bit operation.
+  EfiIsaIoWidthUint16,         ///< 16-bit operation.
+  EfiIsaIoWidthUint32,         ///< 32-bit operation
+  EfiIsaIoWidthReserved,
+  EfiIsaIoWidthFifoUint8,      ///< 8-bit FIFO operation.
+  EfiIsaIoWidthFifoUint16,     ///< 16-bit FIFO operation.
+  EfiIsaIoWidthFifoUint32,     ///< 32-bit FIFO operation.
+  EfiIsaIoWidthFifoReserved,
+  EfiIsaIoWidthFillUint8,      ///< 8-bit Fill operation.
+  EfiIsaIoWidthFillUint16,     ///< 16-bit Fill operation.
+  EfiIsaIoWidthFillUint32,     ///< 32-bit Fill operation.
+  EfiIsaIoWidthFillReserved,
+  EfiIsaIoWidthMaximum
+} EFI_ISA_IO_PROTOCOL_WIDTH;
+
+///
+/// Attributes for the EFI_ISA_IO_PROTOCOL common DMA buffer allocations.
+///
+#define EFI_ISA_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE  0x080    ///< Map a memory range so write are combined.
+#define EFI_ISA_IO_ATTRIBUTE_MEMORY_CACHED         0x800    ///< Map a memory range so all read and write accesses are cached.
+#define EFI_ISA_IO_ATTRIBUTE_MEMORY_DISABLE        0x1000   ///< Disable a memory range.
+
+///
+/// Channel attribute for EFI_ISA_IO_PROTOCOL slave DMA requests
+///
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_SPEED_COMPATIBLE  0x001   ///< Set the speed of the DMA transfer in compatible mode.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_SPEED_A           0x002   ///< Not supported.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_SPEED_B           0x004   ///< Not supported.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_SPEED_C           0x008   ///< Not supported.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_WIDTH_8           0x010   ///< Request 8-bit DMA transfers.  Only available on channels 0..3.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_WIDTH_16          0x020   ///< Request 16-bit DMA transfers.  Only available on channels 4..7.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_SINGLE_MODE       0x040   ///< Request a single DMA transfer.
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_DEMAND_MODE       0x080   ///< Request multiple DMA transfers until TC (Terminal Count) or EOP (End of Process).
+#define EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_AUTO_INITIALIZE   0x100   ///< Automatically reload base and count at the end of the DMA transfer.
+
+///
+/// The DMA opreration type for EFI_ISA_IO_PROTOCOL DMA requests.
+///
+typedef enum {
+  ///
+  /// A read operation from system memory by a bus master.
+  ///
+  EfiIsaIoOperationBusMasterRead,
+  ///
+  /// A write operation to system memory by a bus master.
+  ///
+  EfiIsaIoOperationBusMasterWrite,
+  ///
+  /// Provides both read and write access to system memory by both the processor
+  /// and a bus master. The buffer is coherent from both the processor's and the
+  /// bus master's point of view.
+  ///
+  EfiIsaIoOperationBusMasterCommonBuffer,
+  ///
+  /// A read operation from system memory by a slave device.
+  ///
+  EfiIsaIoOperationSlaveRead,
+  ///
+  /// A write operation to system memory by a slave master.
+  ///
+  EfiIsaIoOperationSlaveWrite,
+  EfiIsaIoOperationMaximum
+} EFI_ISA_IO_PROTOCOL_OPERATION;
+
+/**
+  Performs ISA I/O and MMIO Read/Write Cycles
+
+  @param[in]      This     A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in]      Width    Specifies the width of the I/O or MMIO operation.
+  @param[in]      Offset   The offset into the ISA I/O or MMIO space to start the
+                           operation.
+  @param[in]      Count    The number of I/O or MMIO operations to perform.
+  @param[in, out] Buffer   For read operations, the destination buffer to store
+                           the results. For write operations, the source buffer to
+                           write data from.
+
+  @retval EFI_SUCCESS             The data was successfully read from or written to the device.
+  @retval EFI_UNSUPPORTED         The Offset is not valid for this device.
+  @retval EFI_INVALID_PARAMETER   Width or Count, or both, were invalid.
+  @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to a lack of resources.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_IO_MEM)(
+  IN     EFI_ISA_IO_PROTOCOL        *This,
+  IN     EFI_ISA_IO_PROTOCOL_WIDTH  Width,
+  IN     UINT32                     Offset,
+  IN     UINTN                      Count,
+  IN OUT VOID                       *Buffer
+  );
+
+///
+/// Structure of functions for accessing ISA I/O and MMIO space.
+///
+typedef struct {
+  ///
+  /// Read from ISA I/O or MMIO space.
+  ///
+  EFI_ISA_IO_PROTOCOL_IO_MEM  Read;
+  ///
+  /// Write to ISA I/O or MMIO space.
+  ///
+  EFI_ISA_IO_PROTOCOL_IO_MEM  Write;
+} EFI_ISA_IO_PROTOCOL_ACCESS;
+
+/**
+  Copies data from one region of ISA MMIO space to another region of ISA
+  MMIO space.
+
+  @param[in] This         A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in] Width        Specifies the width of the MMIO copy operation.
+  @param[in] DestOffset   The offset of the destination in ISA MMIO space.
+  @param[in] SrcOffset    The offset of the source in ISA MMIO space.
+  @param[in] Count        The number tranfers to perform for this copy operation.
+
+  @retval EFI_SUCCESS             The data was copied sucessfully.
+  @retval EFI_UNSUPPORTED         The DestOffset or SrcOffset is not valid for this device.
+  @retval EFI_INVALID_PARAMETER   Width or Count, or both, were invalid.
+  @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to a lack of resources.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_COPY_MEM)(
+  IN EFI_ISA_IO_PROTOCOL         *This,
+  IN EFI_ISA_IO_PROTOCOL_WIDTH   Width,
+  IN UINT32                      DestOffset,
+  IN UINT32                      SrcOffset,
+  IN UINTN                       Count
+  );
+
+/**
+  Maps a memory region for DMA.
+
+  This function returns the device-specific addresses required to access system memory.
+  This function is used to map system memory for ISA DMA operations.  All ISA DMA
+  operations must be performed through their mapped addresses, and such mappings must
+  be freed with EFI_ISA_IO_PROTOCOL.Unmap() after the DMA operation is completed.
+
+  If the DMA operation is a single read or write data transfer through an ISA bus
+  master, then EfiIsaIoOperationBusMasterRead or EfiIsaIoOperationBusMasterWrite
+  is used and the range is unmapped to complete the operation. If the DMA operation
+  is a single read or write data transfer through an ISA slave controller, then
+  EfiIsaIoOperationSlaveRead or EfiIsaIoOperationSlaveWrite is used and the range
+  is unmapped to complete the operation.
+
+  If performing a DMA read operation, all the data must be present in system memory before the Map() is performed.  Similarly,
+  if performing a DMA write operation, the data must not be accessed in system
+  memory until EFI_ISA_IO_PROTOCOL.Unmap() is performed.  Bus master operations that
+  require both read and write access or require multiple host device interactions
+  within the same mapped region must use EfiIsaIoOperationBusMasterCommonBuffer.
+  However, only memory allocated via the EFI_ISA_IO_PROTOCOL.AllocateBuffer() interface
+  is guaranteed to be able to be mapped for this operation type.  In all mapping
+  requests the NumberOfBytes returned may be less than originally requested.  It is
+  the caller's responsibility to make additional requests to complete the entire
+  transfer.
+
+  @param[in]      This                A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in]      Operation           Indicates the type of DMA (slave or bus master),
+                                      and if the DMA operation is going to read or
+                                      write to system memory.
+  @param[in]      ChannelNumber       The slave channel number to use for this DMA
+                                      operation.  If Operation and ChannelAttributes
+                                      shows that this device performs bus mastering
+                                      DMA, then this field is ignored.  The legal
+                                      range for this field is 0..7.
+  @param[in]      ChannelAttributes   A bitmask of the attributes used to configure
+                                      the slave DMA channel for this DMA operation.
+                                      See EFI_ISA_IO_SLAVE_DMA_ATTRIBUTE_* for the
+                                      legal bit combinations.
+  @param[in]      HostAddress         The system memory address to map to the device.
+  @param[in, out] NumberOfBytes       On input the number of bytes to map.  On
+                                      output the number of bytes that were mapped.
+  @param[out]     DeviceAddress       The resulting map address for the bus master
+                                      device to use to access the hosts HostAddress.
+  @param[out]     Mapping             A returned value that must be passed to into
+                                      EFI_ISA_IO_PROTOCOL.Unmap() to free all the the
+                                      resources associated with this map request.
+
+  @retval EFI_SUCCESS             The range was mapped for the returned NumberOfBytes.
+  @retval EFI_INVALID_PARAMETER   The Operation is undefined.
+  @retval EFI_INVALID_PARAMETER   The HostAddress is undefined.
+  @retval EFI_UNSUPPORTED         The HostAddress can not be mapped as a common buffer.
+  @retval EFI_DEVICE_ERROR        The system hardware could not map the requested address.
+  @retval EFI_OUT_OF_RESOURCES    The memory pages could not be allocated.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_MAP)(
+  IN     EFI_ISA_IO_PROTOCOL            *This,
+  IN     EFI_ISA_IO_PROTOCOL_OPERATION  Operation,
+  IN     UINT8                          ChannelNumber      OPTIONAL,
+  IN     UINT32                         ChannelAttributes,
+  IN     VOID                           *HostAddress,
+  IN OUT UINTN                          *NumberOfBytes,
+  OUT    EFI_PHYSICAL_ADDRESS           *DeviceAddress,
+  OUT    VOID                           **Mapping
+  );
+
+/**
+  Unmaps a memory region that was previously mapped with EFI_ISA_IO_PROTOCOL.Map().
+
+  The EFI_ISA_IO_PROTOCOL.Map() operation is completed and any corresponding
+  resources are released.  If the operation was EfiIsaIoOperationSlaveWrite
+  or EfiIsaIoOperationBusMasterWrite, the data is committed to system memory.
+  Any resources used for the mapping are freed.
+
+  @param[in] This           A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in] Mapping        The mapping value returned from EFI_ISA_IO_PROTOCOL.Map().
+
+  @retval EFI_SUCCESS       The memory region was unmapped.
+  @retval EFI_DEVICE_ERROR  The data was not committed to the target system memory.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_UNMAP)(
+  IN  EFI_ISA_IO_PROTOCOL  *This,
+  IN  VOID                 *Mapping
+  );
+
+/**
+  Allocates pages that are suitable for an EfiIsaIoOperationBusMasterCommonBuffer
+  mapping.
+
+  @param[in]  This          A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in]  Type          The type allocation to perform.
+  @param[in]  MemoryType    The type of memory to allocate.
+  @param[in]  Pages         The number of pages to allocate.
+  @param[out] HostAddress   A pointer to store the base address of the allocated range.
+  @param[in]  Attributes    The requested bit mask of attributes for the allocated range.
+
+  @retval EFI_SUCCESS             The requested memory pages were allocated.
+  @retval EFI_INVALID_PARAMETER   Type is invalid.
+  @retval EFI_INVALID_PARAMETER   MemoryType is invalid.
+  @retval EFI_INVALID_PARAMETER   HostAddress is NULL.
+  @retval EFI_UNSUPPORTED         Attributes is unsupported.
+  @retval EFI_UNSUPPORTED         The memory range specified by HostAddress, Pages,
+                                  and Type is not available for common buffer use.
+  @retval EFI_OUT_OF_RESOURCES    The memory pages could not be allocated.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_ALLOCATE_BUFFER)(
+  IN  EFI_ISA_IO_PROTOCOL  *This,
+  IN  EFI_ALLOCATE_TYPE    Type,
+  IN  EFI_MEMORY_TYPE      MemoryType,
+  IN  UINTN                Pages,
+  OUT VOID                 **HostAddress,
+  IN  UINT64               Attributes
+  );
+
+/**
+  Frees a common buffer that was allocated with EFI_ISA_IO_PROTOCOL.AllocateBuffer().
+
+  @param[in] This          A pointer to the EFI_ISA_IO_PROTOCOL instance.
+  @param[in] Pages         The number of pages to free from the previously allocated common buffer.
+  @param[in] HostAddress   The base address of the previously allocated common buffer.
+
+
+  @retval EFI_SUCCESS             The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER   The memory was not allocated with EFI_ISA_IO.AllocateBufer().
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_FREE_BUFFER)(
+  IN  EFI_ISA_IO_PROTOCOL  *This,
+  IN  UINTN                Pages,
+  IN  VOID                 *HostAddress
+  );
+
+/**
+  Flushes a DMA buffer, which forces all DMA posted write transactions to complete.
+
+  @param[in] This   A pointer to the EFI_ISA_IO_PROTOCOL instance.
+
+  @retval  EFI_SUCCESS        The DMA buffers were flushed.
+  @retval  EFI_DEVICE_ERROR   The buffers were not flushed due to a hardware error.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_ISA_IO_PROTOCOL_FLUSH)(
+  IN EFI_ISA_IO_PROTOCOL  *This
+  );
+
+///
+/// The EFI_ISA_IO_PROTOCOL provides the basic Memory, I/O, and DMA interfaces
+/// used to abstract accesses to ISA controllers.  There is one EFI_ISA_IO_PROTOCOL
+/// instance for each ISA controller on a ISA bus. A device driver that wishes
+/// to manage an ISA controller in a system will have to retrieve the
+/// ISA_PCI_IO_PROTOCOL instance associated with the ISA controller.
+///
+struct _EFI_ISA_IO_PROTOCOL {
+  EFI_ISA_IO_PROTOCOL_ACCESS           Mem;
+  EFI_ISA_IO_PROTOCOL_ACCESS           Io;
+  EFI_ISA_IO_PROTOCOL_COPY_MEM         CopyMem;
+  EFI_ISA_IO_PROTOCOL_MAP              Map;
+  EFI_ISA_IO_PROTOCOL_UNMAP            Unmap;
+  EFI_ISA_IO_PROTOCOL_ALLOCATE_BUFFER  AllocateBuffer;
+  EFI_ISA_IO_PROTOCOL_FREE_BUFFER      FreeBuffer;
+  EFI_ISA_IO_PROTOCOL_FLUSH            Flush;
+  ///
+  /// The list of I/O , MMIO, DMA, and Interrupt resources associated with the
+  /// ISA controller abstracted by this instance of the EFI_ISA_IO_PROTOCOL.
+  ///
+  EFI_ISA_ACPI_RESOURCE_LIST           *ResourceList;
+  ///
+  /// The size, in bytes, of the ROM image.
+  ///
+  UINT32                               RomSize;
+  ///
+  /// A pointer to the in memory copy of the ROM image. The ISA Bus Driver is responsible
+  /// for allocating memory for the ROM image, and copying the contents of the ROM to memory
+  /// during ISA Bus initialization.
+  ///
+  VOID                                 *RomImage;
+};
+
+extern EFI_GUID gEfiIsaIoProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/LegacyBios.h b/OvmfPkg/Csm/Include/Protocol/LegacyBios.h
new file mode 100644
index 0000000000..36761da397
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/LegacyBios.h
@@ -0,0 +1,1553 @@
+/** @file
+  The EFI Legacy BIOS Protocol is used to abstract legacy Option ROM usage
+  under EFI and Legacy OS boot.  This file also includes all the related
+  COMPATIBILIY16 structures and defintions.
+
+  Note: The names for EFI_IA32_REGISTER_SET elements were picked to follow
+  well known naming conventions.
+
+  Thunk is the code that switches from 32-bit protected environment into the 16-bit real-mode
+  environment. Reverse thunk is the code that does the opposite.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  This protocol is defined in Framework for EFI Compatibility Support Module spec
+  Version 0.98.
+
+**/
+
+#ifndef _EFI_LEGACY_BIOS_H_
+#define _EFI_LEGACY_BIOS_H_
+
+///
+///
+///
+#pragma pack(1)
+
+typedef UINT8                       SERIAL_MODE;
+typedef UINT8                       PARALLEL_MODE;
+
+#define EFI_COMPATIBILITY16_TABLE_SIGNATURE SIGNATURE_32 ('I', 'F', 'E', '$')
+
+///
+/// There is a table located within the traditional BIOS in either the 0xF000:xxxx or 0xE000:xxxx
+/// physical address range. It is located on a 16-byte boundary and provides the physical address of the
+/// entry point for the Compatibility16 functions. These functions provide the platform-specific
+/// information that is required by the generic EfiCompatibility code. The functions are invoked via
+/// thunking by using EFI_LEGACY_BIOS_PROTOCOL.FarCall86() with the 32-bit physical
+/// entry point.
+///
+typedef struct {
+  ///
+  /// The string "$EFI" denotes the start of the EfiCompatibility table. Byte 0 is "I," byte
+  /// 1 is "F," byte 2 is "E," and byte 3 is "$" and is normally accessed as a DWORD or UINT32.
+  ///
+  UINT32                            Signature;
+
+  ///
+  /// The value required such that byte checksum of TableLength equals zero.
+  ///
+  UINT8                             TableChecksum;
+
+  ///
+  /// The length of this table.
+  ///
+  UINT8                             TableLength;
+
+  ///
+  /// The major EFI revision for which this table was generated.
+  ///
+  UINT8                             EfiMajorRevision;
+
+  ///
+  /// The minor EFI revision for which this table was generated.
+  ///
+  UINT8                             EfiMinorRevision;
+
+  ///
+  /// The major revision of this table.
+  ///
+  UINT8                             TableMajorRevision;
+
+  ///
+  /// The minor revision of this table.
+  ///
+  UINT8                             TableMinorRevision;
+
+  ///
+  /// Reserved for future usage.
+  ///
+  UINT16                            Reserved;
+
+  ///
+  /// The segment of the entry point within the traditional BIOS for Compatibility16 functions.
+  ///
+  UINT16                            Compatibility16CallSegment;
+
+  ///
+  /// The offset of the entry point within the traditional BIOS for Compatibility16 functions.
+  ///
+  UINT16                            Compatibility16CallOffset;
+
+  ///
+  /// The segment of the entry point within the traditional BIOS for EfiCompatibility
+  /// to invoke the PnP installation check.
+  ///
+  UINT16                            PnPInstallationCheckSegment;
+
+  ///
+  /// The Offset of the entry point within the traditional BIOS for EfiCompatibility
+  /// to invoke the PnP installation check.
+  ///
+  UINT16                            PnPInstallationCheckOffset;
+
+  ///
+  /// EFI system resources table. Type EFI_SYSTEM_TABLE is defined in the IntelPlatform
+  ///Innovation Framework for EFI Driver Execution Environment Core Interface Specification (DXE CIS).
+  ///
+  UINT32                            EfiSystemTable;
+
+  ///
+  /// The address of an OEM-provided identifier string. The string is null terminated.
+  ///
+  UINT32                            OemIdStringPointer;
+
+  ///
+  /// The 32-bit physical address where ACPI RSD PTR is stored within the traditional
+  /// BIOS. The remained of the ACPI tables are located at their EFI addresses. The size
+  /// reserved is the maximum for ACPI 2.0. The EfiCompatibility will fill in the ACPI
+  /// RSD PTR with either the ACPI 1.0b or 2.0 values.
+  ///
+  UINT32                            AcpiRsdPtrPointer;
+
+  ///
+  /// The OEM revision number. Usage is undefined but provided for OEM module usage.
+  ///
+  UINT16                            OemRevision;
+
+  ///
+  /// The 32-bit physical address where INT15 E820 data is stored within the traditional
+  /// BIOS. The EfiCompatibility code will fill in the E820Pointer value and copy the
+  /// data to the indicated area.
+  ///
+  UINT32                            E820Pointer;
+
+  ///
+  /// The length of the E820 data and is filled in by the EfiCompatibility code.
+  ///
+  UINT32                            E820Length;
+
+  ///
+  /// The 32-bit physical address where the $PIR table is stored in the traditional BIOS.
+  /// The EfiCompatibility code will fill in the IrqRoutingTablePointer value and
+  /// copy the data to the indicated area.
+  ///
+  UINT32                            IrqRoutingTablePointer;
+
+  ///
+  /// The length of the $PIR table and is filled in by the EfiCompatibility code.
+  ///
+  UINT32                            IrqRoutingTableLength;
+
+  ///
+  /// The 32-bit physical address where the MP table is stored in the traditional BIOS.
+  /// The EfiCompatibility code will fill in the MpTablePtr value and copy the data
+  /// to the indicated area.
+  ///
+  UINT32                            MpTablePtr;
+
+  ///
+  /// The length of the MP table and is filled in by the EfiCompatibility code.
+  ///
+  UINT32                            MpTableLength;
+
+  ///
+  /// The segment of the OEM-specific INT table/code.
+  ///
+  UINT16                            OemIntSegment;
+
+  ///
+  /// The offset of the OEM-specific INT table/code.
+  ///
+  UINT16                            OemIntOffset;
+
+  ///
+  /// The segment of the OEM-specific 32-bit table/code.
+  ///
+  UINT16                            Oem32Segment;
+
+  ///
+  /// The offset of the OEM-specific 32-bit table/code.
+  ///
+  UINT16                            Oem32Offset;
+
+  ///
+  /// The segment of the OEM-specific 16-bit table/code.
+  ///
+  UINT16                            Oem16Segment;
+
+  ///
+  /// The offset of the OEM-specific 16-bit table/code.
+  ///
+  UINT16                            Oem16Offset;
+
+  ///
+  /// The segment of the TPM binary passed to 16-bit CSM.
+  ///
+  UINT16                            TpmSegment;
+
+  ///
+  /// The offset of the TPM binary passed to 16-bit CSM.
+  ///
+  UINT16                            TpmOffset;
+
+  ///
+  /// A pointer to a string identifying the independent BIOS vendor.
+  ///
+  UINT32                            IbvPointer;
+
+  ///
+  /// This field is NULL for all systems not supporting PCI Express. This field is the base
+  /// value of the start of the PCI Express memory-mapped configuration registers and
+  /// must be filled in prior to EfiCompatibility code issuing the Compatibility16 function
+  /// Compatibility16InitializeYourself().
+  /// Compatibility16InitializeYourself() is defined in Compatability16
+  /// Functions.
+  ///
+  UINT32                            PciExpressBase;
+
+  ///
+  /// Maximum PCI bus number assigned.
+  ///
+  UINT8                             LastPciBus;
+
+  ///
+  /// Start Address of Upper Memory Area (UMA) to be set as Read/Write. If
+  /// UmaAddress is a valid address in the shadow RAM, it also indicates that the region
+  /// from 0xC0000 to (UmaAddress - 1) can be used for Option ROM.
+  ///
+  UINT32                            UmaAddress;
+
+  ///
+  /// Upper Memory Area size in bytes to be set as Read/Write. If zero, no UMA region
+  /// will be set as Read/Write (i.e. all Shadow RAM is set as Read-Only).
+  ///
+  UINT32                            UmaSize;
+
+  ///
+  /// Start Address of high memory that can be used for permanent allocation. If zero,
+  /// high memory is not available for permanent allocation.
+  ///
+  UINT32                            HiPermanentMemoryAddress;
+
+  ///
+  /// Size of high memory that can be used for permanent allocation in bytes. If zero,
+  /// high memory is not available for permanent allocation.
+  ///
+  UINT32                            HiPermanentMemorySize;
+} EFI_COMPATIBILITY16_TABLE;
+
+///
+/// Functions provided by the CSM binary which communicate between the EfiCompatibility
+/// and Compatability16 code.
+///
+/// Inconsistent with the specification here:
+/// The member's name started with "Compatibility16" [defined in Intel Framework
+/// Compatibility Support Module Specification / 0.97 version]
+/// has been changed to "Legacy16" since keeping backward compatible.
+///
+typedef enum {
+  ///
+  /// Causes the Compatibility16 code to do any internal initialization required.
+  /// Input:
+  ///   AX = Compatibility16InitializeYourself
+  ///   ES:BX = Pointer to EFI_TO_COMPATIBILITY16_INIT_TABLE
+  /// Return:
+  ///   AX = Return Status codes
+  ///
+  Legacy16InitializeYourself    = 0x0000,
+
+  ///
+  /// Causes the Compatibility16 BIOS to perform any drive number translations to match the boot sequence.
+  /// Input:
+  ///   AX = Compatibility16UpdateBbs
+  ///   ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE
+  /// Return:
+  ///   AX = Returned status codes
+  ///
+  Legacy16UpdateBbs             = 0x0001,
+
+  ///
+  /// Allows the Compatibility16 code to perform any final actions before booting. The Compatibility16
+  /// code is read/write.
+  /// Input:
+  ///   AX = Compatibility16PrepareToBoot
+  ///   ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE structure
+  /// Return:
+  ///   AX = Returned status codes
+  ///
+  Legacy16PrepareToBoot         = 0x0002,
+
+  ///
+  /// Causes the Compatibility16 BIOS to boot. The Compatibility16 code is Read/Only.
+  /// Input:
+  ///   AX = Compatibility16Boot
+  /// Output:
+  ///   AX = Returned status codes
+  ///
+  Legacy16Boot                  = 0x0003,
+
+  ///
+  /// Allows the Compatibility16 code to get the last device from which a boot was attempted. This is
+  /// stored in CMOS and is the priority number of the last attempted boot device.
+  /// Input:
+  ///   AX = Compatibility16RetrieveLastBootDevice
+  /// Output:
+  ///   AX = Returned status codes
+  ///   BX = Priority number of the boot device.
+  ///
+  Legacy16RetrieveLastBootDevice = 0x0004,
+
+  ///
+  /// Allows the Compatibility16 code rehook INT13, INT18, and/or INT19 after dispatching a legacy OpROM.
+  /// Input:
+  ///   AX = Compatibility16DispatchOprom
+  ///   ES:BX = Pointer to EFI_DISPATCH_OPROM_TABLE
+  /// Output:
+  ///   AX = Returned status codes
+  ///   BX = Number of non-BBS-compliant devices found. Equals 0 if BBS compliant.
+  ///
+  Legacy16DispatchOprom         = 0x0005,
+
+  ///
+  /// Finds a free area in the 0xFxxxx or 0xExxxx region of the specified length and returns the address
+  /// of that region.
+  /// Input:
+  ///   AX = Compatibility16GetTableAddress
+  ///   BX = Allocation region
+  ///       00 = Allocate from either 0xE0000 or 0xF0000 64 KB blocks.
+  ///       Bit 0 = 1 Allocate from 0xF0000 64 KB block
+  ///       Bit 1 = 1 Allocate from 0xE0000 64 KB block
+  ///   CX = Requested length in bytes.
+  ///   DX = Required address alignment. Bit mapped. First non-zero bit from the right is the alignment.
+  /// Output:
+  ///   AX = Returned status codes
+  ///   DS:BX = Address of the region
+  ///
+  Legacy16GetTableAddress       = 0x0006,
+
+  ///
+  /// Enables the EfiCompatibility module to do any nonstandard processing of keyboard LEDs or state.
+  /// Input:
+  ///   AX = Compatibility16SetKeyboardLeds
+  ///   CL = LED status.
+  ///     Bit 0  Scroll Lock 0 = Off
+  ///     Bit 1  NumLock
+  ///     Bit 2  Caps Lock
+  /// Output:
+  ///     AX = Returned status codes
+  ///
+  Legacy16SetKeyboardLeds       = 0x0007,
+
+  ///
+  /// Enables the EfiCompatibility module to install an interrupt handler for PCI mass media devices that
+  /// do not have an OpROM associated with them. An example is SATA.
+  /// Input:
+  ///   AX = Compatibility16InstallPciHandler
+  ///   ES:BX = Pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure
+  /// Output:
+  ///   AX = Returned status codes
+  ///
+  Legacy16InstallPciHandler     = 0x0008
+} EFI_COMPATIBILITY_FUNCTIONS;
+
+
+///
+/// EFI_DISPATCH_OPROM_TABLE
+///
+typedef struct {
+  UINT16  PnPInstallationCheckSegment;  ///< A pointer to the PnpInstallationCheck data structure.
+  UINT16  PnPInstallationCheckOffset;   ///< A pointer to the PnpInstallationCheck data structure.
+  UINT16  OpromSegment;                 ///< The segment where the OpROM was placed. Offset is assumed to be 3.
+  UINT8   PciBus;                       ///< The PCI bus.
+  UINT8   PciDeviceFunction;            ///< The PCI device * 0x08 | PCI function.
+  UINT8   NumberBbsEntries;             ///< The number of valid BBS table entries upon entry and exit. The IBV code may
+                                        ///< increase this number, if BBS-compliant devices also hook INTs in order to force the
+                                        ///< OpROM BIOS Setup to be executed.
+  UINT32  BbsTablePointer;              ///< A pointer to the BBS table.
+  UINT16  RuntimeSegment;               ///< The segment where the OpROM can be relocated to. If this value is 0x0000, this
+                                        ///< means that the relocation of this run time code is not supported.
+                                        ///< Inconsistent with specification here:
+                                        ///< The member's name "OpromDestinationSegment" [defined in Intel Framework Compatibility Support Module Specification / 0.97 version]
+                                        ///< has been changed to "RuntimeSegment" since keeping backward compatible.
+
+} EFI_DISPATCH_OPROM_TABLE;
+
+///
+/// EFI_TO_COMPATIBILITY16_INIT_TABLE
+///
+typedef struct {
+  ///
+  /// Starting address of memory under 1 MB. The ending address is assumed to be 640 KB or 0x9FFFF.
+  ///
+  UINT32                            BiosLessThan1MB;
+
+  ///
+  /// The starting address of the high memory block.
+  ///
+  UINT32                            HiPmmMemory;
+
+  ///
+  /// The length of high memory block.
+  ///
+  UINT32                            HiPmmMemorySizeInBytes;
+
+  ///
+  /// The segment of the reverse thunk call code.
+  ///
+  UINT16                            ReverseThunkCallSegment;
+
+  ///
+  /// The offset of the reverse thunk call code.
+  ///
+  UINT16                            ReverseThunkCallOffset;
+
+  ///
+  /// The number of E820 entries copied to the Compatibility16 BIOS.
+  ///
+  UINT32                            NumberE820Entries;
+
+  ///
+  /// The amount of usable memory above 1 MB, e.g., E820 type 1 memory.
+  ///
+  UINT32                            OsMemoryAbove1Mb;
+
+  ///
+  /// The start of thunk code in main memory. Memory cannot be used by BIOS or PMM.
+  ///
+  UINT32                            ThunkStart;
+
+  ///
+  /// The size of the thunk code.
+  ///
+  UINT32                            ThunkSizeInBytes;
+
+  ///
+  /// Starting address of memory under 1 MB.
+  ///
+  UINT32                            LowPmmMemory;
+
+  ///
+  /// The length of low Memory block.
+  ///
+  UINT32                            LowPmmMemorySizeInBytes;
+} EFI_TO_COMPATIBILITY16_INIT_TABLE;
+
+///
+/// DEVICE_PRODUCER_SERIAL.
+///
+typedef struct {
+  UINT16                            Address;    ///< I/O address assigned to the serial port.
+  UINT8                             Irq;        ///< IRQ assigned to the serial port.
+  SERIAL_MODE                       Mode;       ///< Mode of serial port. Values are defined below.
+} DEVICE_PRODUCER_SERIAL;
+
+///
+/// DEVICE_PRODUCER_SERIAL's modes.
+///@{
+#define DEVICE_SERIAL_MODE_NORMAL               0x00
+#define DEVICE_SERIAL_MODE_IRDA                 0x01
+#define DEVICE_SERIAL_MODE_ASK_IR               0x02
+#define DEVICE_SERIAL_MODE_DUPLEX_HALF          0x00
+#define DEVICE_SERIAL_MODE_DUPLEX_FULL          0x10
+///@)
+
+///
+/// DEVICE_PRODUCER_PARALLEL.
+///
+typedef struct {
+  UINT16                            Address;  ///< I/O address assigned to the parallel port.
+  UINT8                             Irq;      ///< IRQ assigned to the parallel port.
+  UINT8                             Dma;      ///< DMA assigned to the parallel port.
+  PARALLEL_MODE                     Mode;     ///< Mode of the parallel port. Values are defined below.
+} DEVICE_PRODUCER_PARALLEL;
+
+///
+/// DEVICE_PRODUCER_PARALLEL's modes.
+///@{
+#define DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY   0x00
+#define DEVICE_PARALLEL_MODE_MODE_BIDIRECTIONAL 0x01
+#define DEVICE_PARALLEL_MODE_MODE_EPP           0x02
+#define DEVICE_PARALLEL_MODE_MODE_ECP           0x03
+///@}
+
+///
+/// DEVICE_PRODUCER_FLOPPY
+///
+typedef struct {
+  UINT16                            Address;          ///< I/O address assigned to the floppy.
+  UINT8                             Irq;              ///< IRQ assigned to the floppy.
+  UINT8                             Dma;              ///< DMA assigned to the floppy.
+  UINT8                             NumberOfFloppy;   ///< Number of floppies in the system.
+} DEVICE_PRODUCER_FLOPPY;
+
+///
+/// LEGACY_DEVICE_FLAGS
+///
+typedef struct {
+  UINT32                            A20Kybd : 1;      ///< A20 controller by keyboard controller.
+  UINT32                            A20Port90 : 1;    ///< A20 controlled by port 0x92.
+  UINT32                            Reserved : 30;    ///< Reserved for future usage.
+} LEGACY_DEVICE_FLAGS;
+
+///
+/// DEVICE_PRODUCER_DATA_HEADER
+///
+typedef struct {
+  DEVICE_PRODUCER_SERIAL            Serial[4];      ///< Data for serial port x. Type DEVICE_PRODUCER_SERIAL is defined below.
+  DEVICE_PRODUCER_PARALLEL          Parallel[3];    ///< Data for parallel port x. Type DEVICE_PRODUCER_PARALLEL is defined below.
+  DEVICE_PRODUCER_FLOPPY            Floppy;         ///< Data for floppy. Type DEVICE_PRODUCER_FLOPPY is defined below.
+  UINT8                             MousePresent;   ///< Flag to indicate if mouse is present.
+  LEGACY_DEVICE_FLAGS               Flags;          ///< Miscellaneous Boolean state information passed to CSM.
+} DEVICE_PRODUCER_DATA_HEADER;
+
+///
+/// ATAPI_IDENTIFY
+///
+typedef struct {
+  UINT16                            Raw[256];     ///< Raw data from the IDE IdentifyDrive command.
+} ATAPI_IDENTIFY;
+
+///
+/// HDD_INFO
+///
+typedef struct {
+  ///
+  /// Status of IDE device. Values are defined below. There is one HDD_INFO structure
+  /// per IDE controller. The IdentifyDrive is per drive. Index 0 is master and index
+  /// 1 is slave.
+  ///
+  UINT16                            Status;
+
+  ///
+  /// PCI bus of IDE controller.
+  ///
+  UINT32                            Bus;
+
+  ///
+  /// PCI device of IDE controller.
+  ///
+  UINT32                            Device;
+
+  ///
+  /// PCI function of IDE controller.
+  ///
+  UINT32                            Function;
+
+  ///
+  /// Command ports base address.
+  ///
+  UINT16                            CommandBaseAddress;
+
+  ///
+  /// Control ports base address.
+  ///
+  UINT16                            ControlBaseAddress;
+
+  ///
+  /// Bus master address.
+  ///
+  UINT16                            BusMasterAddress;
+
+  UINT8                             HddIrq;
+
+  ///
+  /// Data that identifies the drive data; one per possible attached drive.
+  ///
+  ATAPI_IDENTIFY                    IdentifyDrive[2];
+} HDD_INFO;
+
+///
+/// HDD_INFO status bits
+///
+#define HDD_PRIMARY               0x01
+#define HDD_SECONDARY             0x02
+#define HDD_MASTER_ATAPI_CDROM    0x04
+#define HDD_SLAVE_ATAPI_CDROM     0x08
+#define HDD_MASTER_IDE            0x20
+#define HDD_SLAVE_IDE             0x40
+#define HDD_MASTER_ATAPI_ZIPDISK  0x10
+#define HDD_SLAVE_ATAPI_ZIPDISK   0x80
+
+///
+/// BBS_STATUS_FLAGS;\.
+///
+typedef struct {
+  UINT16                            OldPosition : 4;    ///< Prior priority.
+  UINT16                            Reserved1 : 4;      ///< Reserved for future use.
+  UINT16                            Enabled : 1;        ///< If 0, ignore this entry.
+  UINT16                            Failed : 1;         ///< 0 = Not known if boot failure occurred.
+                                                        ///< 1 = Boot attempted failed.
+
+  ///
+  /// State of media present.
+  ///   00 = No bootable media is present in the device.
+  ///   01 = Unknown if a bootable media present.
+  ///   10 = Media is present and appears bootable.
+  ///   11 = Reserved.
+  ///
+  UINT16                            MediaPresent : 2;
+  UINT16                            Reserved2 : 4;      ///< Reserved for future use.
+} BBS_STATUS_FLAGS;
+
+///
+/// BBS_TABLE, device type values & boot priority values.
+///
+typedef struct {
+  ///
+  /// The boot priority for this boot device. Values are defined below.
+  ///
+  UINT16                            BootPriority;
+
+  ///
+  /// The PCI bus for this boot device.
+  ///
+  UINT32                            Bus;
+
+  ///
+  /// The PCI device for this boot device.
+  ///
+  UINT32                            Device;
+
+  ///
+  /// The PCI function for the boot device.
+  ///
+  UINT32                            Function;
+
+  ///
+  /// The PCI class for this boot device.
+  ///
+  UINT8                             Class;
+
+  ///
+  /// The PCI Subclass for this boot device.
+  ///
+  UINT8                             SubClass;
+
+  ///
+  /// Segment:offset address of an ASCIIZ description string describing the manufacturer.
+  ///
+  UINT16                            MfgStringOffset;
+
+  ///
+  /// Segment:offset address of an ASCIIZ description string describing the manufacturer.
+  ///
+  UINT16                            MfgStringSegment;
+
+  ///
+  /// BBS device type. BBS device types are defined below.
+  ///
+  UINT16                            DeviceType;
+
+  ///
+  /// Status of this boot device. Type BBS_STATUS_FLAGS is defined below.
+  ///
+  BBS_STATUS_FLAGS                  StatusFlags;
+
+  ///
+  /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for
+  /// BCV devices.
+  ///
+  UINT16                            BootHandlerOffset;
+
+  ///
+  /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for
+  /// BCV devices.
+  ///
+  UINT16                            BootHandlerSegment;
+
+  ///
+  /// Segment:offset address of an ASCIIZ description string describing this device.
+  ///
+  UINT16                            DescStringOffset;
+
+  ///
+  /// Segment:offset address of an ASCIIZ description string describing this device.
+  ///
+  UINT16                            DescStringSegment;
+
+  ///
+  /// Reserved.
+  ///
+  UINT32                            InitPerReserved;
+
+  ///
+  /// The use of these fields is IBV dependent. They can be used to flag that an OpROM
+  /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI
+  /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup
+  ///
+  UINT32                            AdditionalIrq13Handler;
+
+  ///
+  /// The use of these fields is IBV dependent. They can be used to flag that an OpROM
+  /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI
+  /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup
+  ///
+  UINT32                            AdditionalIrq18Handler;
+
+  ///
+  /// The use of these fields is IBV dependent. They can be used to flag that an OpROM
+  /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI
+  /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup
+  ///
+  UINT32                            AdditionalIrq19Handler;
+
+  ///
+  /// The use of these fields is IBV dependent. They can be used to flag that an OpROM
+  /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI
+  /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup
+  ///
+  UINT32                            AdditionalIrq40Handler;
+  UINT8                             AssignedDriveNumber;
+  UINT32                            AdditionalIrq41Handler;
+  UINT32                            AdditionalIrq46Handler;
+  UINT32                            IBV1;
+  UINT32                            IBV2;
+} BBS_TABLE;
+
+///
+/// BBS device type values
+///@{
+#define BBS_FLOPPY              0x01
+#define BBS_HARDDISK            0x02
+#define BBS_CDROM               0x03
+#define BBS_PCMCIA              0x04
+#define BBS_USB                 0x05
+#define BBS_EMBED_NETWORK       0x06
+#define BBS_BEV_DEVICE          0x80
+#define BBS_UNKNOWN             0xff
+///@}
+
+///
+/// BBS boot priority values
+///@{
+#define BBS_DO_NOT_BOOT_FROM    0xFFFC
+#define BBS_LOWEST_PRIORITY     0xFFFD
+#define BBS_UNPRIORITIZED_ENTRY 0xFFFE
+#define BBS_IGNORE_ENTRY        0xFFFF
+///@}
+
+///
+/// SMM_ATTRIBUTES
+///
+typedef struct {
+  ///
+  /// Access mechanism used to generate the soft SMI. Defined types are below. The other
+  /// values are reserved for future usage.
+  ///
+  UINT16                            Type : 3;
+
+  ///
+  /// The size of "port" in bits. Defined values are below.
+  ///
+  UINT16                            PortGranularity : 3;
+
+  ///
+  /// The size of data in bits. Defined values are below.
+  ///
+  UINT16                            DataGranularity : 3;
+
+  ///
+  /// Reserved for future use.
+  ///
+  UINT16                            Reserved : 7;
+} SMM_ATTRIBUTES;
+
+///
+/// SMM_ATTRIBUTES type values.
+///@{
+#define STANDARD_IO       0x00
+#define STANDARD_MEMORY   0x01
+///@}
+
+///
+/// SMM_ATTRIBUTES port size constants.
+///@{
+#define PORT_SIZE_8       0x00
+#define PORT_SIZE_16      0x01
+#define PORT_SIZE_32      0x02
+#define PORT_SIZE_64      0x03
+///@}
+
+///
+/// SMM_ATTRIBUTES data size constants.
+///@{
+#define DATA_SIZE_8       0x00
+#define DATA_SIZE_16      0x01
+#define DATA_SIZE_32      0x02
+#define DATA_SIZE_64      0x03
+///@}
+
+///
+/// SMM_FUNCTION & relating constants.
+///
+typedef struct {
+  UINT16                            Function : 15;
+  UINT16                            Owner : 1;
+} SMM_FUNCTION;
+
+///
+/// SMM_FUNCTION Function constants.
+///@{
+#define INT15_D042        0x0000
+#define GET_USB_BOOT_INFO 0x0001
+#define DMI_PNP_50_57     0x0002
+///@}
+
+///
+/// SMM_FUNCTION Owner constants.
+///@{
+#define STANDARD_OWNER    0x0
+#define OEM_OWNER         0x1
+///@}
+
+///
+/// This structure assumes both port and data sizes are 1. SmmAttribute must be
+/// properly to reflect that assumption.
+///
+typedef struct {
+  ///
+  /// Describes the access mechanism, SmmPort, and SmmData sizes. Type
+  /// SMM_ATTRIBUTES is defined below.
+  ///
+  SMM_ATTRIBUTES                    SmmAttributes;
+
+  ///
+  /// Function Soft SMI is to perform. Type SMM_FUNCTION is defined below.
+  ///
+  SMM_FUNCTION                      SmmFunction;
+
+  ///
+  /// SmmPort size depends upon SmmAttributes and ranges from2 bytes to 16 bytes.
+  ///
+  UINT8                             SmmPort;
+
+  ///
+  /// SmmData size depends upon SmmAttributes and ranges from2 bytes to 16 bytes.
+  ///
+  UINT8                             SmmData;
+} SMM_ENTRY;
+
+///
+/// SMM_TABLE
+///
+typedef struct {
+  UINT16                            NumSmmEntries;    ///< Number of entries represented by SmmEntry.
+  SMM_ENTRY                         SmmEntry;         ///< One entry per function. Type SMM_ENTRY is defined below.
+} SMM_TABLE;
+
+///
+/// UDC_ATTRIBUTES
+///
+typedef struct {
+  ///
+  /// This bit set indicates that the ServiceAreaData is valid.
+  ///
+  UINT8                             DirectoryServiceValidity : 1;
+
+  ///
+  /// This bit set indicates to use the Reserve Area Boot Code Address (RACBA) only if
+  /// DirectoryServiceValidity is 0.
+  ///
+  UINT8                             RabcaUsedFlag : 1;
+
+  ///
+  /// This bit set indicates to execute hard disk diagnostics.
+  ///
+  UINT8                             ExecuteHddDiagnosticsFlag : 1;
+
+  ///
+  /// Reserved for future use. Set to 0.
+  ///
+  UINT8                             Reserved : 5;
+} UDC_ATTRIBUTES;
+
+///
+/// UD_TABLE
+///
+typedef struct {
+  ///
+  /// This field contains the bit-mapped attributes of the PARTIES information. Type
+  /// UDC_ATTRIBUTES is defined below.
+  ///
+  UDC_ATTRIBUTES                    Attributes;
+
+  ///
+  /// This field contains the zero-based device on which the selected
+  /// ServiceDataArea is present. It is 0 for master and 1 for the slave device.
+  ///
+  UINT8                             DeviceNumber;
+
+  ///
+  /// This field contains the zero-based index into the BbsTable for the parent device.
+  /// This index allows the user to reference the parent device information such as PCI
+  /// bus, device function.
+  ///
+  UINT8                             BbsTableEntryNumberForParentDevice;
+
+  ///
+  /// This field contains the zero-based index into the BbsTable for the boot entry.
+  ///
+  UINT8                             BbsTableEntryNumberForBoot;
+
+  ///
+  /// This field contains the zero-based index into the BbsTable for the HDD diagnostics entry.
+  ///
+  UINT8                             BbsTableEntryNumberForHddDiag;
+
+  ///
+  /// The raw Beer data.
+  ///
+  UINT8                             BeerData[128];
+
+  ///
+  /// The raw data of selected service area.
+  ///
+  UINT8                             ServiceAreaData[64];
+} UD_TABLE;
+
+#define EFI_TO_LEGACY_MAJOR_VERSION 0x02
+#define EFI_TO_LEGACY_MINOR_VERSION 0x00
+#define MAX_IDE_CONTROLLER          8
+
+///
+/// EFI_TO_COMPATIBILITY16_BOOT_TABLE
+///
+typedef struct {
+  UINT16                            MajorVersion;                 ///< The EfiCompatibility major version number.
+  UINT16                            MinorVersion;                 ///< The EfiCompatibility minor version number.
+  UINT32                            AcpiTable;                    ///< The location of the RSDT ACPI table. < 4G range.
+  UINT32                            SmbiosTable;                  ///< The location of the SMBIOS table in EFI memory. < 4G range.
+  UINT32                            SmbiosTableLength;
+  //
+  // Legacy SIO state
+  //
+  DEVICE_PRODUCER_DATA_HEADER       SioData;                      ///< Standard traditional device information.
+  UINT16                            DevicePathType;               ///< The default boot type.
+  UINT16                            PciIrqMask;                   ///< Mask of which IRQs have been assigned to PCI.
+  UINT32                            NumberE820Entries;            ///< Number of E820 entries. The number can change from the
+                                                                  ///< Compatibility16InitializeYourself() function.
+  //
+  // Controller & Drive Identify[2] per controller information
+  //
+  HDD_INFO                          HddInfo[MAX_IDE_CONTROLLER];  ///< Hard disk drive information, including raw Identify Drive data.
+  UINT32                            NumberBbsEntries;             ///< Number of entries in the BBS table
+  UINT32                            BbsTable;                     ///< A pointer to the BBS table. Type BBS_TABLE is defined below.
+  UINT32                            SmmTable;                     ///< A pointer to the SMM table. Type SMM_TABLE is defined below.
+  UINT32                            OsMemoryAbove1Mb;             ///< The amount of usable memory above 1 MB, i.e. E820 type 1 memory. This value can
+                                                                  ///< differ from the value in EFI_TO_COMPATIBILITY16_INIT_TABLE as more
+                                                                  ///< memory may have been discovered.
+  UINT32                            UnconventionalDeviceTable;    ///< Information to boot off an unconventional device like a PARTIES partition. Type
+                                                                  ///< UD_TABLE is defined below.
+} EFI_TO_COMPATIBILITY16_BOOT_TABLE;
+
+///
+/// EFI_LEGACY_INSTALL_PCI_HANDLER
+///
+typedef struct {
+  UINT8                             PciBus;             ///< The PCI bus of the device.
+  UINT8                             PciDeviceFun;       ///< The PCI device in bits 7:3 and function in bits 2:0.
+  UINT8                             PciSegment;         ///< The PCI segment of the device.
+  UINT8                             PciClass;           ///< The PCI class code of the device.
+  UINT8                             PciSubclass;        ///< The PCI subclass code of the device.
+  UINT8                             PciInterface;       ///< The PCI interface code of the device.
+  //
+  // Primary section
+  //
+  UINT8                             PrimaryIrq;         ///< The primary device IRQ.
+  UINT8                             PrimaryReserved;    ///< Reserved.
+  UINT16                            PrimaryControl;     ///< The primary device control I/O base.
+  UINT16                            PrimaryBase;        ///< The primary device I/O base.
+  UINT16                            PrimaryBusMaster;   ///< The primary device bus master I/O base.
+  //
+  // Secondary Section
+  //
+  UINT8                             SecondaryIrq;       ///< The secondary device IRQ.
+  UINT8                             SecondaryReserved;  ///< Reserved.
+  UINT16                            SecondaryControl;   ///< The secondary device control I/O base.
+  UINT16                            SecondaryBase;      ///< The secondary device I/O base.
+  UINT16                            SecondaryBusMaster; ///< The secondary device bus master I/O base.
+} EFI_LEGACY_INSTALL_PCI_HANDLER;
+
+//
+// Restore default pack value
+//
+#pragma pack()
+
+#define EFI_LEGACY_BIOS_PROTOCOL_GUID \
+  { \
+    0xdb9a1e3d, 0x45cb, 0x4abb, {0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d } \
+  }
+
+typedef struct _EFI_LEGACY_BIOS_PROTOCOL EFI_LEGACY_BIOS_PROTOCOL;
+
+///
+/// Flags returned by CheckPciRom().
+///
+#define NO_ROM            0x00
+#define ROM_FOUND         0x01
+#define VALID_LEGACY_ROM  0x02
+#define ROM_WITH_CONFIG   0x04     ///< Not defined in the Framework CSM Specification.
+
+///
+/// The following macros do not appear in the Framework CSM Specification and
+/// are kept for backward compatibility only.  They convert 32-bit address (_Adr)
+/// to Segment:Offset 16-bit form.
+///
+///@{
+#define EFI_SEGMENT(_Adr)     (UINT16) ((UINT16) (((UINTN) (_Adr)) >> 4) & 0xf000)
+#define EFI_OFFSET(_Adr)      (UINT16) (((UINT16) ((UINTN) (_Adr))) & 0xffff)
+///@}
+
+#define CARRY_FLAG            0x01
+
+///
+/// EFI_EFLAGS_REG
+///
+typedef struct {
+  UINT32 CF:1;
+  UINT32 Reserved1:1;
+  UINT32 PF:1;
+  UINT32 Reserved2:1;
+  UINT32 AF:1;
+  UINT32 Reserved3:1;
+  UINT32 ZF:1;
+  UINT32 SF:1;
+  UINT32 TF:1;
+  UINT32 IF:1;
+  UINT32 DF:1;
+  UINT32 OF:1;
+  UINT32 IOPL:2;
+  UINT32 NT:1;
+  UINT32 Reserved4:2;
+  UINT32 VM:1;
+  UINT32 Reserved5:14;
+} EFI_EFLAGS_REG;
+
+///
+/// EFI_DWORD_REGS
+///
+typedef struct {
+    UINT32           EAX;
+    UINT32           EBX;
+    UINT32           ECX;
+    UINT32           EDX;
+    UINT32           ESI;
+    UINT32           EDI;
+    EFI_EFLAGS_REG   EFlags;
+    UINT16           ES;
+    UINT16           CS;
+    UINT16           SS;
+    UINT16           DS;
+    UINT16           FS;
+    UINT16           GS;
+    UINT32           EBP;
+    UINT32           ESP;
+} EFI_DWORD_REGS;
+
+///
+/// EFI_FLAGS_REG
+///
+typedef struct {
+  UINT16     CF:1;
+  UINT16     Reserved1:1;
+  UINT16     PF:1;
+  UINT16     Reserved2:1;
+  UINT16     AF:1;
+  UINT16     Reserved3:1;
+  UINT16     ZF:1;
+  UINT16     SF:1;
+  UINT16     TF:1;
+  UINT16     IF:1;
+  UINT16     DF:1;
+  UINT16     OF:1;
+  UINT16     IOPL:2;
+  UINT16     NT:1;
+  UINT16     Reserved4:1;
+} EFI_FLAGS_REG;
+
+///
+/// EFI_WORD_REGS
+///
+typedef struct {
+    UINT16           AX;
+    UINT16           ReservedAX;
+    UINT16           BX;
+    UINT16           ReservedBX;
+    UINT16           CX;
+    UINT16           ReservedCX;
+    UINT16           DX;
+    UINT16           ReservedDX;
+    UINT16           SI;
+    UINT16           ReservedSI;
+    UINT16           DI;
+    UINT16           ReservedDI;
+    EFI_FLAGS_REG    Flags;
+    UINT16           ReservedFlags;
+    UINT16           ES;
+    UINT16           CS;
+    UINT16           SS;
+    UINT16           DS;
+    UINT16           FS;
+    UINT16           GS;
+    UINT16           BP;
+    UINT16           ReservedBP;
+    UINT16           SP;
+    UINT16           ReservedSP;
+} EFI_WORD_REGS;
+
+///
+/// EFI_BYTE_REGS
+///
+typedef struct {
+    UINT8   AL, AH;
+    UINT16  ReservedAX;
+    UINT8   BL, BH;
+    UINT16  ReservedBX;
+    UINT8   CL, CH;
+    UINT16  ReservedCX;
+    UINT8   DL, DH;
+    UINT16  ReservedDX;
+} EFI_BYTE_REGS;
+
+///
+/// EFI_IA32_REGISTER_SET
+///
+typedef union {
+  EFI_DWORD_REGS  E;
+  EFI_WORD_REGS   X;
+  EFI_BYTE_REGS   H;
+} EFI_IA32_REGISTER_SET;
+
+/**
+  Thunk to 16-bit real mode and execute a software interrupt with a vector
+  of BiosInt. Regs will contain the 16-bit register context on entry and
+  exit.
+
+  @param[in]     This      The protocol instance pointer.
+  @param[in]     BiosInt   The processor interrupt vector to invoke.
+  @param[in,out] Reg       Register contexted passed into (and returned) from thunk to
+                           16-bit mode.
+
+  @retval TRUE                Thunk completed with no BIOS errors in the target code. See Regs for status.
+  @retval FALSE                  There was a BIOS error in the target code.
+**/
+typedef
+BOOLEAN
+(EFIAPI *EFI_LEGACY_BIOS_INT86)(
+  IN     EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN     UINT8                     BiosInt,
+  IN OUT EFI_IA32_REGISTER_SET     *Regs
+  );
+
+/**
+  Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
+  16-bit register context on entry and exit. Arguments can be passed on
+  the Stack argument
+
+  @param[in] This        The protocol instance pointer.
+  @param[in] Segment     The segemnt of 16-bit mode call.
+  @param[in] Offset      The offset of 16-bit mdoe call.
+  @param[in] Reg         Register contexted passed into (and returned) from thunk to
+                         16-bit mode.
+  @param[in] Stack       The caller allocated stack used to pass arguments.
+  @param[in] StackSize   The size of Stack in bytes.
+
+  @retval FALSE                 Thunk completed with no BIOS errors in the target code.                                See Regs for status.  @retval TRUE                  There was a BIOS error in the target code.
+**/
+typedef
+BOOLEAN
+(EFIAPI *EFI_LEGACY_BIOS_FARCALL86)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN UINT16                    Segment,
+  IN UINT16                    Offset,
+  IN EFI_IA32_REGISTER_SET     *Regs,
+  IN VOID                      *Stack,
+  IN UINTN                     StackSize
+  );
+
+/**
+  Test to see if a legacy PCI ROM exists for this device. Optionally return
+  the Legacy ROM instance for this PCI device.
+
+  @param[in]  This        The protocol instance pointer.
+  @param[in]  PciHandle   The PCI PC-AT OPROM from this devices ROM BAR will be loaded
+  @param[out] RomImage    Return the legacy PCI ROM for this device.
+  @param[out] RomSize     The size of ROM Image.
+  @param[out] Flags       Indicates if ROM found and if PC-AT. Multiple bits can be set as follows:
+                            - 00 = No ROM.
+                            - 01 = ROM Found.
+                            - 02 = ROM is a valid legacy ROM.
+
+  @retval EFI_SUCCESS       The Legacy Option ROM available for this device
+  @retval EFI_UNSUPPORTED   The Legacy Option ROM is not supported.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_CHECK_ROM)(
+  IN  EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN  EFI_HANDLE                PciHandle,
+  OUT VOID                      **RomImage, OPTIONAL
+  OUT UINTN                     *RomSize, OPTIONAL
+  OUT UINTN                     *Flags
+  );
+
+/**
+  Load a legacy PC-AT OPROM on the PciHandle device. Return information
+  about how many disks were added by the OPROM and the shadow address and
+  size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C:
+
+  @param[in]  This               The protocol instance pointer.
+  @param[in]  PciHandle          The PCI PC-AT OPROM from this devices ROM BAR will be loaded.
+                                 This value is NULL if RomImage is non-NULL. This is the normal
+                                 case.
+  @param[in]  RomImage           A PCI PC-AT ROM image. This argument is non-NULL if there is
+                                 no hardware associated with the ROM and thus no PciHandle,
+                                 otherwise is must be NULL.
+                                 Example is PXE base code.
+  @param[out] Flags              The type of ROM discovered. Multiple bits can be set, as follows:
+                                   - 00 = No ROM.
+                                   - 01 = ROM found.
+                                   - 02 = ROM is a valid legacy ROM.
+  @param[out] DiskStart          The disk number of first device hooked by the ROM. If DiskStart
+                                 is the same as DiskEnd no disked were hooked.
+  @param[out] DiskEnd            disk number of the last device hooked by the ROM.
+  @param[out] RomShadowAddress   Shadow address of PC-AT ROM.
+  @param[out] RomShadowSize      Size of RomShadowAddress in bytes.
+
+  @retval EFI_SUCCESS             Thunk completed, see Regs for status.
+  @retval EFI_INVALID_PARAMETER   PciHandle not found
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_INSTALL_ROM)(
+  IN  EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN  EFI_HANDLE                PciHandle,
+  IN  VOID                      **RomImage,
+  OUT UINTN                     *Flags,
+  OUT UINT8                     *DiskStart, OPTIONAL
+  OUT UINT8                     *DiskEnd, OPTIONAL
+  OUT VOID                      **RomShadowAddress, OPTIONAL
+  OUT UINT32                    *ShadowedRomSize OPTIONAL
+  );
+
+/**
+  This function attempts to traditionally boot the specified BootOption. If the EFI context has
+  been compromised, this function will not return. This procedure is not used for loading an EFI-aware
+  OS off a traditional device. The following actions occur:
+  - Get EFI SMBIOS data structures, convert them to a traditional format, and copy to
+    Compatibility16.
+  - Get a pointer to ACPI data structures and copy the Compatibility16 RSD PTR to F0000 block.
+  - Find the traditional SMI handler from a firmware volume and register the traditional SMI
+    handler with the EFI SMI handler.
+  - Build onboard IDE information and pass this information to the Compatibility16 code.
+  - Make sure all PCI Interrupt Line registers are programmed to match 8259.
+  - Reconfigure SIO devices from EFI mode (polled) into traditional mode (interrupt driven).
+  - Shadow all PCI ROMs.
+  - Set up BDA and EBDA standard areas before the legacy boot.
+  - Construct the Compatibility16 boot memory map and pass it to the Compatibility16 code.
+  - Invoke the Compatibility16 table function Compatibility16PrepareToBoot(). This
+    invocation causes a thunk into the Compatibility16 code, which sets all appropriate internal
+    data structures. The boot device list is a parameter.
+  - Invoke the Compatibility16 Table function Compatibility16Boot(). This invocation
+    causes a thunk into the Compatibility16 code, which does an INT19.
+  - If the Compatibility16Boot() function returns, then the boot failed in a graceful
+    manner--meaning that the EFI code is still valid. An ungraceful boot failure causes a reset because the state
+    of EFI code is unknown.
+
+  @param[in] This             The protocol instance pointer.
+  @param[in] BootOption       The EFI Device Path from BootXXXX variable.
+  @param[in] LoadOptionSize   The size of LoadOption in size.
+  @param[in] LoadOption       LThe oadOption from BootXXXX variable.
+
+  @retval EFI_DEVICE_ERROR      Failed to boot from any boot device and memory is uncorrupted.                                Note: This function normally does not returns. It will either boot the                                OS or reset the system if memory has been "corrupted" by loading                                a boot sector and passing control to it.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_BOOT)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN BBS_BBS_DEVICE_PATH       *BootOption,
+  IN UINT32                    LoadOptionsSize,
+  IN VOID                      *LoadOptions
+  );
+
+/**
+  This function takes the Leds input parameter and sets/resets the BDA accordingly.
+  Leds is also passed to Compatibility16 code, in case any special processing is required.
+  This function is normally called from EFI Setup drivers that handle user-selectable
+  keyboard options such as boot with NUM LOCK on/off. This function does not
+  touch the keyboard or keyboard LEDs but only the BDA.
+
+  @param[in] This   The protocol instance pointer.
+  @param[in] Leds   The status of current Scroll, Num & Cap lock LEDS:
+                      - Bit 0 is Scroll Lock 0 = Not locked.
+                      - Bit 1 is Num Lock.
+                      - Bit 2 is Caps Lock.
+
+  @retval EFI_SUCCESS   The BDA was updated successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_UPDATE_KEYBOARD_LED_STATUS)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN UINT8                     Leds
+  );
+
+/**
+  Retrieve legacy BBS info and assign boot priority.
+
+  @param[in]     This       The protocol instance pointer.
+  @param[out]    HddCount   The number of HDD_INFO structures.
+  @param[out]    HddInfo    Onboard IDE controller information.
+  @param[out]    BbsCount   The number of BBS_TABLE structures.
+  @param[in,out] BbsTable   Points to List of BBS_TABLE.
+
+  @retval EFI_SUCCESS   Tables were returned.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_GET_BBS_INFO)(
+  IN     EFI_LEGACY_BIOS_PROTOCOL  *This,
+  OUT    UINT16                    *HddCount,
+  OUT    HDD_INFO                  **HddInfo,
+  OUT    UINT16                    *BbsCount,
+  IN OUT BBS_TABLE                 **BbsTable
+  );
+
+/**
+  Assign drive number to legacy HDD drives prior to booting an EFI
+  aware OS so the OS can access drives without an EFI driver.
+
+  @param[in]  This       The protocol instance pointer.
+  @param[out] BbsCount   The number of BBS_TABLE structures
+  @param[out] BbsTable   List of BBS entries
+
+  @retval EFI_SUCCESS   Drive numbers assigned.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PREPARE_TO_BOOT_EFI)(
+  IN  EFI_LEGACY_BIOS_PROTOCOL  *This,
+  OUT UINT16                    *BbsCount,
+  OUT BBS_TABLE                 **BbsTable
+  );
+
+/**
+  To boot from an unconventional device like parties and/or execute
+  HDD diagnostics.
+
+  @param[in]  This              The protocol instance pointer.
+  @param[in]  Attributes        How to interpret the other input parameters.
+  @param[in]  BbsEntry          The 0-based index into the BbsTable for the parent
+                                device.
+  @param[in]  BeerData          A pointer to the 128 bytes of ram BEER data.
+  @param[in]  ServiceAreaData   A pointer to the 64 bytes of raw Service Area data. The
+                                caller must provide a pointer to the specific Service
+                                Area and not the start all Service Areas.
+
+  @retval EFI_INVALID_PARAMETER   If error. Does NOT return if no error.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_BOOT_UNCONVENTIONAL_DEVICE)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN UDC_ATTRIBUTES            Attributes,
+  IN UINTN                     BbsEntry,
+  IN VOID                      *BeerData,
+  IN VOID                      *ServiceAreaData
+  );
+
+/**
+  Shadow all legacy16 OPROMs that haven't been shadowed.
+  Warning: Use this with caution. This routine disconnects all EFI
+  drivers. If used externally, then  the caller must re-connect EFI
+  drivers.
+
+  @param[in]  This   The protocol instance pointer.
+
+  @retval EFI_SUCCESS   OPROMs were shadowed.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_SHADOW_ALL_LEGACY_OPROMS)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This
+  );
+
+/**
+  Get a region from the LegacyBios for S3 usage.
+
+  @param[in]  This                  The protocol instance pointer.
+  @param[in]  LegacyMemorySize      The size of required region.
+  @param[in]  Region                The region to use.
+                                    00 = Either 0xE0000 or 0xF0000 block.
+                                      - Bit0 = 1 0xF0000 block.
+                                      - Bit1 = 1 0xE0000 block.
+  @param[in]  Alignment             Address alignment. Bit mapped. The first non-zero
+                                    bit from right is alignment.
+  @param[out] LegacyMemoryAddress   The Region Assigned
+
+  @retval EFI_SUCCESS           The Region was assigned.
+  @retval EFI_ACCESS_DENIED     The function was previously invoked.
+  @retval Other                 The Region was not assigned.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_GET_LEGACY_REGION)(
+  IN  EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN  UINTN                     LegacyMemorySize,
+  IN  UINTN                     Region,
+  IN  UINTN                     Alignment,
+  OUT VOID                      **LegacyMemoryAddress
+  );
+
+/**
+  Get a region from the LegacyBios for Tiano usage. Can only be invoked once.
+
+  @param[in]  This                        The protocol instance pointer.
+  @param[in]  LegacyMemorySize            The size of data to copy.
+  @param[in]  LegacyMemoryAddress         The Legacy Region destination address.
+                                          Note: must be in region assigned by
+                                          LegacyBiosGetLegacyRegion.
+  @param[in]  LegacyMemorySourceAddress   The source of the data to copy.
+
+  @retval EFI_SUCCESS           The Region assigned.
+  @retval EFI_ACCESS_DENIED     Destination was outside an assigned region.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_COPY_LEGACY_REGION)(
+  IN EFI_LEGACY_BIOS_PROTOCOL  *This,
+  IN UINTN                     LegacyMemorySize,
+  IN VOID                      *LegacyMemoryAddress,
+  IN VOID                      *LegacyMemorySourceAddress
+  );
+
+///
+/// Abstracts the traditional BIOS from the rest of EFI. The LegacyBoot()
+/// member function allows the BDS to support booting a traditional OS.
+/// EFI thunks drivers that make EFI bindings for BIOS INT services use
+/// all the other member functions.
+///
+struct _EFI_LEGACY_BIOS_PROTOCOL {
+  ///
+  /// Performs traditional software INT. See the Int86() function description.
+  ///
+  EFI_LEGACY_BIOS_INT86                       Int86;
+
+  ///
+  /// Performs a far call into Compatibility16 or traditional OpROM code.
+  ///
+  EFI_LEGACY_BIOS_FARCALL86                   FarCall86;
+
+  ///
+  /// Checks if a traditional OpROM exists for this device.
+  ///
+  EFI_LEGACY_BIOS_CHECK_ROM                   CheckPciRom;
+
+  ///
+  /// Loads a traditional OpROM in traditional OpROM address space.
+  ///
+  EFI_LEGACY_BIOS_INSTALL_ROM                 InstallPciRom;
+
+  ///
+  /// Boots a traditional OS.
+  ///
+  EFI_LEGACY_BIOS_BOOT                        LegacyBoot;
+
+  ///
+  /// Updates BDA to reflect the current EFI keyboard LED status.
+  ///
+  EFI_LEGACY_BIOS_UPDATE_KEYBOARD_LED_STATUS  UpdateKeyboardLedStatus;
+
+  ///
+  /// Allows an external agent, such as BIOS Setup, to get the BBS data.
+  ///
+  EFI_LEGACY_BIOS_GET_BBS_INFO                GetBbsInfo;
+
+  ///
+  /// Causes all legacy OpROMs to be shadowed.
+  ///
+  EFI_LEGACY_BIOS_SHADOW_ALL_LEGACY_OPROMS    ShadowAllLegacyOproms;
+
+  ///
+  /// Performs all actions prior to boot. Used when booting an EFI-aware OS
+  /// rather than a legacy OS.
+  ///
+  EFI_LEGACY_BIOS_PREPARE_TO_BOOT_EFI         PrepareToBootEfi;
+
+  ///
+  /// Allows EFI to reserve an area in the 0xE0000 or 0xF0000 block.
+  ///
+  EFI_LEGACY_BIOS_GET_LEGACY_REGION           GetLegacyRegion;
+
+  ///
+  /// Allows EFI to copy data to the area specified by GetLegacyRegion.
+  ///
+  EFI_LEGACY_BIOS_COPY_LEGACY_REGION          CopyLegacyRegion;
+
+  ///
+  /// Allows the user to boot off an unconventional device such as a PARTIES partition.
+  ///
+  EFI_LEGACY_BIOS_BOOT_UNCONVENTIONAL_DEVICE  BootUnconventionalDevice;
+};
+
+//
+// Legacy BIOS needs to access memory in page 0 (0-4095), which is disabled if
+// NULL pointer detection feature is enabled. Following macro can be used to
+// enable/disable page 0 before/after accessing it.
+//
+#define ACCESS_PAGE0_CODE(statements)                           \
+  do {                                                          \
+    EFI_STATUS                            Status_;              \
+    EFI_GCD_MEMORY_SPACE_DESCRIPTOR       Desc_;                \
+                                                                \
+    Desc_.Attributes = 0;                                       \
+    Status_ = gDS->GetMemorySpaceDescriptor (0, &Desc_);        \
+    ASSERT_EFI_ERROR (Status_);                                 \
+    if ((Desc_.Attributes & EFI_MEMORY_RP) != 0) {              \
+      Status_ = gDS->SetMemorySpaceAttributes (                 \
+                      0,                                        \
+                      EFI_PAGES_TO_SIZE(1),                     \
+                      Desc_.Attributes & ~(UINT64)EFI_MEMORY_RP \
+                      );                                        \
+      ASSERT_EFI_ERROR (Status_);                               \
+    }                                                           \
+                                                                \
+    {                                                           \
+      statements;                                               \
+    }                                                           \
+                                                                \
+    if ((Desc_.Attributes & EFI_MEMORY_RP) != 0) {              \
+      Status_ = gDS->SetMemorySpaceAttributes (                 \
+                      0,                                        \
+                      EFI_PAGES_TO_SIZE(1),                     \
+                      Desc_.Attributes                          \
+                      );                                        \
+      ASSERT_EFI_ERROR (Status_);                               \
+    }                                                           \
+  } while (FALSE)
+
+extern EFI_GUID gEfiLegacyBiosProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/LegacyBiosPlatform.h b/OvmfPkg/Csm/Include/Protocol/LegacyBiosPlatform.h
new file mode 100644
index 0000000000..0a164dad3b
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/LegacyBiosPlatform.h
@@ -0,0 +1,755 @@
+/** @file
+  The EFI Legacy BIOS Patform Protocol is used to mate a Legacy16
+  implementation with this EFI code. The EFI driver that produces
+  the Legacy BIOS protocol is generic and consumes this protocol.
+  A driver that matches the Legacy16 produces this protocol
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  This protocol is defined in Framework for EFI Compatibility Support Module spec
+  Version 0.97.
+
+**/
+
+#ifndef _EFI_LEGACY_BIOS_PLATFORM_H_
+#define _EFI_LEGACY_BIOS_PLATFORM_H_
+
+///
+/// Legacy BIOS Platform depends on HDD_INFO and EFI_COMPATIBILITY16_TABLE that
+/// are defined with the Legacy BIOS Protocol
+///
+#include <Protocol/LegacyBios.h>
+
+#define EFI_LEGACY_BIOS_PLATFORM_PROTOCOL_GUID \
+  { \
+    0x783658a3, 0x4172, 0x4421, {0xa2, 0x99, 0xe0, 0x9, 0x7, 0x9c, 0xc, 0xb4 } \
+  }
+
+typedef struct _EFI_LEGACY_BIOS_PLATFORM_PROTOCOL EFI_LEGACY_BIOS_PLATFORM_PROTOCOL;
+
+/**
+  This enum specifies the Mode param values for GetPlatformInfo()
+**/
+typedef enum {
+  ///
+  /// This mode is invoked twice. The first invocation has LegacySegment and
+  /// LegacyOffset set to 0. The mode returns the MP table address in EFI memory, along with its size.
+  /// The second invocation has LegacySegment and LegacyOffset set to the location
+  /// in the 0xF0000 or 0xE0000 block to which the MP table is to be copied. The second
+  /// invocation allows any MP table address fixes to occur in the EFI memory copy of the
+  /// MP table. The caller, not EfiGetPlatformBinaryMpTable, copies the modified MP
+  /// table to the allocated region in 0xF0000 or 0xE0000 block after the second invocation.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Table Pointer to the MP table.
+  ///
+  ///   TableSize Size in bytes of the MP table.
+  ///
+  ///   Location Location to place table. 0x00. Either 0xE0000 or 0xF0000 64 KB blocks.
+  ///     Bit 0 = 1 0xF0000 64 KB block.
+  ///     Bit 1 = 1 0xE0000 64 KB block.
+  ///     Multiple bits can be set.
+  ///
+  ///   Alignment Bit-mapped address alignment granularity.
+  ///     The first nonzero bit from the right is the address granularity.
+  ///
+  //    LegacySegment Segment in which EfiCompatibility code will place the MP table.
+  ///
+  ///   LegacyOffset Offset in which EfiCompatibility code will place the MP table.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///   EFI_SUCCESS The MP table was returned.
+  ///
+  ///   EFI_UNSUPPORTED The MP table is not supported on this platform.
+  ///
+  EfiGetPlatformBinaryMpTable      = 0,
+  ///
+  /// This mode returns a block of data. The content and usage is IBV or OEM defined.
+  /// OEMs or IBVs normally use this function for nonstandard Compatibility16 runtime soft
+  /// INTs. It is the responsibility of this routine to coalesce multiple OEM 16 bit functions, if
+  /// they exist, into one coherent package that is understandable by the Compatibility16 code.
+  /// This function is invoked twice. The first invocation has LegacySegment and
+  /// LegacyOffset set to 0. The function returns the table address in EFI memory, as well as its size.
+  /// The second invocation has LegacySegment and LegacyOffset set to the location
+  /// in the 0xF0000 or 0xE0000 block to which the data (table) is to be copied. The second
+  /// invocation allows any data (table) address fixes to occur in the EFI memory copy of
+  /// the table. The caller, not GetOemIntData(), copies the modified data (table) to the
+  /// allocated region in 0xF0000 or 0xE0000 block after the second invocation.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Table Pointer to OEM legacy 16 bit code or data.
+  ///
+  ///   TableSize Size of data.
+  ///
+  ///   Location Location to place table. 0x00. Either 0xE0000 or 0xF0000 64 KB blocks.
+  ///       Bit 0 = 1 0xF0000 64 KB block.
+  ///       Bit 1 = 1 0xE0000 64 KB block.
+  ///       Multiple bits can be set.
+  ///
+  ///   Alignment Bit mapped address alignment granularity.
+  ///     The first nonzero bit from the right is the address granularity.
+  ///
+  ///   LegacySegment Segment in which EfiCompatibility code will place the table or data.
+  ///
+  ///   LegacyOffset Offset in which EfiCompatibility code will place the table or data.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///   EFI_SUCCESS The data was returned successfully.
+  ///
+  ///   EFI_UNSUPPORTED Oem INT is not supported on this platform.
+  ///
+  EfiGetPlatformBinaryOemIntData   = 1,
+  ///
+  /// This mode returns a block of data. The content and usage is IBV defined. OEMs or
+  /// IBVs normally use this mode for nonstandard Compatibility16 runtime 16 bit routines. It
+  /// is the responsibility of this routine to coalesce multiple OEM 16 bit functions, if they
+  /// exist, into one coherent package that is understandable by the Compatibility16 code.
+  ///
+  /// Example usage: A legacy mobile BIOS that has a pre-existing runtime
+  /// interface to return the battery status to calling applications.
+  ///
+  /// This mode is invoked twice. The first invocation has LegacySegment and
+  /// LegacyOffset set to 0. The mode returns the table address in EFI memory and its size.
+  /// The second invocation has LegacySegment and LegacyOffset set to the location
+  /// in the 0xF0000 or 0xE0000 block to which the table is to be copied. The second
+  /// invocation allows any table address fixes to occur in the EFI memory copy of the table.
+  /// The caller, not EfiGetPlatformBinaryOem16Data, copies the modified table to
+  /// the allocated region in 0xF0000 or 0xE0000 block after the second invocation.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Table Pointer to OEM legacy 16 bit code or data.
+  ///
+  ///   TableSize Size of data.
+  ///
+  ///   Location Location to place the table. 0x00. Either 0xE0000 or 0xF0000 64 KB blocks.
+  ///      Bit 0 = 1 0xF0000 64 KB block.
+  ///      Bit 1 = 1 0xE0000 64 KB block.
+  ///      Multiple bits can be set.
+  ///
+  ///   Alignment Bit mapped address alignment granularity.
+  ///     The first nonzero bit from the right is the address granularity.
+  ///
+  ///   LegacySegment Segment in which EfiCompatibility code will place the table or data.
+  ///
+  ///   LegacyOffset Offset in which EfiCompatibility code will place the table or data.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///   EFI_SUCCESS The data was returned successfully.
+  ///
+  ///   EFI_UNSUPPORTED Oem16 is not supported on this platform.
+  ///
+  EfiGetPlatformBinaryOem16Data    = 2,
+///
+/// This mode returns a block of data. The content and usage are IBV defined. OEMs or
+/// IBVs normally use this mode for nonstandard Compatibility16 runtime 32 bit routines. It
+/// is the responsibility of this routine to coalesce multiple OEM 32 bit functions, if they
+/// exist, into one coherent package that is understandable by the Compatibility16 code.
+///
+/// Example usage: A legacy mobile BIOS that has a pre existing runtime
+/// interface to return the battery status to calling applications.
+///
+/// This mode is invoked twice. The first invocation has LegacySegment and
+/// LegacyOffset set to 0. The mode returns the table address in EFI memory and its size.
+///
+/// The second invocation has LegacySegment and LegacyOffset set to the location
+/// in the 0xF0000 or 0xE0000 block to which the table is to be copied. The second
+/// invocation allows any table address fix ups to occur in the EFI memory copy of the table.
+/// The caller, not EfiGetPlatformBinaryOem32Data, copies the modified table to
+/// the allocated region in 0xF0000 or 0xE0000 block after the second invocation..
+///
+/// Note: There are two generic mechanisms by which this mode can be used.
+/// Mechanism 1: This mode returns the data and the Legacy BIOS Protocol copies
+/// the data into the F0000 or E0000 block in the Compatibility16 code. The
+/// EFI_COMPATIBILITY16_TABLE entries Oem32Segment and Oem32Offset can
+/// be viewed as two UINT16 entries.
+/// Mechanism 2: This mode directly fills in the EFI_COMPATIBILITY16_TABLE with
+/// a pointer to the INT15 E820 region containing the 32 bit code. It returns
+/// EFI_UNSUPPORTED. The EFI_COMPATIBILITY16_TABLE entries,
+/// Oem32Segment and Oem32Offset, can be viewed as two UINT16 entries or
+/// as a single UINT32 entry as determined by the IBV.
+///
+/// The function parameters associated with this mode are:
+///
+///   TableSize Size of data.
+///
+///   Location Location to place the table. 0x00 or 0xE0000 or 0xF0000 64 KB blocks.
+///       Bit 0 = 1 0xF0000 64 KB block.
+///       Bit 1 = 1 0xE0000 64 KB block.
+///       Multiple bits can be set.
+///
+///   Alignment Bit mapped address alignment granularity.
+///       The first nonzero bit from the right is the address granularity.
+///
+///   LegacySegment Segment in which EfiCompatibility code will place the table or data.
+///
+///   LegacyOffset Offset in which EfiCompatibility code will place the table or data.
+///
+/// The return values associated with this mode are:
+///   EFI_SUCCESS The data was returned successfully.
+///   EFI_UNSUPPORTED Oem32 is not supported on this platform.
+///
+EfiGetPlatformBinaryOem32Data    = 3,
+  ///
+  /// This mode returns a TPM binary image for the onboard TPM device.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Table TPM binary image for the onboard TPM device.
+  ///
+  ///   TableSize Size of BinaryImage in bytes.
+  ///
+  ///   Location Location to place the table. 0x00. Either 0xE0000 or 0xF0000 64 KB blocks.
+  ///      Bit 0 = 1 0xF0000 64 KB block.
+  ///      Bit 1 = 1 0xE0000 64 KB block.
+  ///      Multiple bits can be set.
+  ///
+  ///   Alignment Bit mapped address alignment granularity.
+  ///     The first nonzero bit from the right is the address granularity.
+  ///
+  ///   LegacySegment Segment in which EfiCompatibility code will place the table or data.
+  ///
+  ///   LegacyOffset Offset in which EfiCompatibility code will place the table or data.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///   EFI_SUCCESS BinaryImage is valid.
+  ///
+  ///   EFI_UNSUPPORTED Mode is not supported on this platform.
+  ///
+  ///   EFI_NOT_FOUND No BinaryImage was found.
+  ///
+  EfiGetPlatformBinaryTpmBinary    = 4,
+  ///
+  /// The mode finds the Compatibility16 Rom Image.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///    System ROM image for the platform.
+  ///
+  ///    TableSize Size of Table in bytes.
+  ///
+  ///    Location Ignored.
+  ///
+  ///    Alignment Ignored.
+  ///
+  ///    LegacySegment Ignored.
+  ///
+  ///    LegacyOffset Ignored.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///    EFI_SUCCESS ROM image found.
+  ///
+  ///    EFI_NOT_FOUND ROM not found.
+  ///
+  EfiGetPlatformBinarySystemRom    = 5,
+  ///
+  /// This mode returns the Base address of PciExpress memory mapped configuration
+  /// address space.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///    Table System ROM image for the platform.
+  ///
+  ///    TableSize Size of Table in bytes.
+  ///
+  ///    Location Ignored.
+  ///
+  ///    Alignment Ignored.
+  ///
+  ///    LegacySegment Ignored.
+  ///
+  ///    LegacyOffset Ignored.
+  ///
+  /// The return values associated with this mode are:
+  ///
+  ///   EFI_SUCCESS Address is valid.
+  ///
+  ///   EFI_UNSUPPORTED System does not PciExpress.
+  ///
+  EfiGetPlatformPciExpressBase     = 6,
+  ///
+  EfiGetPlatformPmmSize            = 7,
+  ///
+  EfiGetPlatformEndOpromShadowAddr = 8,
+  ///
+} EFI_GET_PLATFORM_INFO_MODE;
+
+/**
+  This enum specifies the Mode param values for GetPlatformHandle().
+**/
+typedef enum {
+  ///
+  /// This mode returns the Compatibility16 policy for the device that should be the VGA
+  /// controller used during a Compatibility16 boot.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Type 0x00.
+  ///
+  ///   HandleBuffer Buffer of all VGA handles found.
+  ///
+  ///   HandleCount Number of VGA handles found.
+  ///
+  ///   AdditionalData NULL.
+  ///
+  EfiGetPlatformVgaHandle       = 0,
+  ///
+  /// This mode returns the Compatibility16 policy for the device that should be the IDE
+  /// controller used during a Compatibility16 boot.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Type 0x00.
+  ///
+  ///   HandleBuffer Buffer of all IDE handles found.
+  ///
+  ///   HandleCount Number of IDE handles found.
+  ///
+  ///   AdditionalData Pointer to HddInfo.
+  ///     Information about all onboard IDE controllers.
+  ///
+  EfiGetPlatformIdeHandle       = 1,
+  ///
+  /// This mode returns the Compatibility16 policy for the device that should be the ISA bus
+  /// controller used during a Compatibility16 boot.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Type 0x00.
+  ///
+  ///   HandleBuffer Buffer of all ISA bus handles found.
+  ///
+  ///   HandleCount Number of ISA bus handles found.
+  ///
+  ///   AdditionalData NULL.
+  ///
+  EfiGetPlatformIsaBusHandle    = 2,
+  ///
+  /// This mode returns the Compatibility16 policy for the device that should be the USB
+  /// device used during a Compatibility16 boot.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///   Type 0x00.
+  ///
+  ///   HandleBuffer Buffer of all USB handles found.
+  ///
+  ///   HandleCount Number of USB bus handles found.
+  ///
+  ///   AdditionalData NULL.
+  ///
+  EfiGetPlatformUsbHandle       = 3
+} EFI_GET_PLATFORM_HANDLE_MODE;
+
+/**
+  This enum specifies the Mode param values for PlatformHooks().
+  Note: Any OEM defined hooks start with 0x8000.
+**/
+typedef enum {
+  ///
+  /// This mode allows any preprocessing before scanning OpROMs.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///     Type 0.
+  ///
+  ///     DeviceHandle Handle of device OpROM is associated with.
+  ///
+  ///     ShadowAddress Address where OpROM is shadowed.
+  ///
+  ///     Compatibility16Table NULL.
+  ///
+  ///     AdditionalData NULL.
+  ///
+  EfiPlatformHookPrepareToScanRom = 0,
+  ///
+  /// This mode shadows legacy OpROMS that may not have a physical device associated with
+  /// them. It returns EFI_SUCCESS if the ROM was shadowed.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///     Type 0.
+  ///
+  ///     DeviceHandle 0.
+  ///
+  ///     ShadowAddress First free OpROM area, after other OpROMs have been dispatched..
+  ///
+  ///     Compatibility16Table Pointer to the Compatability16 Table.
+  ///
+  ///       AdditionalData NULL.
+  ///
+  EfiPlatformHookShadowServiceRoms= 1,
+  ///
+  /// This mode allows platform to perform any required operation after an OpROM has
+  /// completed its initialization.
+  ///
+  /// The function parameters associated with this mode are:
+  ///
+  ///       Type 0.
+  ///
+  ///       DeviceHandle Handle of device OpROM is associated with.
+  ///
+  ///       ShadowAddress Address where OpROM is shadowed.
+  ///
+  ///       Compatibility16Table NULL.
+  ///
+  ///       AdditionalData NULL.
+  ///
+  EfiPlatformHookAfterRomInit     = 2
+} EFI_GET_PLATFORM_HOOK_MODE;
+
+///
+/// This IRQ has not been assigned to PCI.
+///
+#define PCI_UNUSED        0x00
+///
+/// This IRQ has been assigned to PCI.
+///
+#define PCI_USED          0xFF
+///
+/// This IRQ has been used by an SIO legacy device and cannot be used by PCI.
+///
+#define LEGACY_USED       0xFE
+
+#pragma pack(1)
+
+typedef struct {
+  ///
+  /// IRQ for this entry.
+  ///
+  UINT8 Irq;
+  ///
+  /// Status of this IRQ.
+  ///
+  /// PCI_UNUSED 0x00. This IRQ has not been assigned to PCI.
+  ///
+  /// PCI_USED 0xFF. This IRQ has been assigned to PCI.
+  ///
+  /// LEGACY_USED 0xFE. This IRQ has been used by an SIO legacy
+  /// device and cannot be used by PCI.
+  ///
+  UINT8 Used;
+} EFI_LEGACY_IRQ_PRIORITY_TABLE_ENTRY;
+
+//
+// Define PIR table structures
+//
+#define EFI_LEGACY_PIRQ_TABLE_SIGNATURE SIGNATURE_32 ('$', 'P', 'I', 'R')
+
+typedef struct {
+  ///
+  /// $PIR.
+  ///
+  UINT32  Signature;
+  ///
+  /// 0x00.
+  ///
+  UINT8   MinorVersion;
+  ///
+  /// 0x01 for table version 1.0.
+  ///
+  UINT8   MajorVersion;
+  ///
+  /// 0x20 + RoutingTableEntries * 0x10.
+  ///
+  UINT16  TableSize;
+  ///
+  /// PCI interrupt router bus.
+  ///
+  UINT8   Bus;
+  ///
+  /// PCI interrupt router device/function.
+  ///
+  UINT8   DevFun;
+  ///
+  /// If nonzero, bit map of IRQs reserved for PCI.
+  ///
+  UINT16  PciOnlyIrq;
+  ///
+  /// Vendor ID of a compatible PCI interrupt router.
+  ///
+  UINT16  CompatibleVid;
+  ///
+  /// Device ID of a compatible PCI interrupt router.
+  ///
+  UINT16  CompatibleDid;
+  ///
+  /// If nonzero, a value passed directly to the IRQ miniport's Initialize function.
+  ///
+  UINT32  Miniport;
+  ///
+  /// Reserved for future usage.
+  ///
+  UINT8   Reserved[11];
+  ///
+  /// This byte plus the sum of all other bytes in the LocalPirqTable equal 0x00.
+  ///
+  UINT8   Checksum;
+} EFI_LEGACY_PIRQ_TABLE_HEADER;
+
+
+typedef struct {
+  ///
+  /// If nonzero, a value assigned by the IBV.
+  ///
+  UINT8   Pirq;
+  ///
+  /// If nonzero, the IRQs that can be assigned to this device.
+  ///
+  UINT16  IrqMask;
+} EFI_LEGACY_PIRQ_ENTRY;
+
+typedef struct {
+  ///
+  /// PCI bus of the entry.
+  ///
+  UINT8                 Bus;
+  ///
+  /// PCI device of this entry.
+  ///
+  UINT8                 Device;
+  ///
+  /// An IBV value and IRQ mask for PIRQ pins A through D.
+  ///
+  EFI_LEGACY_PIRQ_ENTRY PirqEntry[4];
+  ///
+  /// If nonzero, the slot number assigned by the board manufacturer.
+  ///
+  UINT8                 Slot;
+  ///
+  /// Reserved for future use.
+  ///
+  UINT8                 Reserved;
+} EFI_LEGACY_IRQ_ROUTING_ENTRY;
+
+#pragma pack()
+
+
+/**
+  Finds the binary data or other platform information.
+
+  @param  This                  The protocol instance pointer.
+  @param  Mode                  Specifies what data to return. See See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  Table                 Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  TableSize              Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  Location               Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  Alignment             Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  LegacySegment         Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+  @param  LegacyOffset          Mode specific.  See EFI_GET_PLATFORM_INFO_MODE enum.
+
+  @retval EFI_SUCCESS           Data returned successfully.
+  @retval EFI_UNSUPPORTED       Mode is not supported on the platform.
+  @retval EFI_NOT_FOUND         Binary image or table not found.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_GET_PLATFORM_INFO)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN EFI_GET_PLATFORM_INFO_MODE          Mode,
+  OUT VOID                               **Table,
+  OUT UINTN                              *TableSize,
+  OUT UINTN                              *Location,
+  OUT UINTN                              *Alignment,
+  IN  UINT16                             LegacySegment,
+  IN  UINT16                             LegacyOffset
+  );
+
+/**
+  Returns a buffer of handles for the requested subfunction.
+
+  @param  This                  The protocol instance pointer.
+  @param  Mode                  Specifies what handle to return. See EFI_GET_PLATFORM_HANDLE_MODE enum.
+  @param  Type                  Mode specific. See EFI_GET_PLATFORM_HANDLE_MODE enum.
+  @param  HandleBuffer          Mode specific. See EFI_GET_PLATFORM_HANDLE_MODE enum.
+  @param  HandleCount           Mode specific. See EFI_GET_PLATFORM_HANDLE_MODE enum.
+  @param  AdditionalData        Mode specific. See EFI_GET_PLATFORM_HANDLE_MODE enum.
+
+  @retval EFI_SUCCESS           Handle is valid.
+  @retval EFI_UNSUPPORTED       Mode is not supported on the platform.
+  @retval EFI_NOT_FOUND         Handle is not known.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_GET_PLATFORM_HANDLE)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN EFI_GET_PLATFORM_HANDLE_MODE        Mode,
+  IN UINT16                              Type,
+  OUT EFI_HANDLE                         **HandleBuffer,
+  OUT UINTN                              *HandleCount,
+  IN  VOID                               **AdditionalData OPTIONAL
+  );
+
+/**
+  Load and initialize the Legacy BIOS SMM handler.
+
+  @param  This                   The protocol instance pointer.
+  @param  EfiToLegacy16BootTable A pointer to Legacy16 boot table.
+
+  @retval EFI_SUCCESS           SMM code loaded.
+  @retval EFI_DEVICE_ERROR      SMM code failed to load
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_SMM_INIT)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN  VOID                               *EfiToLegacy16BootTable
+  );
+
+/**
+  Allows platform to perform any required action after a LegacyBios operation.
+  Invokes the specific sub function specified by Mode.
+
+  @param  This                  The protocol instance pointer.
+  @param  Mode                  Specifies what handle to return. See EFI_GET_PLATFORM_HOOK_MODE enum.
+  @param  Type                  Mode specific.  See EFI_GET_PLATFORM_HOOK_MODE enum.
+  @param  DeviceHandle          Mode specific.  See EFI_GET_PLATFORM_HOOK_MODE enum.
+  @param  ShadowAddress         Mode specific.  See EFI_GET_PLATFORM_HOOK_MODE enum.
+  @param  Compatibility16Table  Mode specific.  See EFI_GET_PLATFORM_HOOK_MODE enum.
+  @param  AdditionalData        Mode specific.  See EFI_GET_PLATFORM_HOOK_MODE enum.
+
+  @retval EFI_SUCCESS           The operation performed successfully. Mode specific.
+  @retval EFI_UNSUPPORTED       Mode is not supported on the platform.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_HOOKS)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN EFI_GET_PLATFORM_HOOK_MODE          Mode,
+  IN UINT16                              Type,
+  IN  EFI_HANDLE                         DeviceHandle, OPTIONAL
+  IN  OUT UINTN                          *ShadowAddress, OPTIONAL
+  IN  EFI_COMPATIBILITY16_TABLE          *Compatibility16Table, OPTIONAL
+  OUT  VOID                               **AdditionalData OPTIONAL
+  );
+
+/**
+  Returns information associated with PCI IRQ routing.
+  This function returns the following information associated with PCI IRQ routing:
+    * An IRQ routing table and number of entries in the table.
+    * The $PIR table and its size.
+    * A list of PCI IRQs and the priority order to assign them.
+
+  @param  This                    The protocol instance pointer.
+  @param  RoutingTable            The pointer to PCI IRQ Routing table.
+                                  This location is the $PIR table minus the header.
+  @param  RoutingTableEntries     The number of entries in table.
+  @param  LocalPirqTable          $PIR table.
+  @param  PirqTableSize           $PIR table size.
+  @param  LocalIrqPriorityTable   A list of interrupts in priority order to assign.
+  @param  IrqPriorityTableEntries The number of entries in the priority table.
+
+  @retval EFI_SUCCESS           Data was successfully returned.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_GET_ROUTING_TABLE)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  OUT VOID                               **RoutingTable,
+  OUT UINTN                              *RoutingTableEntries,
+  OUT VOID                               **LocalPirqTable, OPTIONAL
+  OUT UINTN                              *PirqTableSize, OPTIONAL
+  OUT VOID                               **LocalIrqPriorityTable, OPTIONAL
+  OUT UINTN                              *IrqPriorityTableEntries OPTIONAL
+  );
+
+/**
+  Translates the given PIRQ accounting for bridge.
+  This function translates the given PIRQ back through all buses, if required,
+  and returns the true PIRQ and associated IRQ.
+
+  @param  This                  The protocol instance pointer.
+  @param  PciBus                The PCI bus number for this device.
+  @param  PciDevice             The PCI device number for this device.
+  @param  PciFunction           The PCI function number for this device.
+  @param  Pirq                  Input is PIRQ reported by device, and output is true PIRQ.
+  @param  PciIrq                The IRQ already assigned to the PIRQ, or the IRQ to be
+                                assigned to the PIRQ.
+
+  @retval EFI_SUCCESS           The PIRQ was translated.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_TRANSLATE_PIRQ)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN  UINTN                              PciBus,
+  IN  UINTN                              PciDevice,
+  IN  UINTN                              PciFunction,
+  IN  OUT UINT8                          *Pirq,
+  OUT UINT8                              *PciIrq
+  );
+
+/**
+  Attempt to legacy boot the BootOption. If the EFI contexted has been
+  compromised this function will not return.
+
+  @param  This                   The protocol instance pointer.
+  @param  BbsDevicePath          The EFI Device Path from BootXXXX variable.
+  @param  BbsTable               The Internal BBS table.
+  @param  LoadOptionSize         The size of LoadOption in size.
+  @param  LoadOption             The LoadOption from BootXXXX variable
+  @param  EfiToLegacy16BootTable A pointer to BootTable structure
+
+  @retval EFI_SUCCESS           Ready to boot.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_BIOS_PLATFORM_PREPARE_TO_BOOT)(
+  IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL   *This,
+  IN  BBS_BBS_DEVICE_PATH                *BbsDevicePath,
+  IN  VOID                               *BbsTable,
+  IN  UINT32                             LoadOptionsSize,
+  IN  VOID                               *LoadOptions,
+  IN  VOID                               *EfiToLegacy16BootTable
+  );
+
+/**
+  This protocol abstracts the platform portion of the traditional BIOS.
+**/
+struct _EFI_LEGACY_BIOS_PLATFORM_PROTOCOL {
+  ///
+  ///  Gets binary data or other platform information.
+  ///
+  EFI_LEGACY_BIOS_PLATFORM_GET_PLATFORM_INFO    GetPlatformInfo;
+  ///
+  ///  Returns a buffer of all handles matching the requested subfunction.
+  ///
+  EFI_LEGACY_BIOS_PLATFORM_GET_PLATFORM_HANDLE  GetPlatformHandle;
+  ///
+  ///  Loads and initializes the traditional BIOS SMM handler.
+  EFI_LEGACY_BIOS_PLATFORM_SMM_INIT             SmmInit;
+  ///
+  ///  Allows platform to perform any required actions after a LegacyBios operation.
+  ///
+  EFI_LEGACY_BIOS_PLATFORM_HOOKS                PlatformHooks;
+  ///
+  ///  Gets $PIR table.
+  EFI_LEGACY_BIOS_PLATFORM_GET_ROUTING_TABLE    GetRoutingTable;
+  ///
+  ///  Translates the given PIRQ to the final value after traversing any PCI bridges.
+  ///
+  EFI_LEGACY_BIOS_PLATFORM_TRANSLATE_PIRQ       TranslatePirq;
+  ///
+  ///  Final platform function before the system attempts to boot to a traditional OS.
+  ///
+  EFI_LEGACY_BIOS_PLATFORM_PREPARE_TO_BOOT      PrepareToBoot;
+};
+
+extern EFI_GUID gEfiLegacyBiosPlatformProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/LegacyInterrupt.h b/OvmfPkg/Csm/Include/Protocol/LegacyInterrupt.h
new file mode 100644
index 0000000000..b3ad2ffb3c
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/LegacyInterrupt.h
@@ -0,0 +1,122 @@
+/** @file
+  This protocol abstracts the PIRQ programming from the generic EFI Compatibility Support Modules (CSMs).
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Revision Reference:
+  This protocol is defined in Framework for the EFI Compatibility Support Module specification.
+  Version 0.97.
+
+**/
+
+#ifndef _EFI_LEGACY_INTERRUPT_H_
+#define _EFI_LEGACY_INTERRUPT_H_
+
+
+#define EFI_LEGACY_INTERRUPT_PROTOCOL_GUID \
+  { \
+    0x31ce593d, 0x108a, 0x485d, {0xad, 0xb2, 0x78, 0xf2, 0x1f, 0x29, 0x66, 0xbe } \
+  }
+
+typedef struct _EFI_LEGACY_INTERRUPT_PROTOCOL EFI_LEGACY_INTERRUPT_PROTOCOL;
+
+/**
+  Get the number of PIRQs this hardware supports.
+
+  @param  This                  The protocol instance pointer.
+  @param  NumberPirsq           The number of PIRQs that are supported.
+
+  @retval EFI_SUCCESS           The number of PIRQs was returned successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_INTERRUPT_GET_NUMBER_PIRQS)(
+  IN EFI_LEGACY_INTERRUPT_PROTOCOL            *This,
+  OUT UINT8                                   *NumberPirqs
+  );
+
+/**
+  Gets the PCI location associated with this protocol.
+
+  @param  This                  The Protocol instance pointer.
+  @param  Bus                   The PCI Bus.
+  @param  Device                The PCI Device.
+  @param  Function              The PCI Function.
+
+  @retval EFI_SUCCESS           The Bus, Device, and Function were returned successfully.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_INTERRUPT_GET_LOCATION)(
+  IN EFI_LEGACY_INTERRUPT_PROTOCOL            *This,
+  OUT UINT8                                   *Bus,
+  OUT UINT8                                   *Device,
+  OUT UINT8                                   *Function
+  );
+
+/**
+  Read the PIRQ register and return the data
+
+  @param  This                  The protocol instance pointer.
+  @param  PirqNumber            The PIRQ register to read.
+  @param  PirqData              The data read.
+
+  @retval EFI_SUCCESS           The data was read.
+  @retval EFI_INVALID_PARAMETER Invalid PIRQ number.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_INTERRUPT_READ_PIRQ)(
+  IN EFI_LEGACY_INTERRUPT_PROTOCOL           *This,
+  IN  UINT8                                  PirqNumber,
+  OUT UINT8                                  *PirqData
+  );
+
+/**
+  Write the specified PIRQ register with the given data.
+
+  @param  This                  The protocol instance pointer.
+  @param  PirqNumber            A PIRQ register to read.
+  @param  PirqData              The data to write.
+
+  @retval EFI_SUCCESS           The PIRQ was programmed.
+  @retval EFI_INVALID_PARAMETER Invalid PIRQ number.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_LEGACY_INTERRUPT_WRITE_PIRQ)(
+  IN EFI_LEGACY_INTERRUPT_PROTOCOL           *This,
+  IN  UINT8                                  PirqNumber,
+  IN UINT8                                   PirqData
+  );
+
+struct _EFI_LEGACY_INTERRUPT_PROTOCOL {
+  ///
+  ///   Gets the number of PIRQs supported.
+  ///
+  EFI_LEGACY_INTERRUPT_GET_NUMBER_PIRQS GetNumberPirqs;
+
+  ///
+  /// Gets the PCI bus, device, and function that is associated with this protocol.
+  ///
+  EFI_LEGACY_INTERRUPT_GET_LOCATION     GetLocation;
+
+  ///
+  /// Reads the indicated PIRQ register.
+  ///
+  EFI_LEGACY_INTERRUPT_READ_PIRQ        ReadPirq;
+
+  ///
+  /// Writes to the indicated PIRQ register.
+  ///
+  EFI_LEGACY_INTERRUPT_WRITE_PIRQ       WritePirq;
+};
+
+extern EFI_GUID gEfiLegacyInterruptProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/Include/Protocol/VgaMiniPort.h b/OvmfPkg/Csm/Include/Protocol/VgaMiniPort.h
new file mode 100644
index 0000000000..41ff58e14c
--- /dev/null
+++ b/OvmfPkg/Csm/Include/Protocol/VgaMiniPort.h
@@ -0,0 +1,88 @@
+/** @file
+  The VGA Mini Port Protocol used to set the text display mode of a VGA controller.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __VGA_MINI_PORT_H_
+#define __VGA_MINI_PORT_H_
+
+///
+/// Global ID for the EFI_VGA_MINI_PORT_PROTOCOL.
+///
+#define EFI_VGA_MINI_PORT_PROTOCOL_GUID \
+  { \
+    0xc7735a2f, 0x88f5, 0x4882, {0xae, 0x63, 0xfa, 0xac, 0x8c, 0x8b, 0x86, 0xb3 } \
+  }
+
+///
+/// Forward declaration for the EFI_VGA_MINI_PORT_PROTOCOL.
+///
+typedef struct _EFI_VGA_MINI_PORT_PROTOCOL  EFI_VGA_MINI_PORT_PROTOCOL;
+
+/**
+  Sets the text display mode of a VGA controller.
+
+  Sets the text display mode of the VGA controller to the mode specified by
+  ModeNumber.  A ModeNumber of 0 is a request for an 80x25 text mode.  A
+  ModeNumber of 1 is a request for an 80x50 text mode.  If ModeNumber is greater
+  than MaxModeNumber, then EFI_UNSUPPORTED is returned.  If the VGA controller
+  is not functioning properly, then EFI_DEVICE_ERROR is returned.  If the VGA
+  controller is sucessfully set to the mode number specified by ModeNumber, then
+  EFI_SUCCESS is returned.
+
+  @param[in] This         A pointer to the EFI_VGA_MINI_PORT_PROTOCOL instance.
+  @param[in] ModeNumber   The requested mode number.  0 for 80x25.  1 for 80x5.
+
+  @retval EFI_SUCCESS        The mode number was set.
+  @retval EFI_UNSUPPORTED    The mode number specified by ModeNumber is not supported.
+  @retval EFI_DEVICE_ERROR   The device is not functioning properly.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_VGA_MINI_PORT_SET_MODE)(
+  IN EFI_VGA_MINI_PORT_PROTOCOL  *This,
+  IN UINTN                        ModeNumber
+  );
+
+struct _EFI_VGA_MINI_PORT_PROTOCOL {
+  EFI_VGA_MINI_PORT_SET_MODE  SetMode;
+  ///
+  /// MMIO base address of the VGA text mode framebuffer.  Typically set to 0xB8000.
+  ///
+  UINT64                      VgaMemoryOffset;
+  ///
+  /// I/O Port address for the VGA CRTC address register. Typically set to 0x3D4.
+  ///
+  UINT64                      CrtcAddressRegisterOffset;
+  ///
+  /// I/O Port address for the VGA CRTC data register.  Typically set to 0x3D5.
+  ///
+  UINT64                      CrtcDataRegisterOffset;
+  ///
+  /// PCI Controller MMIO BAR index of the VGA text mode frame buffer.  Typically
+  /// set to EFI_PCI_IO_PASS_THROUGH_BAR
+  ///
+  UINT8                       VgaMemoryBar;
+  ///
+  /// PCI Controller I/O BAR index of the VGA CRTC address register.  Typically
+  /// set to EFI_PCI_IO_PASS_THROUGH_BAR
+  ///
+  UINT8                       CrtcAddressRegisterBar;
+  ///
+  /// PCI Controller I/O BAR index of the VGA CRTC data register.  Typically set
+  /// to EFI_PCI_IO_PASS_THROUGH_BAR
+  ///
+  UINT8                       CrtcDataRegisterBar;
+  ///
+  /// The maximum number of text modes that this VGA controller supports.
+  ///
+  UINT8                       MaxMode;
+};
+
+extern EFI_GUID gEfiVgaMiniPortProtocolGuid;
+
+#endif
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h
new file mode 100644
index 0000000000..a72c8470f6
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h
@@ -0,0 +1,1460 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _LEGACY_BIOS_INTERFACE_
+#define _LEGACY_BIOS_INTERFACE_
+
+
+#include <FrameworkDxe.h>
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/Acpi10.h>
+
+#include <Guid/SmBios.h>
+#include <Guid/Acpi.h>
+#include <Guid/DxeServices.h>
+#include <Guid/LegacyBios.h>
+#include <Guid/StatusCodeDataTypeId.h>
+#include <Guid/ImageAuthentication.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/Cpu.h>
+#include <Protocol/Timer.h>
+#include <Protocol/IsaIo.h>
+#include <Protocol/LegacyRegion2.h>
+#include <Protocol/SimpleTextIn.h>
+#include <Protocol/LegacyInterrupt.h>
+#include <Protocol/LegacyBios.h>
+#include <Protocol/DiskInfo.h>
+#include <Protocol/GenericMemoryTest.h>
+#include <Protocol/LegacyBiosPlatform.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/Legacy8259.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/SerialIo.h>
+#include <Protocol/SuperIo.h>
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DebugAgentLib.h>
+
+//
+// BUGBUG: This entry maybe changed to PCD in future and wait for
+//         redesign of BDS library
+//
+#define MAX_BBS_ENTRIES 0x100
+
+//
+// Thunk Status Codes
+//   (These apply only to errors with the thunk and not to the code that was
+//   thunked to.)
+//
+#define THUNK_OK              0x00
+#define THUNK_ERR_A20_UNSUP   0x01
+#define THUNK_ERR_A20_FAILED  0x02
+
+//
+// Vector base definitions
+//
+//
+// 8259 Hardware definitions
+//
+#define LEGACY_MODE_BASE_VECTOR_MASTER     0x08
+#define LEGACY_MODE_BASE_VECTOR_SLAVE      0x70
+
+//
+// The original PC used INT8-F for master PIC. Since these mapped over
+// processor exceptions TIANO moved the master PIC to INT68-6F.
+//
+// The vector base for slave PIC is set as 0x70 for PC-AT compatibility.
+//
+#define PROTECTED_MODE_BASE_VECTOR_MASTER  0x68
+#define PROTECTED_MODE_BASE_VECTOR_SLAVE   0x70
+
+//
+// When we call CSM16 functions, some CSM16 use es:[offset + 0xabcd] to get data passed from CSM32,
+// offset + 0xabcd could overflow which exceeds 0xFFFF which is invalid in real mode.
+// So this will keep offset as small as possible to avoid offset overflow in real mode.
+//
+#define NORMALIZE_EFI_SEGMENT(_Adr)      (UINT16) (((UINTN) (_Adr)) >> 4)
+#define NORMALIZE_EFI_OFFSET(_Adr)       (UINT16) (((UINT16) ((UINTN) (_Adr))) & 0xf)
+
+//
+// Trace defines
+//
+//
+#define LEGACY_BDA_TRACE    0x000
+#define LEGACY_BIOS_TRACE   0x040
+#define LEGACY_BOOT_TRACE   0x080
+#define LEGACY_CMOS_TRACE   0x0C0
+#define LEGACY_IDE_TRACE    0x100
+#define LEGACY_MP_TRACE     0x140
+#define LEGACY_PCI_TRACE    0x180
+#define LEGACY_SIO_TRACE    0x1C0
+
+#define LEGACY_PCI_TRACE_000 LEGACY_PCI_TRACE + 0x00
+#define LEGACY_PCI_TRACE_001 LEGACY_PCI_TRACE + 0x01
+#define LEGACY_PCI_TRACE_002 LEGACY_PCI_TRACE + 0x02
+#define LEGACY_PCI_TRACE_003 LEGACY_PCI_TRACE + 0x03
+#define LEGACY_PCI_TRACE_004 LEGACY_PCI_TRACE + 0x04
+#define LEGACY_PCI_TRACE_005 LEGACY_PCI_TRACE + 0x05
+#define LEGACY_PCI_TRACE_006 LEGACY_PCI_TRACE + 0x06
+#define LEGACY_PCI_TRACE_007 LEGACY_PCI_TRACE + 0x07
+#define LEGACY_PCI_TRACE_008 LEGACY_PCI_TRACE + 0x08
+#define LEGACY_PCI_TRACE_009 LEGACY_PCI_TRACE + 0x09
+#define LEGACY_PCI_TRACE_00A LEGACY_PCI_TRACE + 0x0A
+#define LEGACY_PCI_TRACE_00B LEGACY_PCI_TRACE + 0x0B
+#define LEGACY_PCI_TRACE_00C LEGACY_PCI_TRACE + 0x0C
+#define LEGACY_PCI_TRACE_00D LEGACY_PCI_TRACE + 0x0D
+#define LEGACY_PCI_TRACE_00E LEGACY_PCI_TRACE + 0x0E
+#define LEGACY_PCI_TRACE_00F LEGACY_PCI_TRACE + 0x0F
+
+#define BDA_VIDEO_MODE      0x49
+
+#define IDE_PI_REGISTER_PNE     BIT0
+#define IDE_PI_REGISTER_SNE     BIT2
+
+typedef struct {
+  UINTN   PciSegment;
+  UINTN   PciBus;
+  UINTN   PciDevice;
+  UINTN   PciFunction;
+  UINT32  ShadowAddress;
+  UINT32  ShadowedSize;
+  UINT8   DiskStart;
+  UINT8   DiskEnd;
+} ROM_INSTANCE_ENTRY;
+
+//
+// Values for RealModeGdt
+//
+#if defined (MDE_CPU_IA32)
+
+#define NUM_REAL_GDT_ENTRIES  3
+#define CONVENTIONAL_MEMORY_TOP 0xA0000   // 640 KB
+#define INITIAL_VALUE_BELOW_1K  0x0
+
+#elif defined (MDE_CPU_X64)
+
+#define NUM_REAL_GDT_ENTRIES  8
+#define CONVENTIONAL_MEMORY_TOP 0xA0000   // 640 KB
+#define INITIAL_VALUE_BELOW_1K  0x0
+
+#endif
+
+#pragma pack(1)
+
+//
+// Define what a processor GDT looks like
+//
+typedef struct {
+  UINT32  LimitLo : 16;
+  UINT32  BaseLo : 16;
+  UINT32  BaseMid : 8;
+  UINT32  Type : 4;
+  UINT32  System : 1;
+  UINT32  Dpl : 2;
+  UINT32  Present : 1;
+  UINT32  LimitHi : 4;
+  UINT32  Software : 1;
+  UINT32  Reserved : 1;
+  UINT32  DefaultSize : 1;
+  UINT32  Granularity : 1;
+  UINT32  BaseHi : 8;
+} GDT32;
+
+typedef struct {
+  UINT16  LimitLow;
+  UINT16  BaseLow;
+  UINT8   BaseMid;
+  UINT8   Attribute;
+  UINT8   LimitHi;
+  UINT8   BaseHi;
+} GDT64;
+
+//
+// Define what a processor descriptor looks like
+// This data structure must be kept in sync with ASM STRUCT in Thunk.inc
+//
+typedef struct {
+  UINT16  Limit;
+  UINT64  Base;
+} DESCRIPTOR64;
+
+typedef struct {
+  UINT16  Limit;
+  UINT32  Base;
+} DESCRIPTOR32;
+
+//
+// Low stub lay out
+//
+#define LOW_STACK_SIZE      (8 * 1024)  // 8k?
+#define EFI_MAX_E820_ENTRY  100
+#define FIRST_INSTANCE      1
+#define NOT_FIRST_INSTANCE  0
+
+#if defined (MDE_CPU_IA32)
+typedef struct {
+  //
+  // Space for the code
+  //  The address of Code is also the beginning of the relocated Thunk code
+  //
+  CHAR8                             Code[4096]; // ?
+  //
+  // The address of the Reverse Thunk code
+  //  Note that this member CONTAINS the address of the relocated reverse thunk
+  //  code unlike the member variable 'Code', which IS the address of the Thunk
+  //  code.
+  //
+  UINT32                            LowReverseThunkStart;
+
+  //
+  // Data for the code (cs releative)
+  //
+  DESCRIPTOR32                      GdtDesc;          // Protected mode GDT
+  DESCRIPTOR32                      IdtDesc;          // Protected mode IDT
+  UINT32                            FlatSs;
+  UINT32                            FlatEsp;
+
+  UINT32                            LowCodeSelector;  // Low code selector in GDT
+  UINT32                            LowDataSelector;  // Low data selector in GDT
+  UINT32                            LowStack;
+  DESCRIPTOR32                      RealModeIdtDesc;
+
+  //
+  // real-mode GDT (temporary GDT with two real mode segment descriptors)
+  //
+  GDT32                             RealModeGdt[NUM_REAL_GDT_ENTRIES];
+  DESCRIPTOR32                      RealModeGdtDesc;
+
+  //
+  // Members specifically for the reverse thunk
+  //  The RevReal* members are used to store the current state of real mode
+  //  before performing the reverse thunk.  The RevFlat* members must be set
+  //  before calling the reverse thunk assembly code.
+  //
+  UINT16                            RevRealDs;
+  UINT16                            RevRealSs;
+  UINT32                            RevRealEsp;
+  DESCRIPTOR32                      RevRealIdtDesc;
+  UINT16                            RevFlatDataSelector;  // Flat data selector in GDT
+  UINT32                            RevFlatStack;
+
+  //
+  // A low memory stack
+  //
+  CHAR8                             Stack[LOW_STACK_SIZE];
+
+  //
+  // Stack for flat mode after reverse thunk
+  // @bug    - This may no longer be necessary if the reverse thunk interface
+  //           is changed to have the flat stack in a different location.
+  //
+  CHAR8                             RevThunkStack[LOW_STACK_SIZE];
+
+  //
+  // Legacy16 Init memory map info
+  //
+  EFI_TO_COMPATIBILITY16_INIT_TABLE EfiToLegacy16InitTable;
+
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE EfiToLegacy16BootTable;
+
+  CHAR8                             InterruptRedirectionCode[32];
+  EFI_LEGACY_INSTALL_PCI_HANDLER    PciHandler;
+  EFI_DISPATCH_OPROM_TABLE          DispatchOpromTable;
+  BBS_TABLE                         BbsTable[MAX_BBS_ENTRIES];
+} LOW_MEMORY_THUNK;
+
+#elif defined (MDE_CPU_X64)
+
+typedef struct {
+  //
+  // Space for the code
+  //  The address of Code is also the beginning of the relocated Thunk code
+  //
+  CHAR8                             Code[4096]; // ?
+
+  //
+  // Data for the code (cs releative)
+  //
+  DESCRIPTOR64                      X64GdtDesc;          // Protected mode GDT
+  DESCRIPTOR64                      X64IdtDesc;          // Protected mode IDT
+  UINTN                             X64Ss;
+  UINTN                             X64Esp;
+
+  UINTN                             RealStack;
+  DESCRIPTOR32                      RealModeIdtDesc;
+  DESCRIPTOR32                      RealModeGdtDesc;
+
+  //
+  // real-mode GDT (temporary GDT with two real mode segment descriptors)
+  //
+  GDT64                             RealModeGdt[NUM_REAL_GDT_ENTRIES];
+  UINT64                            PageMapLevel4;
+
+  //
+  // A low memory stack
+  //
+  CHAR8                             Stack[LOW_STACK_SIZE];
+
+  //
+  // Legacy16 Init memory map info
+  //
+  EFI_TO_COMPATIBILITY16_INIT_TABLE EfiToLegacy16InitTable;
+
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE EfiToLegacy16BootTable;
+
+  CHAR8                             InterruptRedirectionCode[32];
+  EFI_LEGACY_INSTALL_PCI_HANDLER    PciHandler;
+  EFI_DISPATCH_OPROM_TABLE          DispatchOpromTable;
+  BBS_TABLE                         BbsTable[MAX_BBS_ENTRIES];
+} LOW_MEMORY_THUNK;
+
+#endif
+
+//
+// PnP Expansion Header
+//
+typedef struct {
+  UINT32  PnpSignature;
+  UINT8   Revision;
+  UINT8   Length;
+  UINT16  NextHeader;
+  UINT8   Reserved1;
+  UINT8   Checksum;
+  UINT32  DeviceId;
+  UINT16  MfgPointer;
+  UINT16  ProductNamePointer;
+  UINT8   Class;
+  UINT8   SubClass;
+  UINT8   Interface;
+  UINT8   DeviceIndicators;
+  UINT16  Bcv;
+  UINT16  DisconnectVector;
+  UINT16  Bev;
+  UINT16  Reserved2;
+  UINT16  StaticResourceVector;
+} LEGACY_PNP_EXPANSION_HEADER;
+
+typedef struct {
+  UINT8   PciSegment;
+  UINT8   PciBus;
+  UINT8   PciDevice;
+  UINT8   PciFunction;
+  UINT16  Vid;
+  UINT16  Did;
+  UINT16  SysSid;
+  UINT16  SVid;
+  UINT8   Class;
+  UINT8   SubClass;
+  UINT8   Interface;
+  UINT8   Reserved;
+  UINTN   RomStart;
+  UINTN   ManufacturerString;
+  UINTN   ProductNameString;
+} LEGACY_ROM_AND_BBS_TABLE;
+
+//
+// Structure how EFI has mapped a devices HDD drive numbers.
+// Boot to EFI aware OS or shell requires this mapping when
+// 16-bit CSM assigns drive numbers.
+// This mapping is ignored booting to a legacy OS.
+//
+typedef struct {
+  UINT8 PciSegment;
+  UINT8 PciBus;
+  UINT8 PciDevice;
+  UINT8 PciFunction;
+  UINT8 StartDriveNumber;
+  UINT8 EndDriveNumber;
+} LEGACY_EFI_HDD_TABLE;
+
+//
+// This data is passed to Leacy16Boot
+//
+typedef enum {
+  EfiAcpiAddressRangeMemory   = 1,
+  EfiAcpiAddressRangeReserved = 2,
+  EfiAcpiAddressRangeACPI     = 3,
+  EfiAcpiAddressRangeNVS      = 4,
+  EfiAddressRangePersistentMemory = 7
+} EFI_ACPI_MEMORY_TYPE;
+
+typedef struct {
+  UINT64                BaseAddr;
+  UINT64                Length;
+  EFI_ACPI_MEMORY_TYPE  Type;
+} EFI_E820_ENTRY64;
+
+typedef struct {
+  UINT32                BassAddrLow;
+  UINT32                BaseAddrHigh;
+  UINT32                LengthLow;
+  UINT32                LengthHigh;
+  EFI_ACPI_MEMORY_TYPE  Type;
+} EFI_E820_ENTRY;
+
+#pragma pack()
+
+extern BBS_TABLE           *mBbsTable;
+
+extern EFI_GENERIC_MEMORY_TEST_PROTOCOL *gGenMemoryTest;
+
+extern BOOLEAN mEndOfDxe;
+
+#define PORT_70 0x70
+#define PORT_71 0x71
+
+#define CMOS_0A     0x0a  ///< Status register A
+#define CMOS_0D     0x0d  ///< Status register D
+#define CMOS_0E     0x0e  ///< Diagnostic Status
+#define CMOS_0F     0x0f  ///< Shutdown status
+#define CMOS_10     0x10  ///< Floppy type
+#define CMOS_12     0x12  ///< IDE type
+#define CMOS_14     0x14  ///< Same as BDA 40:10
+#define CMOS_15     0x15  ///< Low byte of base memory in 1k increments
+#define CMOS_16     0x16  ///< High byte of base memory in 1k increments
+#define CMOS_17     0x17  ///< Low byte of 1MB+ memory in 1k increments - max 15 MB
+#define CMOS_18     0x18  ///< High byte of 1MB+ memory in 1k increments - max 15 MB
+#define CMOS_19     0x19  ///< C: extended drive type
+#define CMOS_1A     0x1a  ///< D: extended drive type
+#define CMOS_2E     0x2e  ///< Most significient byte of standard checksum
+#define CMOS_2F     0x2f  ///< Least significient byte of standard checksum
+#define CMOS_30     0x30  ///< CMOS 0x17
+#define CMOS_31     0x31  ///< CMOS 0x18
+#define CMOS_32     0x32  ///< Century byte
+
+//
+// 8254 Timer registers
+//
+#define TIMER0_COUNT_PORT                         0x40
+#define TIMER1_COUNT_PORT                         0x41
+#define TIMER2_COUNT_PORT                         0x42
+#define TIMER_CONTROL_PORT                        0x43
+
+//
+// Timer 0, Read/Write LSB then MSB, Square wave output, binary count use.
+//
+#define TIMER0_CONTROL_WORD         0x36
+
+#define LEGACY_BIOS_INSTANCE_SIGNATURE  SIGNATURE_32 ('L', 'B', 'I', 'T')
+typedef struct {
+  UINTN                             Signature;
+
+  EFI_HANDLE                        Handle;
+  EFI_LEGACY_BIOS_PROTOCOL          LegacyBios;
+
+  EFI_HANDLE                        ImageHandle;
+
+  //
+  // CPU Architectural Protocol
+  //
+  EFI_CPU_ARCH_PROTOCOL             *Cpu;
+
+  //
+  // Timer Architectural Protocol
+  //
+  EFI_TIMER_ARCH_PROTOCOL           *Timer;
+  BOOLEAN                           TimerUses8254;
+
+  //
+  // Protocol to Lock and Unlock 0xc0000 - 0xfffff
+  //
+  EFI_LEGACY_REGION2_PROTOCOL       *LegacyRegion;
+
+  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
+
+  //
+  // Interrupt control for thunk and PCI IRQ
+  //
+  EFI_LEGACY_8259_PROTOCOL          *Legacy8259;
+
+  //
+  // PCI Interrupt PIRQ control
+  //
+  EFI_LEGACY_INTERRUPT_PROTOCOL     *LegacyInterrupt;
+
+  //
+  // Generic Memory Test
+  //
+  EFI_GENERIC_MEMORY_TEST_PROTOCOL  *GenericMemoryTest;
+
+  //
+  // TRUE if PCI Interupt Line registers have been programmed.
+  //
+  BOOLEAN                           PciInterruptLine;
+
+  //
+  // Code space below 1MB needed by thunker to transition to real mode.
+  // Contains stack and real mode code fragments
+  //
+  LOW_MEMORY_THUNK                  *IntThunk;
+
+  //
+  // Starting shadow address of the Legacy BIOS
+  //
+  UINT32                            BiosStart;
+  UINT32                            LegacyBiosImageSize;
+
+  //
+  // Start of variables used by CsmItp.mac ITP macro file and/os LegacyBios
+  //
+  UINT8                             Dump[4];
+
+  //
+  // $EFI Legacy16 code entry info in memory < 1 MB;
+  //
+  EFI_COMPATIBILITY16_TABLE         *Legacy16Table;
+  VOID                              *Legacy16InitPtr;
+  VOID                              *Legacy16BootPtr;
+  VOID                              *InternalIrqRoutingTable;
+  UINT32                            NumberIrqRoutingEntries;
+  VOID                              *BbsTablePtr;
+  VOID                              *HddTablePtr;
+  UINT32                            NumberHddControllers;
+
+  //
+  // Cached copy of Legacy16 entry point
+  //
+  UINT16                            Legacy16CallSegment;
+  UINT16                            Legacy16CallOffset;
+
+  //
+  // Returned from $EFI and passed in to OPROMS
+  //
+  UINT16                            PnPInstallationCheckSegment;
+  UINT16                            PnPInstallationCheckOffset;
+
+  //
+  // E820 table
+  //
+  EFI_E820_ENTRY                    E820Table[EFI_MAX_E820_ENTRY];
+  UINT32                            NumberE820Entries;
+
+  //
+  // True if legacy VGA INT 10h handler installed
+  //
+  BOOLEAN                           VgaInstalled;
+
+  //
+  // Number of IDE drives
+  //
+  UINT8                             IdeDriveCount;
+
+  //
+  // Current Free Option ROM space. An option ROM must NOT go past
+  // BiosStart.
+  //
+  UINT32                            OptionRom;
+
+  //
+  // Save Legacy16 unexpected interrupt vector. Reprogram INT 68-6F from
+  // EFI values to legacy value just before boot.
+  //
+  UINT32                            BiosUnexpectedInt;
+  UINT32                            ThunkSavedInt[8];
+  UINT16                            ThunkSeg;
+  LEGACY_EFI_HDD_TABLE              *LegacyEfiHddTable;
+  UINT16                            LegacyEfiHddTableIndex;
+  UINT8                             DiskEnd;
+  UINT8                             Disk4075;
+  UINT16                            TraceIndex;
+  UINT16                            Trace[0x200];
+
+  //
+  // Indicate that whether GenericLegacyBoot is entered or not
+  //
+  BOOLEAN                           LegacyBootEntered;
+
+  //
+  // CSM16 PCI Interface Version
+  //
+  UINT16                            Csm16PciInterfaceVersion;
+
+} LEGACY_BIOS_INSTANCE;
+
+
+#pragma pack(1)
+
+/*
+  40:00-01 Com1
+  40:02-03 Com2
+  40:04-05 Com3
+  40:06-07 Com4
+  40:08-09 Lpt1
+  40:0A-0B Lpt2
+  40:0C-0D Lpt3
+  40:0E-0E Ebda segment
+  40:10-11 MachineConfig
+  40:12    Bda12 - skip
+  40:13-14 MemSize below 1MB
+  40:15-16 Bda15_16 - skip
+  40:17    Keyboard Shift status
+  40:18-19 Bda18_19 - skip
+  40:1A-1B Key buffer head
+  40:1C-1D Key buffer tail
+  40:1E-3D Bda1E_3D- key buffer -skip
+  40:3E-3F FloppyData 3E = Calibration status 3F = Motor status
+  40:40    FloppyTimeout
+  40:41-74 Bda41_74 - skip
+  40:75    Number of HDD drives
+  40:76-77 Bda76_77 - skip
+  40:78-79 78 = Lpt1 timeout, 79 = Lpt2 timeout
+  40:7A-7B 7A = Lpt3 timeout, 7B = Lpt4 timeout
+  40:7C-7D 7C = Com1 timeout, 7D = Com2 timeout
+  40:7E-7F 7E = Com3 timeout, 7F = Com4 timeout
+  40:80-81 Pointer to start of key buffer
+  40:82-83 Pointer to end of key buffer
+  40:84-87 Bda84_87 - skip
+  40:88    HDD Data Xmit rate
+  40:89-8f skip
+  40:90    Floppy data rate
+  40:91-95 skip
+  40:96    Keyboard Status
+  40:97    LED Status
+  40:98-101 skip
+*/
+typedef struct {
+  UINT16  Com1;
+  UINT16  Com2;
+  UINT16  Com3;
+  UINT16  Com4;
+  UINT16  Lpt1;
+  UINT16  Lpt2;
+  UINT16  Lpt3;
+  UINT16  Ebda;
+  UINT16  MachineConfig;
+  UINT8   Bda12;
+  UINT16  MemSize;
+  UINT8   Bda15_16[0x02];
+  UINT8   ShiftStatus;
+  UINT8   Bda18_19[0x02];
+  UINT16  KeyHead;
+  UINT16  KeyTail;
+  UINT16  Bda1E_3D[0x10];
+  UINT16  FloppyData;
+  UINT8   FloppyTimeout;
+  UINT8   Bda41_74[0x34];
+  UINT8   NumberOfDrives;
+  UINT8   Bda76_77[0x02];
+  UINT16  Lpt1_2Timeout;
+  UINT16  Lpt3_4Timeout;
+  UINT16  Com1_2Timeout;
+  UINT16  Com3_4Timeout;
+  UINT16  KeyStart;
+  UINT16  KeyEnd;
+  UINT8   Bda84_87[0x4];
+  UINT8   DataXmit;
+  UINT8   Bda89_8F[0x07];
+  UINT8   FloppyXRate;
+  UINT8   Bda91_95[0x05];
+  UINT8   KeyboardStatus;
+  UINT8   LedStatus;
+} BDA_STRUC;
+#pragma pack()
+
+#define LEGACY_BIOS_INSTANCE_FROM_THIS(this)  CR (this, LEGACY_BIOS_INSTANCE, LegacyBios, LEGACY_BIOS_INSTANCE_SIGNATURE)
+
+/**
+  Thunk to 16-bit real mode and execute a software interrupt with a vector
+  of BiosInt. Regs will contain the 16-bit register context on entry and
+  exit.
+
+  @param  This    Protocol instance pointer.
+  @param  BiosInt Processor interrupt vector to invoke
+  @param  Regs    Register contexted passed into (and returned) from thunk to
+                  16-bit mode
+
+  @retval FALSE   Thunk completed, and there were no BIOS errors in the target code.
+                  See Regs for status.
+  @retval TRUE     There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+LegacyBiosInt86 (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          *This,
+  IN  UINT8                             BiosInt,
+  IN  EFI_IA32_REGISTER_SET             *Regs
+  );
+
+
+/**
+  Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
+  16-bit register context on entry and exit. Arguments can be passed on
+  the Stack argument
+
+  @param  This                   Protocol instance pointer.
+  @param  Segment                Segemnt of 16-bit mode call
+  @param  Offset                 Offset of 16-bit mdoe call
+  @param  Regs                   Register contexted passed into (and returned) from
+                                 thunk to  16-bit mode
+  @param  Stack                  Caller allocated stack used to pass arguments
+  @param  StackSize              Size of Stack in bytes
+
+  @retval FALSE                  Thunk completed, and there were no BIOS errors in
+                                 the target code. See Regs for status.
+  @retval TRUE                   There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+LegacyBiosFarCall86 (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          *This,
+  IN  UINT16                            Segment,
+  IN  UINT16                            Offset,
+  IN  EFI_IA32_REGISTER_SET             *Regs,
+  IN  VOID                              *Stack,
+  IN  UINTN                             StackSize
+  );
+
+
+/**
+  Test to see if a legacy PCI ROM exists for this device. Optionally return
+  the Legacy ROM instance for this PCI device.
+
+  @param  This                   Protocol instance pointer.
+  @param  PciHandle              The PCI PC-AT OPROM from this devices ROM BAR will
+                                 be loaded
+  @param  RomImage               Return the legacy PCI ROM for this device
+  @param  RomSize                Size of ROM Image
+  @param  Flags                  Indicates if ROM found and if PC-AT.
+
+  @retval EFI_SUCCESS            Legacy Option ROM available for this device
+  @retval EFI_UNSUPPORTED        Legacy Option ROM not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosCheckPciRom (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          *This,
+  IN  EFI_HANDLE                        PciHandle,
+  OUT VOID                              **RomImage, OPTIONAL
+  OUT UINTN                             *RomSize, OPTIONAL
+  OUT UINTN                             *Flags
+  );
+
+
+/**
+  Assign drive number to legacy HDD drives prior to booting an EFI
+  aware OS so the OS can access drives without an EFI driver.
+  Note: BBS compliant drives ARE NOT available until this call by
+  either shell or EFI.
+
+  @param  This                   Protocol instance pointer.
+  @param  BbsCount               Number of BBS_TABLE structures
+  @param  BbsTable               List BBS entries
+
+  @retval EFI_SUCCESS            Drive numbers assigned
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosPrepareToBootEfi (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  OUT UINT16                          *BbsCount,
+  OUT BBS_TABLE                       **BbsTable
+  );
+
+
+/**
+  To boot from an unconventional device like parties and/or execute
+  HDD diagnostics.
+
+  @param  This                   Protocol instance pointer.
+  @param  Attributes             How to interpret the other input parameters
+  @param  BbsEntry               The 0-based index into the BbsTable for the parent
+                                  device.
+  @param  BeerData               Pointer to the 128 bytes of ram BEER data.
+  @param  ServiceAreaData        Pointer to the 64 bytes of raw Service Area data.
+                                 The caller must provide a pointer to the specific
+                                 Service Area and not the start all Service Areas.
+ EFI_INVALID_PARAMETER if error. Does NOT return if no error.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosBootUnconventionalDevice (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  IN UDC_ATTRIBUTES                   Attributes,
+  IN UINTN                            BbsEntry,
+  IN VOID                             *BeerData,
+  IN VOID                             *ServiceAreaData
+  );
+
+
+/**
+  Load a legacy PC-AT OPROM on the PciHandle device. Return information
+  about how many disks were added by the OPROM and the shadow address and
+  size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C:
+
+  @param  This                   Protocol instance pointer.
+  @param  PciHandle              The PCI PC-AT OPROM from this devices ROM BAR will
+                                 be loaded. This value is NULL if RomImage is
+                                 non-NULL. This is the normal case.
+  @param  RomImage               A PCI PC-AT ROM image. This argument is non-NULL
+                                 if there is no hardware associated with the ROM
+                                 and thus no PciHandle, otherwise is must be NULL.
+                                 Example is PXE base code.
+  @param  Flags                  Indicates if ROM found and if PC-AT.
+  @param  DiskStart              Disk number of first device hooked by the ROM. If
+                                 DiskStart is the same as DiskEnd no disked were
+                                 hooked.
+  @param  DiskEnd                Disk number of the last device hooked by the ROM.
+  @param  RomShadowAddress       Shadow address of PC-AT ROM
+  @param  RomShadowedSize        Size of RomShadowAddress in bytes
+
+  @retval EFI_SUCCESS            Legacy ROM loaded for this device
+  @retval EFI_INVALID_PARAMETER  PciHandle not found
+  @retval EFI_UNSUPPORTED        There is no PCI ROM in the ROM BAR or no onboard
+                                 ROM
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosInstallPciRom (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          * This,
+  IN  EFI_HANDLE                        PciHandle,
+  IN  VOID                              **RomImage,
+  OUT UINTN                             *Flags,
+  OUT UINT8                             *DiskStart, OPTIONAL
+  OUT UINT8                             *DiskEnd, OPTIONAL
+  OUT VOID                              **RomShadowAddress, OPTIONAL
+  OUT UINT32                            *RomShadowedSize OPTIONAL
+  );
+
+
+/**
+  Fill in the standard BDA for Keyboard LEDs
+
+  @param  This                   Protocol instance pointer.
+  @param  Leds                   Current LED status
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosUpdateKeyboardLedStatus (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN UINT8                              Leds
+  );
+
+
+/**
+  Get all BBS info
+
+  @param  This                   Protocol instance pointer.
+  @param  HddCount               Number of HDD_INFO structures
+  @param  HddInfo                Onboard IDE controller information
+  @param  BbsCount               Number of BBS_TABLE structures
+  @param  BbsTable               List BBS entries
+
+  @retval EFI_SUCCESS            Tables returned
+  @retval EFI_NOT_FOUND          resource not found
+  @retval EFI_DEVICE_ERROR       can not get BBS table
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosGetBbsInfo (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          *This,
+  OUT UINT16                            *HddCount,
+  OUT HDD_INFO                          **HddInfo,
+  OUT UINT16                            *BbsCount,
+  OUT BBS_TABLE                         **BbsTable
+  );
+
+
+/**
+  Shadow all legacy16 OPROMs that haven't been shadowed.
+  Warning: Use this with caution. This routine disconnects all EFI
+  drivers. If used externally then caller must re-connect EFI
+  drivers.
+
+  @param  This                   Protocol instance pointer.
+
+  @retval EFI_SUCCESS            OPROMs shadowed
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosShadowAllLegacyOproms (
+  IN EFI_LEGACY_BIOS_PROTOCOL   *This
+  );
+
+
+/**
+  Attempt to legacy boot the BootOption. If the EFI contexted has been
+  compromised this function will not return.
+
+  @param  This                   Protocol instance pointer.
+  @param  BbsDevicePath          EFI Device Path from BootXXXX variable.
+  @param  LoadOptionsSize        Size of LoadOption in size.
+  @param  LoadOptions            LoadOption from BootXXXX variable
+
+  @retval EFI_SUCCESS            Removable media not present
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosLegacyBoot (
+  IN  EFI_LEGACY_BIOS_PROTOCOL          *This,
+  IN  BBS_BBS_DEVICE_PATH               *BbsDevicePath,
+  IN  UINT32                            LoadOptionsSize,
+  IN  VOID                              *LoadOptions
+  );
+
+
+/**
+  Allocate memory < 1 MB and copy the thunker code into low memory. Se up
+  all the descriptors.
+
+  @param  Private                Private context for Legacy BIOS
+
+  @retval EFI_SUCCESS            Should only pass.
+
+**/
+EFI_STATUS
+LegacyBiosInitializeThunk (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  );
+
+
+/**
+  Fill in the standard BDA and EBDA stuff before Legacy16 load
+
+  @param  Private                Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosInitBda (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  );
+
+
+/**
+  Collect IDE Inquiry data from the IDE disks
+
+  @param  Private                Legacy BIOS Instance data
+  @param  HddInfo                Hdd Information
+  @param  Flag                   Reconnect IdeController or not
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildIdeData (
+  IN  LEGACY_BIOS_INSTANCE      *Private,
+  IN  HDD_INFO                  **HddInfo,
+  IN  UINT16                    Flag
+  );
+
+
+/**
+  Enable ide controller.  This gets disabled when LegacyBoot.c is about
+  to run the Option ROMs.
+
+  @param  Private                Legacy BIOS Instance data
+
+
+**/
+VOID
+EnableIdeController (
+  IN LEGACY_BIOS_INSTANCE       *Private
+  );
+
+
+/**
+  If the IDE channel is in compatibility (legacy) mode, remove all
+  PCI I/O BAR addresses from the controller.
+
+  @param  IdeController          The handle of target IDE controller
+
+
+**/
+VOID
+InitLegacyIdeController (
+  IN EFI_HANDLE                 IdeController
+  );
+
+
+/**
+  Program the interrupt routing register in all the PCI devices. On a PC AT system
+  this register contains the 8259 IRQ vector that matches it's PCI interrupt.
+
+  @param  Private                Legacy  BIOS Instance data
+
+  @retval EFI_SUCCESS            Succeed.
+  @retval EFI_ALREADY_STARTED    All PCI devices have been processed.
+
+**/
+EFI_STATUS
+PciProgramAllInterruptLineRegisters (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  );
+
+
+/**
+  Collect EFI Info about legacy devices.
+
+  @param  Private                Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildSioData (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  );
+
+
+/**
+  Shadow all the PCI legacy ROMs. Use data from the Legacy BIOS Protocol
+  to chose the order. Skip any devices that have already have legacy
+  BIOS run.
+
+  @param  Private                Protocol instance pointer.
+
+  @retval EFI_SUCCESS            Succeed.
+  @retval EFI_UNSUPPORTED        Cannot get VGA device handle.
+
+**/
+EFI_STATUS
+PciShadowRoms (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  );
+
+
+/**
+  Fill in the standard BDA and EBDA stuff prior to legacy Boot
+
+  @param  Private                Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosCompleteBdaBeforeBoot (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  );
+
+
+/**
+  Fill in the standard CMOS stuff before Legacy16 load
+
+  @param  Private                Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosInitCmos (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  );
+
+
+/**
+  Fill in the standard CMOS stuff prior to legacy Boot
+
+  @param  Private                Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosCompleteStandardCmosBeforeBoot (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  );
+
+
+/**
+  Contains the code that is copied into low memory (below 640K).
+  This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f.
+  This template must be copied into low memory, and the IDT entries
+  0x68-0x6F must be point to the low memory copy of this code.  Each
+  entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily
+  computed.
+
+**/
+VOID
+InterruptRedirectionTemplate (
+  VOID
+  );
+
+
+/**
+  Build the E820 table.
+
+  @param  Private                Legacy BIOS Instance data
+  @param  Size                   Size of E820 Table
+
+  @retval EFI_SUCCESS            It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildE820 (
+  IN  LEGACY_BIOS_INSTANCE    *Private,
+  OUT UINTN                   *Size
+  );
+
+/**
+  This function is to put all AP in halt state.
+
+  @param  Private                Legacy BIOS Instance data
+
+**/
+VOID
+ShutdownAPs (
+  IN LEGACY_BIOS_INSTANCE              *Private
+  );
+
+/**
+  Worker function for LegacyBiosGetFlatDescs, retrieving content of
+  specific registers.
+
+  @param  IntThunk  Pointer to IntThunk of Legacy BIOS context.
+
+**/
+VOID
+GetRegisters (
+  LOW_MEMORY_THUNK    *IntThunk
+  );
+
+/**
+  Routine for calling real thunk code.
+
+  @param  RealCode    The address of thunk code.
+  @param  BiosInt     The Bios interrupt vector number.
+  @param  CallAddress The address of 16-bit mode call.
+
+  @return  Status returned by real thunk code
+
+**/
+UINTN
+CallRealThunkCode (
+  UINT8               *RealCode,
+  UINT8               BiosInt,
+  UINT32              CallAddress
+  );
+
+/**
+  Routine for generating soft interrupt.
+
+  @param Vector  The interrupt vector number.
+
+**/
+VOID
+GenerateSoftInit (
+  UINT8               Vector
+  );
+
+/**
+  Allocate memory for legacy usage.
+
+  @param  AllocateType               The type of allocation to perform.
+  @param  MemoryType                 The type of memory to allocate.
+  @param  StartPageAddress           Start address of range
+  @param  Pages                      Number of pages to allocate
+  @param  Result                     Result of allocation
+
+  @retval EFI_SUCCESS                Legacy16 code loaded
+  @retval Other                      No protocol installed, unload driver.
+
+**/
+EFI_STATUS
+AllocateLegacyMemory (
+  IN  EFI_ALLOCATE_TYPE         AllocateType,
+  IN  EFI_MEMORY_TYPE           MemoryType,
+  IN  EFI_PHYSICAL_ADDRESS      StartPageAddress,
+  IN  UINTN                     Pages,
+  OUT EFI_PHYSICAL_ADDRESS      *Result
+  );
+
+/**
+  Get a region from the LegacyBios for Tiano usage. Can only be invoked once.
+
+  @param  This                       Protocol instance pointer.
+  @param  LegacyMemorySize           Size of required region
+  @param  Region                     Region to use. 00 = Either 0xE0000 or 0xF0000
+                                     block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000
+                                     block
+  @param  Alignment                  Address alignment. Bit mapped. First non-zero
+                                     bit from right is alignment.
+  @param  LegacyMemoryAddress        Region Assigned
+
+  @retval EFI_SUCCESS                Region assigned
+  @retval EFI_ACCESS_DENIED          Procedure previously invoked
+  @retval Other                      Region not assigned
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosGetLegacyRegion (
+  IN    EFI_LEGACY_BIOS_PROTOCOL *This,
+  IN    UINTN                    LegacyMemorySize,
+  IN    UINTN                    Region,
+  IN    UINTN                    Alignment,
+  OUT   VOID                     **LegacyMemoryAddress
+  );
+
+/**
+  Get a region from the LegacyBios for Tiano usage. Can only be invoked once.
+
+  @param  This                       Protocol instance pointer.
+  @param  LegacyMemorySize           Size of data to copy
+  @param  LegacyMemoryAddress        Legacy Region destination address Note: must
+                                     be in region assigned by
+                                     LegacyBiosGetLegacyRegion
+  @param  LegacyMemorySourceAddress  Source of data
+
+  @retval EFI_SUCCESS                Region assigned
+  @retval EFI_ACCESS_DENIED          Destination outside assigned region
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosCopyLegacyRegion (
+  IN EFI_LEGACY_BIOS_PROTOCOL *This,
+  IN    UINTN                 LegacyMemorySize,
+  IN    VOID                  *LegacyMemoryAddress,
+  IN    VOID                  *LegacyMemorySourceAddress
+  );
+
+/**
+  Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find
+  the $EFI table in the shadow area. Thunk into the Legacy16 code after it had
+  been shadowed.
+
+  @param  Private                    Legacy BIOS context data
+
+  @retval EFI_SUCCESS                Legacy16 code loaded
+  @retval Other                      No protocol installed, unload driver.
+
+**/
+EFI_STATUS
+ShadowAndStartLegacy16 (
+  IN  LEGACY_BIOS_INSTANCE  *Private
+  );
+
+/**
+  Checks the state of the floppy and if media is inserted.
+
+  This routine checks the state of the floppy and if media is inserted.
+  There are 3 cases:
+  No floppy present         - Set BBS entry to ignore
+  Floppy present & no media - Set BBS entry to lowest priority. We cannot
+  set it to ignore since 16-bit CSM will
+  indicate no floppy and thus drive A: is
+  unusable. CSM-16 will not try floppy since
+  lowest priority and thus not incur boot
+  time penality.
+  Floppy present & media    - Set BBS entry to some priority.
+
+  @return  State of floppy media
+
+**/
+UINT8
+HasMediaInFloppy (
+  VOID
+  );
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+  This requires updating the checksum, if it exists.
+
+  @param  IdentifyDriveData       ATA Identify Data
+  @param  Checksum                checksum of the ATA Identify Data
+
+  @retval EFI_SUCCESS             checksum calculated
+  @retval EFI_SECURITY_VIOLATION  IdentifyData invalid
+
+**/
+EFI_STATUS
+CalculateIdentifyDriveChecksum (
+  IN  UINT8     *IdentifyDriveData,
+  OUT UINT8     *Checksum
+  );
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+
+  @param  IdentifyDriveData       ATA Identify Data
+
+**/
+VOID
+UpdateIdentifyDriveData (
+  IN  UINT8     *IdentifyDriveData
+  );
+
+/**
+  Complete build of BBS TABLE.
+
+  @param  Private                 Legacy BIOS Instance data
+  @param  BbsTable                BBS Table passed to 16-bit code
+
+  @retval EFI_SUCCESS             Removable media not present
+
+**/
+EFI_STATUS
+LegacyBiosBuildBbs (
+  IN  LEGACY_BIOS_INSTANCE      *Private,
+  IN  BBS_TABLE                 *BbsTable
+  );
+
+/**
+  Read CMOS register through index/data port.
+
+  @param[in]  Index   The index of the CMOS register to read.
+
+  @return  The data value from the CMOS register specified by Index.
+
+**/
+UINT8
+LegacyReadStandardCmos (
+  IN UINT8  Index
+  );
+
+/**
+  Write CMOS register through index/data port.
+
+  @param[in]  Index  The index of the CMOS register to write.
+  @param[in]  Value  The value of CMOS register to write.
+
+  @return  The value written to the CMOS register specified by Index.
+
+**/
+UINT8
+LegacyWriteStandardCmos (
+  IN UINT8  Index,
+  IN UINT8  Value
+  );
+
+/**
+  Calculate the new standard CMOS checksum and write it.
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  Calculate 16-bit checksum successfully
+
+**/
+EFI_STATUS
+LegacyCalculateWriteStandardCmosChecksum (
+  VOID
+  );
+
+/**
+  Test to see if a legacy PCI ROM exists for this device. Optionally return
+  the Legacy ROM instance for this PCI device.
+
+  @param[in]  This                   Protocol instance pointer.
+  @param[in]  PciHandle              The PCI PC-AT OPROM from this devices ROM BAR will be loaded
+  @param[out] RomImage               Return the legacy PCI ROM for this device
+  @param[out] RomSize                Size of ROM Image
+  @param[out] RuntimeImageLength     Runtime size of ROM Image
+  @param[out] Flags                  Indicates if ROM found and if PC-AT.
+  @param[out] OpromRevision          Revision of the PCI Rom
+  @param[out] ConfigUtilityCodeHeaderPointer of Configuration Utility Code Header
+
+  @return EFI_SUCCESS            Legacy Option ROM available for this device
+  @return EFI_ALREADY_STARTED    This device is already managed by its Oprom
+  @return EFI_UNSUPPORTED        Legacy Option ROM not supported.
+
+**/
+EFI_STATUS
+LegacyBiosCheckPciRomEx (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN  EFI_HANDLE                        PciHandle,
+  OUT VOID                              **RomImage, OPTIONAL
+  OUT UINTN                             *RomSize, OPTIONAL
+  OUT UINTN                             *RuntimeImageLength, OPTIONAL
+  OUT UINTN                             *Flags, OPTIONAL
+  OUT UINT8                             *OpromRevision, OPTIONAL
+  OUT VOID                              **ConfigUtilityCodeHeader OPTIONAL
+  );
+
+/**
+  Relocate this image under 4G memory for IPF.
+
+  @param  ImageHandle  Handle of driver image.
+  @param  SystemTable  Pointer to system table.
+
+  @retval EFI_SUCCESS  Image successfully relocated.
+  @retval EFI_ABORTED  Failed to relocate image.
+
+**/
+EFI_STATUS
+RelocateImageUnder4GIfNeeded (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  );
+
+/**
+  Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
+  16-bit register context on entry and exit. Arguments can be passed on
+  the Stack argument
+
+  @param  This       Protocol instance pointer.
+  @param  Segment    Segemnt of 16-bit mode call
+  @param  Offset     Offset of 16-bit mdoe call
+  @param  Regs       Register contexted passed into (and returned) from thunk to
+                     16-bit mode
+  @param  Stack      Caller allocated stack used to pass arguments
+  @param  StackSize  Size of Stack in bytes
+
+  @retval FALSE      Thunk completed, and there were no BIOS errors in the target code.
+                     See Regs for status.
+  @retval TRUE       There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+InternalLegacyBiosFarCall (
+  IN  EFI_LEGACY_BIOS_PROTOCOL        *This,
+  IN  UINT16                          Segment,
+  IN  UINT16                          Offset,
+  IN  EFI_IA32_REGISTER_SET           *Regs,
+  IN  VOID                            *Stack,
+  IN  UINTN                           StackSize
+  );
+
+/**
+  Load a legacy PC-AT OpROM for VGA controller.
+
+  @param  Private                Driver private data.
+
+  @retval EFI_SUCCESS            Legacy ROM successfully installed for this device.
+  @retval EFI_DEVICE_ERROR       No VGA device handle found, or native EFI video
+                                 driver cannot be successfully disconnected, or VGA
+                                 thunk driver cannot be successfully connected.
+
+**/
+EFI_STATUS
+LegacyBiosInstallVgaRom (
+  IN  LEGACY_BIOS_INSTANCE            *Private
+  );
+
+#endif
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.h b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.h
new file mode 100644
index 0000000000..0c491318c2
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.h
@@ -0,0 +1,249 @@
+/** @file
+  Legacy boot maintainence Ui definition.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef _EFI_LEGACY_BOOT_OPTION_H_
+#define _EFI_LEGACY_BOOT_OPTION_H_
+
+#include <PiDxe.h>
+
+
+#include <Guid/GlobalVariable.h>
+#include <Guid/LegacyDevOrder.h>
+#include <Guid/MdeModuleHii.h>
+
+#include <Protocol/HiiConfigAccess.h>
+#include <Protocol/HiiConfigRouting.h>
+
+#include <Protocol/HiiDatabase.h>
+#include <Protocol/LegacyBios.h>
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/BaseMemoryLib.h>
+
+#include "LegacyBootMaintUiVfr.h"
+
+#define CONFIG_OPTION_OFFSET    0x1200
+
+//
+// VarOffset that will be used to create question
+// all these values are computed from the structure
+// defined below
+//
+#define VAR_OFFSET(Field)              ((UINT16) ((UINTN) &(((LEGACY_BOOT_NV_DATA *) 0)->Field)))
+
+//
+// Question Id of Zero is invalid, so add an offset to it
+//
+#define QUESTION_ID(Field)             (VAR_OFFSET (Field) + CONFIG_OPTION_OFFSET)
+
+
+#define LEGACY_FD_QUESTION_ID           QUESTION_ID (LegacyFD)
+#define LEGACY_HD_QUESTION_ID           QUESTION_ID (LegacyHD)
+#define LEGACY_CD_QUESTION_ID           QUESTION_ID (LegacyCD)
+#define LEGACY_NET_QUESTION_ID          QUESTION_ID (LegacyNET)
+#define LEGACY_BEV_QUESTION_ID          QUESTION_ID (LegacyBEV)
+
+
+//
+// String Contant
+//
+#define STR_FLOPPY          L"Floppy Drive #%02x"
+#define STR_HARDDISK        L"HardDisk Drive #%02x"
+#define STR_CDROM           L"ATAPI CDROM Drive #%02x"
+#define STR_NET             L"NET Drive #%02x"
+#define STR_BEV             L"BEV Drive #%02x"
+
+#define STR_FLOPPY_HELP     L"Select Floppy Drive #%02x"
+#define STR_HARDDISK_HELP   L"Select HardDisk Drive #%02x"
+#define STR_CDROM_HELP      L"Select ATAPI CDROM Drive #%02x"
+#define STR_NET_HELP        L"NET Drive #%02x"
+#define STR_BEV_HELP        L"BEV Drive #%02x"
+
+#define STR_FLOPPY_TITLE    L"Set Legacy Floppy Drive Order"
+#define STR_HARDDISK_TITLE  L"Set Legacy HardDisk Drive Order"
+#define STR_CDROM_TITLE     L"Set Legacy CDROM Drive Order"
+#define STR_NET_TITLE       L"Set Legacy NET Drive Order"
+#define STR_BEV_TITLE       L"Set Legacy BEV Drive Order"
+
+//
+// These are the VFR compiler generated data representing our VFR data.
+//
+extern UINT8 LegacyBootMaintUiVfrBin[];
+
+#pragma pack(1)
+
+///
+/// HII specific Vendor Device Path definition.
+///
+typedef struct {
+  VENDOR_DEVICE_PATH             VendorDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL       End;
+} HII_VENDOR_DEVICE_PATH;
+
+
+
+//
+// Variable created with this flag will be "Efi:...."
+//
+#define VAR_FLAG  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE
+
+
+#define LEGACY_BOOT_OPTION_CALLBACK_DATA_SIGNATURE  SIGNATURE_32 ('L', 'G', 'C', 'B')
+
+typedef struct {
+  UINTN                            Signature;
+
+  //
+  // HII relative handles
+  //
+  EFI_HII_HANDLE                   HiiHandle;
+  EFI_HANDLE                       DriverHandle;
+
+  //
+  // Produced protocols
+  //
+  EFI_HII_CONFIG_ACCESS_PROTOCOL   ConfigAccess;
+
+  //
+  // Maintain the data.
+  //
+  LEGACY_BOOT_MAINTAIN_DATA        *MaintainMapData;
+} LEGACY_BOOT_OPTION_CALLBACK_DATA;
+
+//
+// All of the signatures that will be used in list structure
+//
+#define LEGACY_MENU_OPTION_SIGNATURE      SIGNATURE_32 ('m', 'e', 'n', 'u')
+#define LEGACY_MENU_ENTRY_SIGNATURE       SIGNATURE_32 ('e', 'n', 't', 'r')
+
+#define LEGACY_LEGACY_DEV_CONTEXT_SELECT  0x9
+
+typedef struct {
+  UINTN           Signature;
+  LIST_ENTRY      Head;
+  UINTN           MenuNumber;
+} LEGACY_MENU_OPTION;
+
+typedef struct {
+  UINT16    BbsIndex;
+  CHAR16    *Description;
+} LEGACY_DEVICE_CONTEXT;
+
+typedef struct {
+  UINTN           Signature;
+  LIST_ENTRY      Link;
+  UINTN           OptionNumber;
+  UINT16          *DisplayString;
+  UINT16          *HelpString;
+  EFI_STRING_ID   DisplayStringToken;
+  EFI_STRING_ID   HelpStringToken;
+  VOID            *VariableContext;
+} LEGACY_MENU_ENTRY;
+
+typedef struct {
+  UINT16     BbsIndex;
+} LEGACY_BOOT_OPTION_BBS_DATA;
+
+#pragma pack()
+
+/**
+  This call back function is registered with Boot Manager formset.
+  When user selects a boot option, this call back function will
+  be triggered. The boot option is saved for later processing.
+
+
+  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Action          Specifies the type of action taken by the browser.
+  @param QuestionId      A unique value which is sent to the original exporting driver
+                         so that it can identify the type of data to expect.
+  @param Type            The type of value for the question.
+  @param Value           A pointer to the data being sent to the original exporting driver.
+  @param ActionRequest   On return, points to the action requested by the callback function.
+
+  @retval  EFI_SUCCESS           The callback successfully handled the action.
+  @retval  EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionCallback (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  EFI_BROWSER_ACTION                     Action,
+  IN  EFI_QUESTION_ID                        QuestionId,
+  IN  UINT8                                  Type,
+  IN  EFI_IFR_TYPE_VALUE                     *Value,
+  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
+  );
+
+/**
+  This function allows a caller to extract the current configuration for one
+  or more named elements from the target driver.
+
+
+  @param This            - Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Request         - A null-terminated Unicode string in <ConfigRequest> format.
+  @param Progress        - On return, points to a character in the Request string.
+                         Points to the string's null terminator if request was successful.
+                         Points to the most recent '&' before the first failing name/value
+                         pair (or the beginning of the string if the failure is in the
+                         first name/value pair) if the request was not successful.
+  @param Results         - A null-terminated Unicode string in <ConfigAltResp> format which
+                         has all values filled in for the names in the Request string.
+                         String to be allocated by the called function.
+
+  @retval  EFI_SUCCESS            The Results is filled with the requested values.
+  @retval  EFI_OUT_OF_RESOURCES   Not enough memory to store the results.
+  @retval  EFI_INVALID_PARAMETER  Request is NULL, illegal syntax, or unknown name.
+  @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionExtractConfig (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  CONST EFI_STRING                       Request,
+  OUT EFI_STRING                             *Progress,
+  OUT EFI_STRING                             *Results
+  );
+
+/**
+  This function processes the results of changes in configuration.
+
+
+  @param This            - Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Configuration   - A null-terminated Unicode string in <ConfigResp> format.
+  @param Progress        - A pointer to a string filled in with the offset of the most
+                         recent '&' before the first failing name/value pair (or the
+                         beginning of the string if the failure is in the first
+                         name/value pair) or the terminating NULL if all was successful.
+
+  @retval  EFI_SUCCESS            The Results is processed successfully.
+  @retval  EFI_INVALID_PARAMETER  Configuration is NULL.
+  @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionRouteConfig (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  CONST EFI_STRING                       Configuration,
+  OUT EFI_STRING                             *Progress
+  );
+
+#endif
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.h b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.h
new file mode 100644
index 0000000000..e16fc39415
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.h
@@ -0,0 +1,79 @@
+/** @file
+  Legacy Boot Maintainence UI definition.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef _EFI_LEGACY_BOOT_OPTION_VFR_H_
+#define _EFI_LEGACY_BOOT_OPTION_VFR_H_
+
+#include <Guid/HiiBootMaintenanceFormset.h>
+
+#define MAX_MENU_NUMBER 100
+
+#define LEGACY_BOOT_OPTION_FORMSET_GUID  { 0x6bc75598, 0x89b4, 0x483d, { 0x91, 0x60, 0x7f, 0x46, 0x9a, 0x96, 0x35, 0x31 } }
+
+#define VARSTORE_ID_LEGACY_BOOT      0x0001
+
+
+#define LEGACY_BOOT_FORM_ID          0x1000
+#define LEGACY_ORDER_CHANGE_FORM_ID  0x1001
+
+
+#define FORM_FLOPPY_BOOT_ID          0x2000
+#define FORM_HARDDISK_BOOT_ID        0x2001
+#define FORM_CDROM_BOOT_ID           0x2002
+#define FORM_NET_BOOT_ID             0x2003
+#define FORM_BEV_BOOT_ID             0x2004
+
+
+
+#define FORM_BOOT_LEGACY_DEVICE_ID   0x9000
+#define FORM_BOOT_LEGACY_LABEL_END   0x9001
+
+
+#pragma pack(1)
+
+///
+/// This is the structure that will be used to store the
+/// question's current value. Use it at initialize time to
+/// set default value for each question. When using at run
+/// time, this map is returned by the callback function,
+/// so dynamically changing the question's value will be
+/// possible through this mechanism
+///
+typedef struct {
+  //
+  // Legacy Device Order Selection Storage
+  //
+  UINT16   LegacyFD[MAX_MENU_NUMBER];
+  UINT16   LegacyHD[MAX_MENU_NUMBER];
+  UINT16   LegacyCD[MAX_MENU_NUMBER];
+  UINT16   LegacyNET[MAX_MENU_NUMBER];
+  UINT16   LegacyBEV[MAX_MENU_NUMBER];
+} LEGACY_BOOT_NV_DATA;
+
+///
+/// This is the structure that will be used to store the
+/// question's current value. Use it at initialize time to
+/// set default value for each question. When using at run
+/// time, this map is returned by the callback function,
+/// so dynamically changing the question's value will be
+/// possible through this mechanism
+///
+typedef struct {
+  //
+  // Legacy Device Order Selection Storage
+  //
+  LEGACY_BOOT_NV_DATA   InitialNvData;
+  LEGACY_BOOT_NV_DATA   CurrentNvData;
+  LEGACY_BOOT_NV_DATA   LastTimeNvData;
+  UINT8                 DisableMap[32];
+} LEGACY_BOOT_MAINTAIN_DATA;
+
+#pragma pack()
+
+#endif
diff --git a/OvmfPkg/Csm/LegacyBootManagerLib/InternalLegacyBm.h b/OvmfPkg/Csm/LegacyBootManagerLib/InternalLegacyBm.h
new file mode 100644
index 0000000000..292e2c1e7a
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootManagerLib/InternalLegacyBm.h
@@ -0,0 +1,60 @@
+/** @file
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _INTERNAL_LEGACY_BM_H_
+#define _INTERNAL_LEGACY_BM_H_
+
+#include <PiDxe.h>
+#include <Guid/LegacyDevOrder.h>
+#include <Guid/GlobalVariable.h>
+#include <Protocol/LegacyBios.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/PciIo.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/PerformanceLib.h>
+
+#pragma pack(1)
+typedef struct {
+  UINT16     BbsIndex;
+} LEGACY_BM_BOOT_OPTION_BBS_DATA;
+#pragma pack()
+
+/**
+  Boot the legacy system with the boot option.
+
+  @param  BootOption The legacy boot option which have BBS device path
+                     On return, BootOption->Status contains the boot status.
+                     EFI_UNSUPPORTED    There is no legacybios protocol, do not support
+                                        legacy boot.
+                     EFI_STATUS         The status of LegacyBios->LegacyBoot ().
+**/
+VOID
+EFIAPI
+LegacyBmBoot (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION           *BootOption
+  );
+
+/**
+  Refresh all legacy boot options.
+
+**/
+VOID
+EFIAPI
+LegacyBmRefreshAllBootOption (
+  VOID
+  );
+
+#endif // _INTERNAL_LEGACY_BM_H_
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.c b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.c
new file mode 100644
index 0000000000..0640656dba
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideo.c
@@ -0,0 +1,3289 @@
+/** @file
+  ConsoleOut Routines that speak VGA.
+
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BiosVideo.h"
+
+//
+// EFI Driver Binding Protocol Instance
+//
+EFI_DRIVER_BINDING_PROTOCOL gBiosVideoDriverBinding = {
+  BiosVideoDriverBindingSupported,
+  BiosVideoDriverBindingStart,
+  BiosVideoDriverBindingStop,
+  0x3,
+  NULL,
+  NULL
+};
+
+//
+// Global lookup tables for VGA graphics modes
+//
+UINT8                          mVgaLeftMaskTable[]   = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+UINT8                          mVgaRightMaskTable[]  = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+
+UINT8                          mVgaBitMaskTable[]    = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+//
+// Save controller attributes during first start
+//
+UINT64                         mOriginalPciAttributes;
+BOOLEAN                        mPciAttributesSaved = FALSE;
+
+EFI_GRAPHICS_OUTPUT_BLT_PIXEL  mVgaColorToGraphicsOutputColor[] = {
+  { 0x00, 0x00, 0x00, 0x00 },
+  { 0x98, 0x00, 0x00, 0x00 },
+  { 0x00, 0x98, 0x00, 0x00 },
+  { 0x98, 0x98, 0x00, 0x00 },
+  { 0x00, 0x00, 0x98, 0x00 },
+  { 0x98, 0x00, 0x98, 0x00 },
+  { 0x00, 0x98, 0x98, 0x00 },
+  { 0x98, 0x98, 0x98, 0x00 },
+  { 0x10, 0x10, 0x10, 0x00 },
+  { 0xff, 0x10, 0x10, 0x00 },
+  { 0x10, 0xff, 0x10, 0x00 },
+  { 0xff, 0xff, 0x10, 0x00 },
+  { 0x10, 0x10, 0xff, 0x00 },
+  { 0xf0, 0x10, 0xff, 0x00 },
+  { 0x10, 0xff, 0xff, 0x00 },
+  { 0xff, 0xff, 0xff, 0x00 }
+};
+
+//
+// Standard timing defined by VESA EDID
+//
+VESA_BIOS_EXTENSIONS_EDID_TIMING mEstablishedEdidTiming[] = {
+  //
+  // Established Timing I
+  //
+  {800, 600, 60},
+  {800, 600, 56},
+  {640, 480, 75},
+  {640, 480, 72},
+  {640, 480, 67},
+  {640, 480, 60},
+  {720, 400, 88},
+  {720, 400, 70},
+  //
+  // Established Timing II
+  //
+  {1280, 1024, 75},
+  {1024,  768, 75},
+  {1024,  768, 70},
+  {1024,  768, 60},
+  {1024,  768, 87},
+  {832,   624, 75},
+  {800,   600, 75},
+  {800,   600, 72},
+  //
+  // Established Timing III
+  //
+  {1152, 870, 75}
+};
+
+/**
+  Supported.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  RemainingDevicePath    A pointer to the remaining portion of a device
+                                 path
+
+  @retval EFI_STATUS             EFI_SUCCESS:This controller can be managed by this
+                                 driver, Otherwise, this controller cannot be
+                                 managed by this driver
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                Status;
+  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+  EFI_PCI_IO_PROTOCOL       *PciIo;
+  PCI_TYPE00                Pci;
+  EFI_DEV_PATH              *Node;
+
+  //
+  // See if the Legacy BIOS Protocol is available
+  //
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Open the IO Abstraction(s) needed to perform the supported test
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+    return Status;
+  }
+
+  if (Status == EFI_ALREADY_STARTED) {
+    //
+    // If VgaMiniPort protocol is installed, EFI_ALREADY_STARTED indicates failure,
+    // because VgaMiniPort protocol is installed on controller handle directly.
+    //
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiVgaMiniPortProtocolGuid,
+                    NULL,
+                    NULL,
+                    NULL,
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                    );
+    if (!EFI_ERROR (Status)) {
+      return EFI_ALREADY_STARTED;
+    }
+  }
+  //
+  // See if this is a PCI Graphics Controller by looking at the Command register and
+  // Class Code Register
+  //
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  Status = EFI_UNSUPPORTED;
+  if (Pci.Hdr.ClassCode[2] == 0x03 || (Pci.Hdr.ClassCode[2] == 0x00 && Pci.Hdr.ClassCode[1] == 0x01)) {
+
+    Status = EFI_SUCCESS;
+    //
+    // If this is a graphics controller,
+    // go further check RemainingDevicePath validation
+    //
+    if (RemainingDevicePath != NULL) {
+      Node = (EFI_DEV_PATH *) RemainingDevicePath;
+      //
+      // Check if RemainingDevicePath is the End of Device Path Node,
+      // if yes, return EFI_SUCCESS
+      //
+      if (!IsDevicePathEnd (Node)) {
+        //
+        // If RemainingDevicePath isn't the End of Device Path Node,
+        // check its validation
+        //
+        if (Node->DevPath.Type != ACPI_DEVICE_PATH ||
+            Node->DevPath.SubType != ACPI_ADR_DP ||
+            DevicePathNodeLength(&Node->DevPath) < sizeof(ACPI_ADR_DEVICE_PATH)) {
+          Status = EFI_UNSUPPORTED;
+        }
+      }
+    }
+  }
+
+Done:
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+  return Status;
+}
+
+
+/**
+  Install Graphics Output Protocol onto VGA device handles.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  RemainingDevicePath    A pointer to the remaining portion of a device
+                                 path
+
+  @return EFI_STATUS
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;
+  EFI_PCI_IO_PROTOCOL       *PciIo;
+  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+  UINTN                     Flags;
+  UINT64                    Supports;
+
+  //
+  // Initialize local variables
+  //
+  PciIo            = NULL;
+  ParentDevicePath = NULL;
+
+  //
+  //
+  // See if the Legacy BIOS Protocol is available
+  //
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Prepare for status code
+  //
+  Status = gBS->HandleProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Open the IO Abstraction(s) needed
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
+    return Status;
+  }
+
+  //
+  // Save original PCI attributes
+  //
+  if (!mPciAttributesSaved) {
+    Status = PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationGet,
+                      0,
+                      &mOriginalPciAttributes
+                      );
+
+    if (EFI_ERROR (Status)) {
+      goto Done;
+    }
+    mPciAttributesSaved = TRUE;
+  }
+
+  //
+  // Get supported PCI attributes
+  //
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationSupported,
+                    0,
+                    &Supports
+                    );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Supports &= (UINT64)(EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16);
+  if (Supports == 0 || Supports == (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+    EFI_PROGRESS_CODE,
+    EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_PC_ENABLE,
+    ParentDevicePath
+    );
+  //
+  // Enable the device and make sure VGA cycles are being forwarded to this VGA device
+  //
+  Status = PciIo->Attributes (
+             PciIo,
+             EfiPciIoAttributeOperationEnable,
+             EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | Supports,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_RESOURCE_CONFLICT,
+      ParentDevicePath
+      );
+    goto Done;
+  }
+  //
+  // Check to see if there is a legacy option ROM image associated with this PCI device
+  //
+  Status = LegacyBios->CheckPciRom (
+                         LegacyBios,
+                         Controller,
+                         NULL,
+                         NULL,
+                         &Flags
+                         );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+  //
+  // Post the legacy option ROM if it is available.
+  //
+  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+    EFI_PROGRESS_CODE,
+    EFI_P_PC_RESET,
+    ParentDevicePath
+    );
+  Status = LegacyBios->InstallPciRom (
+                         LegacyBios,
+                         Controller,
+                         NULL,
+                         &Flags,
+                         NULL,
+                         NULL,
+                         NULL,
+                         NULL
+                         );
+  if (EFI_ERROR (Status)) {
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_CONTROLLER_ERROR,
+      ParentDevicePath
+      );
+    goto Done;
+  }
+
+  if (RemainingDevicePath != NULL) {
+    if (IsDevicePathEnd (RemainingDevicePath) &&
+        (FeaturePcdGet (PcdBiosVideoCheckVbeEnable) || FeaturePcdGet (PcdBiosVideoCheckVgaEnable))) {
+      //
+      // If RemainingDevicePath is the End of Device Path Node,
+      // don't create any child device and return EFI_SUCESS
+      Status = EFI_SUCCESS;
+      goto Done;
+    }
+  }
+
+  //
+  // Create child handle and install GraphicsOutputProtocol on it
+  //
+  Status = BiosVideoChildHandleInstall (
+             This,
+             Controller,
+             PciIo,
+             LegacyBios,
+             ParentDevicePath,
+             RemainingDevicePath
+             );
+
+Done:
+  if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) {
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_PROGRESS_CODE,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_PC_DISABLE,
+      ParentDevicePath
+      );
+
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_PROGRESS_CODE,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_NOT_DETECTED,
+      ParentDevicePath
+      );
+    if (!HasChildHandle (Controller)) {
+      if (mPciAttributesSaved) {
+        //
+        // Restore original PCI attributes
+        //
+        PciIo->Attributes (
+                        PciIo,
+                        EfiPciIoAttributeOperationSet,
+                        mOriginalPciAttributes,
+                        NULL
+                        );
+      }
+    }
+    //
+    // Release PCI I/O Protocols on the controller handle.
+    //
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+  }
+
+  return Status;
+}
+
+
+/**
+  Stop.
+
+  @param  This                   Pointer to driver binding protocol
+  @param  Controller             Controller handle to connect
+  @param  NumberOfChildren       Number of children handle created by this driver
+  @param  ChildHandleBuffer      Buffer containing child handle created
+
+  @retval EFI_SUCCESS            Driver disconnected successfully from controller
+  @retval EFI_UNSUPPORTED        Cannot find BIOS_VIDEO_DEV structure
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
+  IN  EFI_HANDLE                      Controller,
+  IN  UINTN                           NumberOfChildren,
+  IN  EFI_HANDLE                      *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                   Status;
+  BOOLEAN                      AllChildrenStopped;
+  UINTN                        Index;
+  EFI_PCI_IO_PROTOCOL          *PciIo;
+
+  AllChildrenStopped = TRUE;
+
+  if (NumberOfChildren == 0) {
+    //
+    // Close PCI I/O protocol on the controller handle
+    //
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+
+    return EFI_SUCCESS;
+  }
+
+  for (Index = 0; Index < NumberOfChildren; Index++) {
+    Status = BiosVideoChildHandleUninstall (This, Controller, ChildHandleBuffer[Index]);
+
+    if (EFI_ERROR (Status)) {
+      AllChildrenStopped = FALSE;
+    }
+  }
+
+  if (!AllChildrenStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (!HasChildHandle (Controller)) {
+    if (mPciAttributesSaved) {
+      Status = gBS->HandleProtocol (
+                      Controller,
+                      &gEfiPciIoProtocolGuid,
+                      (VOID **) &PciIo
+                      );
+      ASSERT_EFI_ERROR (Status);
+
+      //
+      // Restore original PCI attributes
+      //
+      Status = PciIo->Attributes (
+                        PciIo,
+                        EfiPciIoAttributeOperationSet,
+                        mOriginalPciAttributes,
+                        NULL
+                        );
+      ASSERT_EFI_ERROR (Status);
+    }
+  }
+
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Install child handles if the Handle supports MBR format.
+
+  @param  This                   Calling context.
+  @param  ParentHandle           Parent Handle
+  @param  ParentPciIo            Parent PciIo interface
+  @param  ParentLegacyBios       Parent LegacyBios interface
+  @param  ParentDevicePath       Parent Device Path
+  @param  RemainingDevicePath    Remaining Device Path
+
+  @retval EFI_SUCCESS            If a child handle was added
+  @retval other                  A child handle was not added
+
+**/
+EFI_STATUS
+BiosVideoChildHandleInstall (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ParentHandle,
+  IN  EFI_PCI_IO_PROTOCOL          *ParentPciIo,
+  IN  EFI_LEGACY_BIOS_PROTOCOL     *ParentLegacyBios,
+  IN  EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS               Status;
+  BIOS_VIDEO_DEV           *BiosVideoPrivate;
+  PCI_TYPE00               Pci;
+  ACPI_ADR_DEVICE_PATH     AcpiDeviceNode;
+  BOOLEAN                  ProtocolInstalled;
+
+  //
+  // Allocate the private device structure for video device
+  //
+  BiosVideoPrivate = (BIOS_VIDEO_DEV *) AllocateZeroPool (
+                                          sizeof (BIOS_VIDEO_DEV)
+                                          );
+  if (NULL == BiosVideoPrivate) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  //
+  // See if this is a VGA compatible controller or not
+  //
+  Status = ParentPciIo->Pci.Read (
+                          ParentPciIo,
+                          EfiPciIoWidthUint32,
+                          0,
+                          sizeof (Pci) / sizeof (UINT32),
+                          &Pci
+                          );
+  if (EFI_ERROR (Status)) {
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_CONTROLLER_ERROR,
+      ParentDevicePath
+      );
+    goto Done;
+  }
+  BiosVideoPrivate->VgaCompatible = FALSE;
+  if (Pci.Hdr.ClassCode[2] == 0x00 && Pci.Hdr.ClassCode[1] == 0x01) {
+    BiosVideoPrivate->VgaCompatible = TRUE;
+  }
+
+  if (Pci.Hdr.ClassCode[2] == 0x03 && Pci.Hdr.ClassCode[1] == 0x00 && Pci.Hdr.ClassCode[0] == 0x00) {
+    BiosVideoPrivate->VgaCompatible = TRUE;
+  }
+
+ if (PcdGetBool (PcdBiosVideoSetTextVgaModeEnable)) {
+    //
+    // Create EXIT_BOOT_SERIVES Event
+    //
+    Status = gBS->CreateEventEx (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    BiosVideoNotifyExitBootServices,
+                    BiosVideoPrivate,
+                    &gEfiEventExitBootServicesGuid,
+                    &BiosVideoPrivate->ExitBootServicesEvent
+                    );
+    if (EFI_ERROR (Status)) {
+      goto Done;
+    }
+  }
+
+  //
+  // Initialize the child private structure
+  //
+  BiosVideoPrivate->Signature = BIOS_VIDEO_DEV_SIGNATURE;
+
+  //
+  // Fill in Graphics Output specific mode structures
+  //
+  BiosVideoPrivate->HardwareNeedsStarting = TRUE;
+  BiosVideoPrivate->ModeData              = NULL;
+  BiosVideoPrivate->LineBuffer            = NULL;
+  BiosVideoPrivate->VgaFrameBuffer        = NULL;
+  BiosVideoPrivate->VbeFrameBuffer        = NULL;
+
+  //
+  // Fill in the Graphics Output Protocol
+  //
+  BiosVideoPrivate->GraphicsOutput.QueryMode = BiosVideoGraphicsOutputQueryMode;
+  BiosVideoPrivate->GraphicsOutput.SetMode   = BiosVideoGraphicsOutputSetMode;
+
+
+  //
+  // Allocate buffer for Graphics Output Protocol mode information
+  //
+  BiosVideoPrivate->GraphicsOutput.Mode = (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *) AllocatePool (
+                                             sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE)
+                                             );
+  if (NULL == BiosVideoPrivate->GraphicsOutput.Mode) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  BiosVideoPrivate->GraphicsOutput.Mode->Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) AllocatePool (
+                                             sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)
+                                             );
+  if (NULL ==  BiosVideoPrivate->GraphicsOutput.Mode->Info) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  //
+  // Assume that Graphics Output Protocol will be produced until proven otherwise
+  //
+  BiosVideoPrivate->ProduceGraphicsOutput = TRUE;
+
+  //
+  // Set Gop Device Path, here RemainingDevicePath will not be one End of Device Path Node.
+  //
+  if ((RemainingDevicePath == NULL) || (!IsDevicePathEnd (RemainingDevicePath))) {
+    if (RemainingDevicePath == NULL) {
+      ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH));
+      AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH;
+      AcpiDeviceNode.Header.SubType = ACPI_ADR_DP;
+      AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0);
+      SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH));
+
+      BiosVideoPrivate->GopDevicePath = AppendDevicePathNode (
+                                          ParentDevicePath,
+                                          (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode
+                                          );
+    } else {
+      BiosVideoPrivate->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath);
+    }
+
+    //
+    // Creat child handle and device path protocol firstly
+    //
+    BiosVideoPrivate->Handle = NULL;
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &BiosVideoPrivate->Handle,
+                    &gEfiDevicePathProtocolGuid,
+                    BiosVideoPrivate->GopDevicePath,
+                    NULL
+                    );
+    if (EFI_ERROR (Status)) {
+      goto Done;
+    }
+  }
+
+  //
+  // Fill in the VGA Mini Port Protocol fields
+  //
+  BiosVideoPrivate->VgaMiniPort.SetMode                   = BiosVideoVgaMiniPortSetMode;
+  BiosVideoPrivate->VgaMiniPort.VgaMemoryOffset           = 0xb8000;
+  BiosVideoPrivate->VgaMiniPort.CrtcAddressRegisterOffset = 0x3d4;
+  BiosVideoPrivate->VgaMiniPort.CrtcDataRegisterOffset    = 0x3d5;
+  BiosVideoPrivate->VgaMiniPort.VgaMemoryBar              = EFI_PCI_IO_PASS_THROUGH_BAR;
+  BiosVideoPrivate->VgaMiniPort.CrtcAddressRegisterBar    = EFI_PCI_IO_PASS_THROUGH_BAR;
+  BiosVideoPrivate->VgaMiniPort.CrtcDataRegisterBar       = EFI_PCI_IO_PASS_THROUGH_BAR;
+
+  //
+  // Child handle need to consume the Legacy Bios protocol
+  //
+  BiosVideoPrivate->LegacyBios = ParentLegacyBios;
+
+  //
+  // When check for VBE, PCI I/O protocol is needed, so use parent's protocol interface temporally
+  //
+  BiosVideoPrivate->PciIo                 = ParentPciIo;
+
+  //
+  // Check for VESA BIOS Extensions for modes that are compatible with Graphics Output
+  //
+  if (FeaturePcdGet (PcdBiosVideoCheckVbeEnable)) {
+    Status = BiosVideoCheckForVbe (BiosVideoPrivate);
+    DEBUG ((EFI_D_INFO, "BiosVideoCheckForVbe - %r\n", Status));
+  } else {
+    Status = EFI_UNSUPPORTED;
+  }
+  if (EFI_ERROR (Status)) {
+    //
+    // The VESA BIOS Extensions are not compatible with Graphics Output, so check for support
+    // for the standard 640x480 16 color VGA mode
+    //
+    DEBUG ((EFI_D_INFO, "VgaCompatible - %x\n", BiosVideoPrivate->VgaCompatible));
+    if (BiosVideoPrivate->VgaCompatible) {
+      if (FeaturePcdGet (PcdBiosVideoCheckVgaEnable)) {
+        Status = BiosVideoCheckForVga (BiosVideoPrivate);
+        DEBUG ((EFI_D_INFO, "BiosVideoCheckForVga - %r\n", Status));
+      } else {
+        Status = EFI_UNSUPPORTED;
+      }
+    }
+
+    if (EFI_ERROR (Status)) {
+      //
+      // Free GOP mode structure if it is not freed before
+      // VgaMiniPort does not need this structure any more
+      //
+      if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) {
+        if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) {
+          FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info);
+          BiosVideoPrivate->GraphicsOutput.Mode->Info = NULL;
+        }
+        FreePool (BiosVideoPrivate->GraphicsOutput.Mode);
+        BiosVideoPrivate->GraphicsOutput.Mode = NULL;
+      }
+
+      //
+      // Neither VBE nor the standard 640x480 16 color VGA mode are supported, so do
+      // not produce the Graphics Output protocol.  Instead, produce the VGA MiniPort Protocol.
+      //
+      BiosVideoPrivate->ProduceGraphicsOutput = FALSE;
+
+      //
+      // INT services are available, so on the 80x25 and 80x50 text mode are supported
+      //
+      BiosVideoPrivate->VgaMiniPort.MaxMode = 2;
+    }
+  }
+
+  ProtocolInstalled = FALSE;
+
+  if (BiosVideoPrivate->ProduceGraphicsOutput) {
+    //
+    // Creat child handle and install Graphics Output Protocol,EDID Discovered/Active Protocol
+    //
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &BiosVideoPrivate->Handle,
+                    &gEfiGraphicsOutputProtocolGuid,
+                    &BiosVideoPrivate->GraphicsOutput,
+                    &gEfiEdidDiscoveredProtocolGuid,
+                    &BiosVideoPrivate->EdidDiscovered,
+                    &gEfiEdidActiveProtocolGuid,
+                    &BiosVideoPrivate->EdidActive,
+                    NULL
+                    );
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // Open the Parent Handle for the child
+      //
+      Status = gBS->OpenProtocol (
+                      ParentHandle,
+                      &gEfiPciIoProtocolGuid,
+                      (VOID **) &BiosVideoPrivate->PciIo,
+                      This->DriverBindingHandle,
+                      BiosVideoPrivate->Handle,
+                      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                      );
+      if (EFI_ERROR (Status)) {
+        goto Done;
+      }
+      ProtocolInstalled = TRUE;
+    }
+  }
+
+  if (!ProtocolInstalled) {
+    //
+    // Install VGA Mini Port Protocol
+    //
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &ParentHandle,
+                    &gEfiVgaMiniPortProtocolGuid,
+                    &BiosVideoPrivate->VgaMiniPort,
+                    NULL
+                    );
+  }
+
+Done:
+  if (EFI_ERROR (Status)) {
+    if ((BiosVideoPrivate != NULL) && (BiosVideoPrivate->ExitBootServicesEvent != NULL)) {
+      gBS->CloseEvent (BiosVideoPrivate->ExitBootServicesEvent);
+    }
+    //
+    // Free private data structure
+    //
+    BiosVideoDeviceReleaseResource (BiosVideoPrivate);
+  }
+
+  return Status;
+}
+
+
+/**
+  Deregister an video child handle and free resources.
+
+  @param  This                   Protocol instance pointer.
+  @param  Controller             Video controller handle
+  @param  Handle                 Video child handle
+
+  @return EFI_STATUS
+
+**/
+EFI_STATUS
+BiosVideoChildHandleUninstall (
+  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  EFI_HANDLE                     Controller,
+  EFI_HANDLE                     Handle
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_IA32_REGISTER_SET        Regs;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+  EFI_VGA_MINI_PORT_PROTOCOL   *VgaMiniPort;
+  BIOS_VIDEO_DEV               *BiosVideoPrivate;
+  EFI_PCI_IO_PROTOCOL          *PciIo;
+
+  BiosVideoPrivate = NULL;
+  GraphicsOutput   = NULL;
+  PciIo            = NULL;
+  Status           = EFI_UNSUPPORTED;
+
+  Status = gBS->OpenProtocol (
+                  Handle,
+                  &gEfiGraphicsOutputProtocolGuid,
+                  (VOID **) &GraphicsOutput,
+                  This->DriverBindingHandle,
+                  Handle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (!EFI_ERROR (Status)) {
+      BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput);
+  }
+
+  if (EFI_ERROR (Status)) {
+    Status = gBS->OpenProtocol (
+                   Handle,
+                   &gEfiVgaMiniPortProtocolGuid,
+                   (VOID **) &VgaMiniPort,
+                   This->DriverBindingHandle,
+                   Handle,
+                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                   );
+    if (!EFI_ERROR (Status)) {
+      BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS (VgaMiniPort);
+    }
+  }
+
+  if (BiosVideoPrivate == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Set the 80x25 Text VGA Mode
+  //
+  Regs.H.AH = 0x00;
+  Regs.H.AL = 0x03;
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  Regs.H.AH = 0x11;
+  Regs.H.AL = 0x14;
+  Regs.H.BL = 0;
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  //
+  // Close PCI I/O protocol that opened by child handle
+  //
+  Status = gBS->CloseProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  This->DriverBindingHandle,
+                  Handle
+                  );
+
+  //
+  // Uninstall protocols on child handle
+  //
+  if (BiosVideoPrivate->ProduceGraphicsOutput) {
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    BiosVideoPrivate->Handle,
+                    &gEfiDevicePathProtocolGuid,
+                    BiosVideoPrivate->GopDevicePath,
+                    &gEfiGraphicsOutputProtocolGuid,
+                    &BiosVideoPrivate->GraphicsOutput,
+                    &gEfiEdidDiscoveredProtocolGuid,
+                    &BiosVideoPrivate->EdidDiscovered,
+                    &gEfiEdidActiveProtocolGuid,
+                    &BiosVideoPrivate->EdidActive,
+                    NULL
+                    );
+  }
+  if (!BiosVideoPrivate->ProduceGraphicsOutput) {
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    Controller,
+                    &gEfiVgaMiniPortProtocolGuid,
+                    &BiosVideoPrivate->VgaMiniPort,
+                    NULL
+                    );
+  }
+
+  if (EFI_ERROR (Status)) {
+    gBS->OpenProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           (VOID **) &PciIo,
+           This->DriverBindingHandle,
+           Handle,
+           EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+           );
+    return Status;
+  }
+
+  if (PcdGetBool (PcdBiosVideoSetTextVgaModeEnable)) {
+    //
+    // Close EXIT_BOOT_SERIVES Event
+    //
+    gBS->CloseEvent (BiosVideoPrivate->ExitBootServicesEvent);
+  }
+
+  //
+  // Release all allocated resources
+  //
+  BiosVideoDeviceReleaseResource (BiosVideoPrivate);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Release resource for biso video instance.
+
+  @param  BiosVideoPrivate       Video child device private data structure
+
+**/
+VOID
+BiosVideoDeviceReleaseResource (
+  BIOS_VIDEO_DEV  *BiosVideoPrivate
+  )
+{
+  if (BiosVideoPrivate == NULL) {
+    return ;
+  }
+
+  //
+  // Release all the resourses occupied by the BIOS_VIDEO_DEV
+  //
+
+  //
+  // Free VGA Frame Buffer
+  //
+  if (BiosVideoPrivate->VgaFrameBuffer != NULL) {
+    FreePool (BiosVideoPrivate->VgaFrameBuffer);
+  }
+  //
+  // Free VBE Frame Buffer
+  //
+  if (BiosVideoPrivate->VbeFrameBuffer != NULL) {
+    FreePool (BiosVideoPrivate->VbeFrameBuffer);
+  }
+  //
+  // Free line buffer
+  //
+  if (BiosVideoPrivate->LineBuffer != NULL) {
+    FreePool (BiosVideoPrivate->LineBuffer);
+  }
+  //
+  // Free mode data
+  //
+  if (BiosVideoPrivate->ModeData != NULL) {
+    FreePool (BiosVideoPrivate->ModeData);
+  }
+  //
+  // Free memory allocated below 1MB
+  //
+  if (BiosVideoPrivate->PagesBelow1MB != 0) {
+    gBS->FreePages (BiosVideoPrivate->PagesBelow1MB, BiosVideoPrivate->NumberOfPagesBelow1MB);
+  }
+
+  if (BiosVideoPrivate->VbeSaveRestorePages != 0) {
+    gBS->FreePages (BiosVideoPrivate->VbeSaveRestoreBuffer, BiosVideoPrivate->VbeSaveRestorePages);
+  }
+
+  //
+  // Free graphics output protocol occupied resource
+  //
+  if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) {
+    if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) {
+        FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info);
+        BiosVideoPrivate->GraphicsOutput.Mode->Info = NULL;
+    }
+    FreePool (BiosVideoPrivate->GraphicsOutput.Mode);
+    BiosVideoPrivate->GraphicsOutput.Mode = NULL;
+  }
+  //
+  // Free EDID discovered protocol occupied resource
+  //
+  if (BiosVideoPrivate->EdidDiscovered.Edid != NULL) {
+    FreePool (BiosVideoPrivate->EdidDiscovered.Edid);
+  }
+  //
+  // Free EDID active protocol occupied resource
+  //
+  if (BiosVideoPrivate->EdidActive.Edid != NULL) {
+    FreePool (BiosVideoPrivate->EdidActive.Edid);
+  }
+
+  if (BiosVideoPrivate->GopDevicePath!= NULL) {
+    FreePool (BiosVideoPrivate->GopDevicePath);
+  }
+
+  FreePool (BiosVideoPrivate);
+
+  return ;
+}
+
+
+/**
+  Generate a search key for a specified timing data.
+
+  @param  EdidTiming             Pointer to EDID timing
+
+  @return The 32 bit unique key for search.
+
+**/
+UINT32
+CalculateEdidKey (
+  VESA_BIOS_EXTENSIONS_EDID_TIMING       *EdidTiming
+  )
+{
+  UINT32 Key;
+
+  //
+  // Be sure no conflicts for all standard timing defined by VESA.
+  //
+  Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution;
+  return Key;
+}
+
+
+/**
+  Parse the Established Timing and Standard Timing in EDID data block.
+
+  @param  EdidBuffer             Pointer to EDID data block
+  @param  ValidEdidTiming        Valid EDID timing information
+
+  @retval TRUE                   The EDID data is valid.
+  @retval FALSE                  The EDID data is invalid.
+
+**/
+BOOLEAN
+ParseEdidData (
+  UINT8                                      *EdidBuffer,
+  VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING     *ValidEdidTiming
+  )
+{
+  UINT8  CheckSum;
+  UINT32 Index;
+  UINT32 ValidNumber;
+  UINT32 TimingBits;
+  UINT8  *BufferIndex;
+  UINT16 HorizontalResolution;
+  UINT16 VerticalResolution;
+  UINT8  AspectRatio;
+  UINT8  RefreshRate;
+  VESA_BIOS_EXTENSIONS_EDID_TIMING     TempTiming;
+  VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *EdidDataBlock;
+
+  EdidDataBlock = (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *) EdidBuffer;
+
+  //
+  // Check the checksum of EDID data
+  //
+  CheckSum = 0;
+  for (Index = 0; Index < VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE; Index ++) {
+    CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]);
+  }
+  if (CheckSum != 0) {
+    return FALSE;
+  }
+
+  ValidNumber = 0;
+  gBS->SetMem (ValidEdidTiming, sizeof (VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING), 0);
+
+  if ((EdidDataBlock->EstablishedTimings[0] != 0) ||
+      (EdidDataBlock->EstablishedTimings[1] != 0) ||
+      (EdidDataBlock->EstablishedTimings[2] != 0)
+      ) {
+    //
+    // Established timing data
+    //
+    TimingBits = EdidDataBlock->EstablishedTimings[0] |
+                 (EdidDataBlock->EstablishedTimings[1] << 8) |
+                 ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ;
+    for (Index = 0; Index < VESA_BIOS_EXTENSIONS_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) {
+      if ((TimingBits & 0x1) != 0) {
+        DEBUG ((EFI_D_INFO, "Established Timing: %d x %d\n",
+        mEstablishedEdidTiming[Index].HorizontalResolution, mEstablishedEdidTiming[Index].VerticalResolution));
+        ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mEstablishedEdidTiming[Index]);
+        ValidNumber ++;
+      }
+      TimingBits = TimingBits >> 1;
+    }
+  }
+
+  //
+  // Parse the standard timing data
+  //
+  BufferIndex = &EdidDataBlock->StandardTimingIdentification[0];
+  for (Index = 0; Index < 8; Index ++) {
+    //
+    // Check if this is a valid Standard Timing entry
+    // VESA documents unused fields should be set to 01h
+    //
+    if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){
+      //
+      // A valid Standard Timing
+      //
+      HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248);
+      AspectRatio = (UINT8) (BufferIndex[1] >> 6);
+      switch (AspectRatio) {
+        case 0:
+          VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10);
+          break;
+        case 1:
+          VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
+          break;
+        case 2:
+          VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4);
+          break;
+        case 3:
+          VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9);
+          break;
+        default:
+          VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
+          break;
+      }
+      RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60);
+      DEBUG ((EFI_D_INFO, "Standard Timing: %d x %d\n", HorizontalResolution, VerticalResolution));
+      TempTiming.HorizontalResolution = HorizontalResolution;
+      TempTiming.VerticalResolution = VerticalResolution;
+      TempTiming.RefreshRate = RefreshRate;
+      ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming);
+      ValidNumber ++;
+    }
+    BufferIndex += 2;
+  }
+
+  //
+  // Parse the Detailed Timing data
+  //
+  BufferIndex = &EdidDataBlock->DetailedTimingDescriptions[0];
+  for (Index = 0; Index < 4; Index ++, BufferIndex += VESA_BIOS_EXTENSIONS_DETAILED_TIMING_EACH_DESCRIPTOR_SIZE) {
+    if ((BufferIndex[0] == 0x0) && (BufferIndex[1] == 0x0)) {
+      //
+      // Check if this is a valid Detailed Timing Descriptor
+      // If first 2 bytes are zero, it is monitor descriptor other than detailed timing descriptor
+      //
+      continue;
+    }
+    //
+    // Calculate Horizontal and Vertical resolution
+    //
+    TempTiming.HorizontalResolution = ((UINT16)(BufferIndex[4] & 0xF0) << 4) | (BufferIndex[2]);
+    TempTiming.VerticalResolution = ((UINT16)(BufferIndex[7] & 0xF0) << 4) | (BufferIndex[5]);
+    DEBUG ((EFI_D_INFO, "Detailed Timing %d: %d x %d\n",
+            Index, TempTiming.HorizontalResolution, TempTiming.VerticalResolution));
+    ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming);
+    ValidNumber ++;
+  }
+
+  ValidEdidTiming->ValidNumber = ValidNumber;
+  return TRUE;
+}
+
+
+/**
+  Search a specified Timing in all the valid EDID timings.
+
+  @param  ValidEdidTiming        All valid EDID timing information.
+  @param  EdidTiming             The Timing to search for.
+
+  @retval TRUE                   Found.
+  @retval FALSE                  Not found.
+
+**/
+BOOLEAN
+SearchEdidTiming (
+  VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING *ValidEdidTiming,
+  VESA_BIOS_EXTENSIONS_EDID_TIMING       *EdidTiming
+  )
+{
+  UINT32 Index;
+  UINT32 Key;
+
+  Key = CalculateEdidKey (EdidTiming);
+
+  for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) {
+    if (Key == ValidEdidTiming->Key[Index]) {
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  Check if all video child handles have been uninstalled.
+
+  @param  Controller             Video controller handle
+
+  @return TRUE                   Child handles exist.
+  @return FALSE                  All video child handles have been uninstalled.
+
+**/
+BOOLEAN
+HasChildHandle (
+  IN EFI_HANDLE  Controller
+  )
+{
+  UINTN                                Index;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfoBuffer;
+  UINTN                                EntryCount;
+  BOOLEAN                              HasChild;
+
+  EntryCount = 0;
+  HasChild   = FALSE;
+  gBS->OpenProtocolInformation (
+         Controller,
+         &gEfiPciIoProtocolGuid,
+         &OpenInfoBuffer,
+         &EntryCount
+         );
+  for (Index = 0; Index < EntryCount; Index++) {
+    if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+      HasChild = TRUE;
+    }
+  }
+
+  return HasChild;
+}
+
+/**
+  Check for VBE device.
+
+  @param  BiosVideoPrivate       Pointer to BIOS_VIDEO_DEV structure
+
+  @retval EFI_SUCCESS            VBE device found
+
+**/
+EFI_STATUS
+BiosVideoCheckForVbe (
+  IN OUT BIOS_VIDEO_DEV  *BiosVideoPrivate
+  )
+{
+  EFI_STATUS                             Status;
+  EFI_IA32_REGISTER_SET                  Regs;
+  UINT16                                 *ModeNumberPtr;
+  UINT16                                 VbeModeNumber;
+  BOOLEAN                                ModeFound;
+  BOOLEAN                                EdidFound;
+  BIOS_VIDEO_MODE_DATA                   *ModeBuffer;
+  BIOS_VIDEO_MODE_DATA                   *CurrentModeData;
+  UINTN                                  PreferMode;
+  UINTN                                  ModeNumber;
+  VESA_BIOS_EXTENSIONS_EDID_TIMING       Timing;
+  VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING ValidEdidTiming;
+  EFI_EDID_OVERRIDE_PROTOCOL             *EdidOverride;
+  UINT32                                 EdidAttributes;
+  BOOLEAN                                EdidOverrideFound;
+  UINTN                                  EdidOverrideDataSize;
+  UINT8                                  *EdidOverrideDataBlock;
+  UINTN                                  EdidActiveDataSize;
+  UINT8                                  *EdidActiveDataBlock;
+  UINT32                                 HighestHorizontalResolution;
+  UINT32                                 HighestVerticalResolution;
+  UINTN                                  HighestResolutionMode;
+
+  EdidFound             = TRUE;
+  EdidOverrideFound     = FALSE;
+  EdidOverrideDataBlock = NULL;
+  EdidActiveDataSize    = 0;
+  EdidActiveDataBlock   = NULL;
+  HighestHorizontalResolution = 0;
+  HighestVerticalResolution   = 0;
+  HighestResolutionMode       = 0;
+
+  //
+  // Allocate buffer under 1MB for VBE data structures
+  //
+  BiosVideoPrivate->NumberOfPagesBelow1MB = EFI_SIZE_TO_PAGES (
+                                              sizeof (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK) +
+                                              sizeof (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK) +
+                                              sizeof (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK) +
+                                              sizeof (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK)
+                                              );
+
+  BiosVideoPrivate->PagesBelow1MB = 0x00100000 - 1;
+
+  Status = gBS->AllocatePages (
+                  AllocateMaxAddress,
+                  EfiBootServicesData,
+                  BiosVideoPrivate->NumberOfPagesBelow1MB,
+                  &BiosVideoPrivate->PagesBelow1MB
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  ZeroMem (&ValidEdidTiming, sizeof (VESA_BIOS_EXTENSIONS_VALID_EDID_TIMING));
+
+  //
+  // Fill in the VBE related data structures
+  //
+  BiosVideoPrivate->VbeInformationBlock = (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK *) (UINTN) (BiosVideoPrivate->PagesBelow1MB);
+  BiosVideoPrivate->VbeModeInformationBlock = (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK *) (BiosVideoPrivate->VbeInformationBlock + 1);
+  BiosVideoPrivate->VbeEdidDataBlock = (VESA_BIOS_EXTENSIONS_EDID_DATA_BLOCK *) (BiosVideoPrivate->VbeModeInformationBlock + 1);
+  BiosVideoPrivate->VbeCrtcInformationBlock = (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK *) (BiosVideoPrivate->VbeEdidDataBlock + 1);
+  BiosVideoPrivate->VbeSaveRestorePages   = 0;
+  BiosVideoPrivate->VbeSaveRestoreBuffer  = 0;
+
+  //
+  // Test to see if the Video Adapter is compliant with VBE 3.0
+  //
+  gBS->SetMem (&Regs, sizeof (Regs), 0);
+  Regs.X.AX = VESA_BIOS_EXTENSIONS_RETURN_CONTROLLER_INFORMATION;
+  gBS->SetMem (BiosVideoPrivate->VbeInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_INFORMATION_BLOCK), 0);
+  BiosVideoPrivate->VbeInformationBlock->VESASignature  = VESA_BIOS_EXTENSIONS_VBE2_SIGNATURE;
+  Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeInformationBlock);
+  Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeInformationBlock);
+
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  Status = EFI_DEVICE_ERROR;
+
+  //
+  // See if the VESA call succeeded
+  //
+  if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) {
+    return Status;
+  }
+  //
+  // Check for 'VESA' signature
+  //
+  if (BiosVideoPrivate->VbeInformationBlock->VESASignature != VESA_BIOS_EXTENSIONS_VESA_SIGNATURE) {
+    return Status;
+  }
+  //
+  // Check to see if this is VBE 2.0 or higher
+  //
+  if (BiosVideoPrivate->VbeInformationBlock->VESAVersion < VESA_BIOS_EXTENSIONS_VERSION_2_0) {
+    return Status;
+  }
+
+  EdidFound            = FALSE;
+  EdidAttributes       = 0xff;
+  EdidOverrideDataSize = 0;
+
+  //
+  // Find EDID Override protocol firstly, this protocol is installed by platform if needed.
+  //
+  Status = gBS->LocateProtocol (
+                   &gEfiEdidOverrideProtocolGuid,
+                   NULL,
+                   (VOID **) &EdidOverride
+                   );
+  if (!EFI_ERROR (Status)) {
+    //
+    // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow
+    //
+    EdidOverrideDataBlock = AllocatePool (VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE * 2);
+    if (NULL == EdidOverrideDataBlock) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Done;
+    }
+
+    Status = EdidOverride->GetEdid (
+                             EdidOverride,
+                             BiosVideoPrivate->Handle,
+                             &EdidAttributes,
+                             &EdidOverrideDataSize,
+                             (UINT8 **) &EdidOverrideDataBlock
+                             );
+    if (!EFI_ERROR (Status)  &&
+         EdidAttributes == 0 &&
+         EdidOverrideDataSize != 0) {
+      //
+      // Succeeded to get EDID Override Data
+      //
+      EdidOverrideFound = TRUE;
+    }
+  }
+
+  if (!EdidOverrideFound || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) {
+    //
+    // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned,
+    // read EDID information through INT10 call
+    //
+
+    gBS->SetMem (&Regs, sizeof (Regs), 0);
+    Regs.X.AX = VESA_BIOS_EXTENSIONS_EDID;
+    Regs.X.BX = 1;
+    Regs.X.CX = 0;
+    Regs.X.DX = 0;
+    Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeEdidDataBlock);
+    Regs.X.DI = EFI_OFFSET  ((UINTN) BiosVideoPrivate->VbeEdidDataBlock);
+
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+    //
+    // See if the VESA call succeeded
+    //
+    if (Regs.X.AX == VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) {
+      //
+      // Set EDID Discovered Data
+      //
+      BiosVideoPrivate->EdidDiscovered.SizeOfEdid = VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE;
+       BiosVideoPrivate->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool (
+                                                          VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE,
+                                                          BiosVideoPrivate->VbeEdidDataBlock
+                                                           );
+
+      if (NULL == BiosVideoPrivate->EdidDiscovered.Edid) {
+         Status = EFI_OUT_OF_RESOURCES;
+        goto Done;
+      }
+
+      EdidFound = TRUE;
+    }
+  }
+
+  if (EdidFound) {
+    EdidActiveDataSize  = VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE;
+    EdidActiveDataBlock = BiosVideoPrivate->EdidDiscovered.Edid;
+  } else if (EdidOverrideFound) {
+    EdidActiveDataSize  = EdidOverrideDataSize;
+    EdidActiveDataBlock = EdidOverrideDataBlock;
+    EdidFound = TRUE;
+   }
+
+   if (EdidFound) {
+    //
+    // Parse EDID data structure to retrieve modes supported by monitor
+    //
+    if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming)) {
+      //
+      // Copy EDID Override Data to EDID Active Data
+      //
+      BiosVideoPrivate->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize;
+      BiosVideoPrivate->EdidActive.Edid = (UINT8 *) AllocateCopyPool (
+                                                      EdidActiveDataSize,
+                                                      EdidActiveDataBlock
+                                                      );
+      if (NULL ==  BiosVideoPrivate->EdidActive.Edid) {
+         Status = EFI_OUT_OF_RESOURCES;
+        goto Done;
+      }
+    }
+  } else {
+    BiosVideoPrivate->EdidActive.SizeOfEdid = 0;
+    BiosVideoPrivate->EdidActive.Edid = NULL;
+    EdidFound = FALSE;
+  }
+
+  //
+  // Walk through the mode list to see if there is at least one mode the is compatible with the EDID mode
+  //
+  ModeNumberPtr = (UINT16 *)
+    (
+      (((UINTN) BiosVideoPrivate->VbeInformationBlock->VideoModePtr & 0xffff0000) >> 12) |
+        ((UINTN) BiosVideoPrivate->VbeInformationBlock->VideoModePtr & 0x0000ffff)
+    );
+
+  PreferMode = 0;
+  ModeNumber = 0;
+
+  //
+  // ModeNumberPtr may be not 16-byte aligned, so ReadUnaligned16 is used to access the buffer pointed by ModeNumberPtr.
+  //
+  for (VbeModeNumber = ReadUnaligned16 (ModeNumberPtr);
+       VbeModeNumber != VESA_BIOS_EXTENSIONS_END_OF_MODE_LIST;
+       VbeModeNumber = ReadUnaligned16 (++ModeNumberPtr)) {
+    //
+    // Make sure this is a mode number defined by the VESA VBE specification.  If it isn'tm then skip this mode number.
+    //
+    if ((VbeModeNumber & VESA_BIOS_EXTENSIONS_MODE_NUMBER_VESA) == 0) {
+      continue;
+    }
+    //
+    // Get the information about the mode
+    //
+    gBS->SetMem (&Regs, sizeof (Regs), 0);
+    Regs.X.AX = VESA_BIOS_EXTENSIONS_RETURN_MODE_INFORMATION;
+    Regs.X.CX = VbeModeNumber;
+    gBS->SetMem (BiosVideoPrivate->VbeModeInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_MODE_INFORMATION_BLOCK), 0);
+    Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeModeInformationBlock);
+    Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeModeInformationBlock);
+
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+    //
+    // See if the call succeeded.  If it didn't, then try the next mode.
+    //
+    if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) {
+      continue;
+    }
+    //
+    // See if the mode supports color.  If it doesn't then try the next mode.
+    //
+    if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_COLOR) == 0) {
+      continue;
+    }
+    //
+    // See if the mode supports graphics.  If it doesn't then try the next mode.
+    //
+    if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_GRAPHICS) == 0) {
+      continue;
+    }
+    //
+    // See if the mode supports a linear frame buffer.  If it doesn't then try the next mode.
+    //
+    if ((BiosVideoPrivate->VbeModeInformationBlock->ModeAttributes & VESA_BIOS_EXTENSIONS_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER) == 0) {
+      continue;
+    }
+    //
+    // See if the mode supports 32 bit color.  If it doesn't then try the next mode.
+    // 32 bit mode can be implemented by 24 Bits Per Pixels. Also make sure the
+    // number of bits per pixel is a multiple of 8 or more than 32 bits per pixel
+    //
+    if (BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel < 24) {
+      continue;
+    }
+
+    if (BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel > 32) {
+      continue;
+    }
+
+    if ((BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel % 8) != 0) {
+      continue;
+    }
+    //
+    // See if the physical base pointer for the linear mode is valid.  If it isn't then try the next mode.
+    //
+    if (BiosVideoPrivate->VbeModeInformationBlock->PhysBasePtr == 0) {
+      continue;
+    }
+
+    DEBUG ((EFI_D_INFO, "Video Controller Mode 0x%x: %d x %d\n",
+            VbeModeNumber, BiosVideoPrivate->VbeModeInformationBlock->XResolution, BiosVideoPrivate->VbeModeInformationBlock->YResolution));
+
+    if (EdidFound && (ValidEdidTiming.ValidNumber > 0)) {
+      //
+      // EDID exist, check whether this mode match with any mode in EDID
+      //
+      Timing.HorizontalResolution = BiosVideoPrivate->VbeModeInformationBlock->XResolution;
+      Timing.VerticalResolution = BiosVideoPrivate->VbeModeInformationBlock->YResolution;
+      if (!SearchEdidTiming (&ValidEdidTiming, &Timing)) {
+        //
+        // When EDID comes from INT10 call, EDID does not include 800x600, 640x480 and 1024x768,
+        // but INT10 can support these modes, we add them into GOP mode.
+        //
+        if ((BiosVideoPrivate->EdidDiscovered.SizeOfEdid != 0) &&
+            !((Timing.HorizontalResolution) == 1024 && (Timing.VerticalResolution == 768)) &&
+            !((Timing.HorizontalResolution) == 800 && (Timing.VerticalResolution == 600)) &&
+            !((Timing.HorizontalResolution) == 640 && (Timing.VerticalResolution == 480))) {
+        continue;
+        }
+      }
+    }
+
+    //
+    // Select a reasonable mode to be set for current display mode
+    //
+    ModeFound = FALSE;
+
+    if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 1024 &&
+        BiosVideoPrivate->VbeModeInformationBlock->YResolution == 768
+        ) {
+      ModeFound = TRUE;
+    }
+    if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 800 &&
+        BiosVideoPrivate->VbeModeInformationBlock->YResolution == 600
+        ) {
+      ModeFound = TRUE;
+      PreferMode = ModeNumber;
+    }
+    if (BiosVideoPrivate->VbeModeInformationBlock->XResolution == 640 &&
+        BiosVideoPrivate->VbeModeInformationBlock->YResolution == 480
+        ) {
+      ModeFound = TRUE;
+    }
+
+    if ((!EdidFound) && (!ModeFound)) {
+      //
+      // When no EDID exist, only select three possible resolutions, i.e. 1024x768, 800x600, 640x480
+      //
+      continue;
+    }
+
+    //
+    // Record the highest resolution mode to set later
+    //
+    if ((BiosVideoPrivate->VbeModeInformationBlock->XResolution > HighestHorizontalResolution) ||
+        ((BiosVideoPrivate->VbeModeInformationBlock->XResolution == HighestHorizontalResolution) &&
+         (BiosVideoPrivate->VbeModeInformationBlock->YResolution > HighestVerticalResolution))) {
+      HighestHorizontalResolution = BiosVideoPrivate->VbeModeInformationBlock->XResolution;
+      HighestVerticalResolution = BiosVideoPrivate->VbeModeInformationBlock->YResolution;
+      HighestResolutionMode = ModeNumber;
+    }
+
+    //
+    // Add mode to the list of available modes
+    //
+    ModeNumber ++;
+    ModeBuffer = (BIOS_VIDEO_MODE_DATA *) AllocatePool (
+                                            ModeNumber * sizeof (BIOS_VIDEO_MODE_DATA)
+                                            );
+    if (NULL == ModeBuffer) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Done;
+    }
+
+    if (ModeNumber > 1) {
+      CopyMem (
+        ModeBuffer,
+        BiosVideoPrivate->ModeData,
+        (ModeNumber - 1) * sizeof (BIOS_VIDEO_MODE_DATA)
+        );
+    }
+
+    if (BiosVideoPrivate->ModeData != NULL) {
+      FreePool (BiosVideoPrivate->ModeData);
+    }
+
+    CurrentModeData = &ModeBuffer[ModeNumber - 1];
+    CurrentModeData->VbeModeNumber = VbeModeNumber;
+    if (BiosVideoPrivate->VbeInformationBlock->VESAVersion >= VESA_BIOS_EXTENSIONS_VERSION_3_0) {
+      CurrentModeData->BytesPerScanLine = BiosVideoPrivate->VbeModeInformationBlock->LinBytesPerScanLine;
+      CurrentModeData->Red.Position = BiosVideoPrivate->VbeModeInformationBlock->LinRedFieldPosition;
+      CurrentModeData->Red.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinRedMaskSize) - 1);
+      CurrentModeData->Blue.Position = BiosVideoPrivate->VbeModeInformationBlock->LinBlueFieldPosition;
+      CurrentModeData->Blue.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinBlueMaskSize) - 1);
+      CurrentModeData->Green.Position = BiosVideoPrivate->VbeModeInformationBlock->LinGreenFieldPosition;
+      CurrentModeData->Green.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinGreenMaskSize) - 1);
+      CurrentModeData->Reserved.Position = BiosVideoPrivate->VbeModeInformationBlock->LinRsvdFieldPosition;
+      CurrentModeData->Reserved.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->LinRsvdMaskSize) - 1);
+    } else {
+      CurrentModeData->BytesPerScanLine = BiosVideoPrivate->VbeModeInformationBlock->BytesPerScanLine;
+      CurrentModeData->Red.Position = BiosVideoPrivate->VbeModeInformationBlock->RedFieldPosition;
+      CurrentModeData->Red.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->RedMaskSize) - 1);
+      CurrentModeData->Blue.Position = BiosVideoPrivate->VbeModeInformationBlock->BlueFieldPosition;
+      CurrentModeData->Blue.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->BlueMaskSize) - 1);
+      CurrentModeData->Green.Position = BiosVideoPrivate->VbeModeInformationBlock->GreenFieldPosition;
+      CurrentModeData->Green.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->GreenMaskSize) - 1);
+      CurrentModeData->Reserved.Position = BiosVideoPrivate->VbeModeInformationBlock->RsvdFieldPosition;
+      CurrentModeData->Reserved.Mask = (UINT8) ((1 << BiosVideoPrivate->VbeModeInformationBlock->RsvdMaskSize) - 1);
+    }
+
+    CurrentModeData->PixelFormat = PixelBitMask;
+    if ((BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel == 32) &&
+        (CurrentModeData->Red.Mask == 0xff) && (CurrentModeData->Green.Mask == 0xff) && (CurrentModeData->Blue.Mask == 0xff)) {
+      if ((CurrentModeData->Red.Position == 0) && (CurrentModeData->Green.Position == 8) && (CurrentModeData->Blue.Position == 16)) {
+        CurrentModeData->PixelFormat = PixelRedGreenBlueReserved8BitPerColor;
+      } else if ((CurrentModeData->Blue.Position == 0) && (CurrentModeData->Green.Position == 8) && (CurrentModeData->Red.Position == 16)) {
+        CurrentModeData->PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
+      }
+    }
+
+    CurrentModeData->PixelBitMask.RedMask = ((UINT32) CurrentModeData->Red.Mask) << CurrentModeData->Red.Position;
+    CurrentModeData->PixelBitMask.GreenMask = ((UINT32) CurrentModeData->Green.Mask) << CurrentModeData->Green.Position;
+    CurrentModeData->PixelBitMask.BlueMask = ((UINT32) CurrentModeData->Blue.Mask) << CurrentModeData->Blue.Position;
+    CurrentModeData->PixelBitMask.ReservedMask = ((UINT32) CurrentModeData->Reserved.Mask) << CurrentModeData->Reserved.Position;
+
+    CurrentModeData->LinearFrameBuffer = (VOID *) (UINTN)BiosVideoPrivate->VbeModeInformationBlock->PhysBasePtr;
+    CurrentModeData->HorizontalResolution = BiosVideoPrivate->VbeModeInformationBlock->XResolution;
+    CurrentModeData->VerticalResolution = BiosVideoPrivate->VbeModeInformationBlock->YResolution;
+
+    CurrentModeData->BitsPerPixel  = BiosVideoPrivate->VbeModeInformationBlock->BitsPerPixel;
+    CurrentModeData->FrameBufferSize = CurrentModeData->BytesPerScanLine * CurrentModeData->VerticalResolution;
+    //
+    // Make sure the FrameBufferSize does not exceed the max available frame buffer size reported by VEB.
+    //
+    ASSERT (CurrentModeData->FrameBufferSize <= ((UINT32)BiosVideoPrivate->VbeInformationBlock->TotalMemory * 64 * 1024));
+
+    BiosVideoPrivate->ModeData = ModeBuffer;
+  }
+  //
+  // Check to see if we found any modes that are compatible with GRAPHICS OUTPUT
+  //
+  if (ModeNumber == 0) {
+    Status = EFI_DEVICE_ERROR;
+    goto Done;
+  }
+
+  //
+  // Assign Gop's Blt function
+  //
+  BiosVideoPrivate->GraphicsOutput.Blt     = BiosVideoGraphicsOutputVbeBlt;
+
+  BiosVideoPrivate->GraphicsOutput.Mode->MaxMode = (UINT32) ModeNumber;
+  //
+  // Current mode is unknow till now, set it to an invalid mode.
+  //
+  BiosVideoPrivate->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;
+
+  //
+  // Find the best mode to initialize
+  //
+  if ((PcdGet32 (PcdVideoHorizontalResolution) == 0x0) || (PcdGet32 (PcdVideoVerticalResolution) == 0x0)) {
+    DEBUG_CODE (
+      BIOS_VIDEO_MODE_DATA    *ModeData;
+      ModeData = &BiosVideoPrivate->ModeData[HighestResolutionMode];
+      DEBUG ((EFI_D_INFO, "BiosVideo set highest resolution %d x %d\n",
+              ModeData->HorizontalResolution, ModeData->VerticalResolution));
+    );
+    PreferMode = HighestResolutionMode;
+  }
+  Status = BiosVideoGraphicsOutputSetMode (&BiosVideoPrivate->GraphicsOutput, (UINT32) PreferMode);
+  if (EFI_ERROR (Status)) {
+    for (PreferMode = 0; PreferMode < ModeNumber; PreferMode ++) {
+      Status = BiosVideoGraphicsOutputSetMode (
+                &BiosVideoPrivate->GraphicsOutput,
+                (UINT32) PreferMode
+                );
+      if (!EFI_ERROR (Status)) {
+        break;
+      }
+    }
+    if (PreferMode == ModeNumber) {
+      //
+      // None mode is set successfully.
+      //
+      goto Done;
+    }
+  }
+
+Done:
+  //
+  // If there was an error, then free the mode structure
+  //
+  if (EFI_ERROR (Status)) {
+    if (BiosVideoPrivate->ModeData != NULL) {
+      FreePool (BiosVideoPrivate->ModeData);
+      BiosVideoPrivate->ModeData  = NULL;
+      BiosVideoPrivate->MaxMode   = 0;
+    }
+    if (EdidOverrideDataBlock != NULL) {
+      FreePool (EdidOverrideDataBlock);
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  Check for VGA device.
+
+  @param  BiosVideoPrivate       Pointer to BIOS_VIDEO_DEV structure
+
+  @retval EFI_SUCCESS            Standard VGA device found
+
+**/
+EFI_STATUS
+BiosVideoCheckForVga (
+  IN OUT BIOS_VIDEO_DEV  *BiosVideoPrivate
+  )
+{
+  EFI_STATUS            Status;
+  BIOS_VIDEO_MODE_DATA  *ModeBuffer;
+
+  Status = EFI_UNSUPPORTED;
+
+  //
+  // Assign Gop's Blt function
+  //
+  BiosVideoPrivate->GraphicsOutput.Blt     = BiosVideoGraphicsOutputVgaBlt;
+
+  //
+  // Add mode to the list of available modes
+  // caller should guarantee that Mode has been allocated.
+  //
+  ASSERT (BiosVideoPrivate->GraphicsOutput.Mode != NULL);
+  BiosVideoPrivate->GraphicsOutput.Mode->MaxMode = 1;
+
+  ModeBuffer = (BIOS_VIDEO_MODE_DATA *) AllocatePool (
+                                          sizeof (BIOS_VIDEO_MODE_DATA)
+                                          );
+  if (NULL == ModeBuffer) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  ModeBuffer->VbeModeNumber         = 0x0012;
+  ModeBuffer->BytesPerScanLine      = 640;
+  ModeBuffer->LinearFrameBuffer     = (VOID *) (UINTN) (0xa0000);
+  ModeBuffer->HorizontalResolution  = 640;
+  ModeBuffer->VerticalResolution    = 480;
+  ModeBuffer->PixelFormat           = PixelBltOnly;
+  ModeBuffer->BitsPerPixel          = 8;
+  ModeBuffer->ColorDepth            = 32;
+  ModeBuffer->RefreshRate           = 60;
+
+  BiosVideoPrivate->ModeData = ModeBuffer;
+
+  //
+  // Test to see if the Video Adapter support the 640x480 16 color mode
+  //
+  BiosVideoPrivate->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;
+  Status = BiosVideoGraphicsOutputSetMode (&BiosVideoPrivate->GraphicsOutput, 0);
+
+Done:
+  //
+  // If there was an error, then free the mode structure
+  //
+  if (EFI_ERROR (Status)) {
+    if (BiosVideoPrivate->ModeData != NULL) {
+      FreePool (BiosVideoPrivate->ModeData);
+      BiosVideoPrivate->ModeData = NULL;
+    }
+    if (BiosVideoPrivate->GraphicsOutput.Mode != NULL) {
+      if (BiosVideoPrivate->GraphicsOutput.Mode->Info != NULL) {
+        FreePool (BiosVideoPrivate->GraphicsOutput.Mode->Info);
+        BiosVideoPrivate->GraphicsOutput.Mode->Info = NULL;
+      }
+      FreePool (BiosVideoPrivate->GraphicsOutput.Mode);
+      BiosVideoPrivate->GraphicsOutput.Mode = NULL;
+    }
+  }
+  return Status;
+}
+
+//
+// Graphics Output Protocol Member Functions for VESA BIOS Extensions
+//
+
+/**
+  Graphics Output protocol interface to get video mode.
+
+  @param  This                   Protocol instance pointer.
+  @param  ModeNumber             The mode number to return information on.
+  @param  SizeOfInfo             A pointer to the size, in bytes, of the Info
+                                 buffer.
+  @param  Info                   Caller allocated buffer that returns information
+                                 about ModeNumber.
+
+  @retval EFI_SUCCESS            Mode information returned.
+  @retval EFI_DEVICE_ERROR       A hardware error occurred trying to retrieve the
+                                 video mode.
+  @retval EFI_NOT_STARTED        Video display is not initialized. Call SetMode ()
+  @retval EFI_INVALID_PARAMETER  One of the input args was NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputQueryMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+  IN  UINT32                                ModeNumber,
+  OUT UINTN                                 *SizeOfInfo,
+  OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
+  )
+{
+  BIOS_VIDEO_DEV        *BiosVideoPrivate;
+  BIOS_VIDEO_MODE_DATA  *ModeData;
+
+  BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  if (BiosVideoPrivate->HardwareNeedsStarting) {
+    REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,
+      EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_OUTPUT_ERROR,
+      BiosVideoPrivate->GopDevicePath
+      );
+    return EFI_NOT_STARTED;
+  }
+
+  if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *) AllocatePool (
+                                                    sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)
+                                                    );
+  if (NULL == *Info) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+
+  ModeData = &BiosVideoPrivate->ModeData[ModeNumber];
+  (*Info)->Version = 0;
+  (*Info)->HorizontalResolution = ModeData->HorizontalResolution;
+  (*Info)->VerticalResolution   = ModeData->VerticalResolution;
+  (*Info)->PixelFormat = ModeData->PixelFormat;
+  CopyMem (&((*Info)->PixelInformation), &(ModeData->PixelBitMask), sizeof(ModeData->PixelBitMask));
+
+  (*Info)->PixelsPerScanLine =  (ModeData->BytesPerScanLine * 8) / ModeData->BitsPerPixel;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Worker function to set video mode.
+
+  @param  BiosVideoPrivate       Instance of BIOS_VIDEO_DEV.
+  @param  ModeData               The mode data to be set.
+  @param  DevicePath             Pointer to Device Path Protocol.
+
+  @retval EFI_SUCCESS            Graphics mode was changed.
+  @retval EFI_DEVICE_ERROR       The device had an error and could not complete the
+                                 request.
+  @retval EFI_UNSUPPORTED        ModeNumber is not supported by this device.
+
+**/
+EFI_STATUS
+BiosVideoSetModeWorker (
+  IN  BIOS_VIDEO_DEV               *BiosVideoPrivate,
+  IN  BIOS_VIDEO_MODE_DATA         *ModeData,
+  IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
+  )
+{
+  EFI_STATUS              Status;
+  EFI_IA32_REGISTER_SET   Regs;
+
+  if (BiosVideoPrivate->LineBuffer != NULL) {
+    FreePool (BiosVideoPrivate->LineBuffer);
+  }
+
+  if (BiosVideoPrivate->VgaFrameBuffer != NULL) {
+    FreePool (BiosVideoPrivate->VgaFrameBuffer);
+  }
+
+  if (BiosVideoPrivate->VbeFrameBuffer != NULL) {
+    FreePool (BiosVideoPrivate->VbeFrameBuffer);
+  }
+
+  BiosVideoPrivate->LineBuffer = (UINT8 *) AllocatePool (
+                                             ModeData->BytesPerScanLine
+                                             );
+  if (NULL == BiosVideoPrivate->LineBuffer) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  //
+  // Clear all registers
+  //
+  ZeroMem (&Regs, sizeof (Regs));
+
+  if (ModeData->VbeModeNumber < 0x100) {
+    //
+    // Allocate a working buffer for BLT operations to the VGA frame buffer
+    //
+    BiosVideoPrivate->VgaFrameBuffer = (UINT8 *) AllocatePool (4 * 480 * 80);
+    if (NULL == BiosVideoPrivate->VgaFrameBuffer) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    //
+    // Set VGA Mode
+    //
+    Regs.X.AX = ModeData->VbeModeNumber;
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  } else {
+    //
+    // Allocate a working buffer for BLT operations to the VBE frame buffer
+    //
+    BiosVideoPrivate->VbeFrameBuffer =
+      (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocatePool (
+                                          ModeData->BytesPerScanLine * ModeData->VerticalResolution
+                                          );
+    if (NULL == BiosVideoPrivate->VbeFrameBuffer) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    //
+    // Set VBE mode
+    //
+    Regs.X.AX = VESA_BIOS_EXTENSIONS_SET_MODE;
+    Regs.X.BX = (UINT16) (ModeData->VbeModeNumber | VESA_BIOS_EXTENSIONS_MODE_NUMBER_LINEAR_FRAME_BUFFER);
+    ZeroMem (BiosVideoPrivate->VbeCrtcInformationBlock, sizeof (VESA_BIOS_EXTENSIONS_CRTC_INFORMATION_BLOCK));
+    Regs.X.ES = EFI_SEGMENT ((UINTN) BiosVideoPrivate->VbeCrtcInformationBlock);
+    Regs.X.DI = EFI_OFFSET ((UINTN) BiosVideoPrivate->VbeCrtcInformationBlock);
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+    //
+    // Check to see if the call succeeded
+    //
+    if (Regs.X.AX != VESA_BIOS_EXTENSIONS_STATUS_SUCCESS) {
+      REPORT_STATUS_CODE_WITH_DEVICE_PATH (
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,
+        EFI_PERIPHERAL_LOCAL_CONSOLE | EFI_P_EC_OUTPUT_ERROR,
+        DevicePath
+        );
+      return EFI_DEVICE_ERROR;
+    }
+    //
+    // Initialize the state of the VbeFrameBuffer
+    //
+    Status = BiosVideoPrivate->PciIo->Mem.Read (
+                                            BiosVideoPrivate->PciIo,
+                                            EfiPciIoWidthUint32,
+                                            EFI_PCI_IO_PASS_THROUGH_BAR,
+                                            (UINT64) (UINTN) ModeData->LinearFrameBuffer,
+                                            (ModeData->BytesPerScanLine * ModeData->VerticalResolution) >> 2,
+                                            BiosVideoPrivate->VbeFrameBuffer
+                                            );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Graphics Output protocol interface to set video mode.
+
+  @param  This                   Protocol instance pointer.
+  @param  ModeNumber             The mode number to be set.
+
+  @retval EFI_SUCCESS            Graphics mode was changed.
+  @retval EFI_DEVICE_ERROR       The device had an error and could not complete the
+                                 request.
+  @retval EFI_UNSUPPORTED        ModeNumber is not supported by this device.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputSetMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL * This,
+  IN  UINT32                       ModeNumber
+  )
+{
+  EFI_STATUS              Status;
+  BIOS_VIDEO_DEV          *BiosVideoPrivate;
+  BIOS_VIDEO_MODE_DATA    *ModeData;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  ModeData = &BiosVideoPrivate->ModeData[ModeNumber];
+
+  if (ModeNumber >= This->Mode->MaxMode) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (ModeNumber == This->Mode->Mode) {
+    //
+    // Clear screen to black
+    //
+    ZeroMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+    BiosVideoGraphicsOutputVbeBlt (
+                        This,
+                        &Background,
+                        EfiBltVideoFill,
+                        0,
+                        0,
+                        0,
+                        0,
+                        ModeData->HorizontalResolution,
+                        ModeData->VerticalResolution,
+                        0
+    );
+    return EFI_SUCCESS;
+  }
+
+  Status = BiosVideoSetModeWorker (BiosVideoPrivate, ModeData, BiosVideoPrivate->GopDevicePath);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  This->Mode->Mode = ModeNumber;
+  This->Mode->Info->Version = 0;
+  This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution;
+  This->Mode->Info->VerticalResolution = ModeData->VerticalResolution;
+  This->Mode->Info->PixelFormat = ModeData->PixelFormat;
+  CopyMem (&(This->Mode->Info->PixelInformation), &(ModeData->PixelBitMask), sizeof (ModeData->PixelBitMask));
+  This->Mode->Info->PixelsPerScanLine =  (ModeData->BytesPerScanLine * 8) / ModeData->BitsPerPixel;
+  This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+  This->Mode->FrameBufferSize = ModeData->FrameBufferSize;
+  This->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS) (UINTN) ModeData->LinearFrameBuffer;
+
+  BiosVideoPrivate->HardwareNeedsStarting = FALSE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Update physical frame buffer, copy 4 bytes block, then copy remaining bytes.
+
+  @param   PciIo              The pointer of EFI_PCI_IO_PROTOCOL
+  @param   VbeBuffer          The data to transfer to screen
+  @param   MemAddress         Physical frame buffer base address
+  @param   DestinationX       The X coordinate of the destination for BltOperation
+  @param   DestinationY       The Y coordinate of the destination for BltOperation
+  @param   TotalBytes         The total bytes of copy
+  @param   VbePixelWidth      Bytes per pixel
+  @param   BytesPerScanLine   Bytes per scan line
+
+**/
+VOID
+CopyVideoBuffer (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT8                 *VbeBuffer,
+  IN  VOID                  *MemAddress,
+  IN  UINTN                 DestinationX,
+  IN  UINTN                 DestinationY,
+  IN  UINTN                 TotalBytes,
+  IN  UINT32                VbePixelWidth,
+  IN  UINTN                 BytesPerScanLine
+  )
+{
+  UINTN                 FrameBufferAddr;
+  UINTN                 CopyBlockNum;
+  UINTN                 RemainingBytes;
+  UINTN                 UnalignedBytes;
+  EFI_STATUS            Status;
+
+  FrameBufferAddr = (UINTN) MemAddress + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth;
+
+  //
+  // If TotalBytes is less than 4 bytes, only start byte copy.
+  //
+  if (TotalBytes < 4) {
+    Status = PciIo->Mem.Write (
+                     PciIo,
+                     EfiPciIoWidthUint8,
+                     EFI_PCI_IO_PASS_THROUGH_BAR,
+                     (UINT64) FrameBufferAddr,
+                     TotalBytes,
+                     VbeBuffer
+                     );
+    ASSERT_EFI_ERROR (Status);
+    return;
+  }
+
+  //
+  // If VbeBuffer is not 4-byte aligned, start byte copy.
+  //
+  UnalignedBytes  = (4 - ((UINTN) VbeBuffer & 0x3)) & 0x3;
+
+  if (UnalignedBytes != 0) {
+    Status = PciIo->Mem.Write (
+                     PciIo,
+                     EfiPciIoWidthUint8,
+                     EFI_PCI_IO_PASS_THROUGH_BAR,
+                     (UINT64) FrameBufferAddr,
+                     UnalignedBytes,
+                     VbeBuffer
+                     );
+    ASSERT_EFI_ERROR (Status);
+    FrameBufferAddr += UnalignedBytes;
+    VbeBuffer       += UnalignedBytes;
+  }
+
+  //
+  // Calculate 4-byte block count and remaining bytes.
+  //
+  CopyBlockNum   = (TotalBytes - UnalignedBytes) >> 2;
+  RemainingBytes = (TotalBytes - UnalignedBytes) &  3;
+
+  //
+  // Copy 4-byte block and remaining bytes to physical frame buffer.
+  //
+  if (CopyBlockNum != 0) {
+    Status = PciIo->Mem.Write (
+                    PciIo,
+                    EfiPciIoWidthUint32,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) FrameBufferAddr,
+                    CopyBlockNum,
+                    VbeBuffer
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  if (RemainingBytes != 0) {
+    FrameBufferAddr += (CopyBlockNum << 2);
+    VbeBuffer       += (CopyBlockNum << 2);
+    Status = PciIo->Mem.Write (
+                    PciIo,
+                    EfiPciIoWidthUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) FrameBufferAddr,
+                    RemainingBytes,
+                    VbeBuffer
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+}
+
+/**
+  Worker function to block transfer for VBE device.
+
+  @param  BiosVideoPrivate       Instance of BIOS_VIDEO_DEV
+  @param  BltBuffer              The data to transfer to screen
+  @param  BltOperation           The operation to perform
+  @param  SourceX                The X coordinate of the source for BltOperation
+  @param  SourceY                The Y coordinate of the source for BltOperation
+  @param  DestinationX           The X coordinate of the destination for
+                                 BltOperation
+  @param  DestinationY           The Y coordinate of the destination for
+                                 BltOperation
+  @param  Width                  The width of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Height                 The height of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Delta                  Not used for EfiBltVideoFill and
+                                 EfiBltVideoToVideo operation. If a Delta of 0 is
+                                 used, the entire BltBuffer will be operated on. If
+                                 a subrectangle of the BltBuffer is used, then
+                                 Delta represents the number of bytes in a row of
+                                 the BltBuffer.
+  @param  Mode                   Mode data.
+
+  @retval EFI_INVALID_PARAMETER  Invalid parameter passed in
+  @retval EFI_SUCCESS            Blt operation success
+
+**/
+EFI_STATUS
+BiosVideoVbeBltWorker (
+  IN  BIOS_VIDEO_DEV                     *BiosVideoPrivate,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN  UINTN                              SourceX,
+  IN  UINTN                              SourceY,
+  IN  UINTN                              DestinationX,
+  IN  UINTN                              DestinationY,
+  IN  UINTN                              Width,
+  IN  UINTN                              Height,
+  IN  UINTN                              Delta,
+  IN  BIOS_VIDEO_MODE_DATA               *Mode
+  )
+{
+  EFI_PCI_IO_PROTOCOL            *PciIo;
+  EFI_TPL                        OriginalTPL;
+  UINTN                          DstY;
+  UINTN                          SrcY;
+  UINTN                          DstX;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *Blt;
+  VOID                           *MemAddress;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *VbeFrameBuffer;
+  UINTN                          BytesPerScanLine;
+  UINTN                          Index;
+  UINT8                          *VbeBuffer;
+  UINT8                          *VbeBuffer1;
+  UINT8                          *BltUint8;
+  UINT32                         VbePixelWidth;
+  UINT32                         Pixel;
+  UINTN                          TotalBytes;
+
+  PciIo             = BiosVideoPrivate->PciIo;
+
+  VbeFrameBuffer    = BiosVideoPrivate->VbeFrameBuffer;
+  MemAddress        = Mode->LinearFrameBuffer;
+  BytesPerScanLine  = Mode->BytesPerScanLine;
+  VbePixelWidth     = Mode->BitsPerPixel / 8;
+  BltUint8          = (UINT8 *) BltBuffer;
+  TotalBytes        = Width * VbePixelWidth;
+
+  if (((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // We need to fill the Virtual Screen buffer with the blt data.
+  // The virtual screen is upside down, as the first row is the bootom row of
+  // the image.
+  //
+  if (BltOperation == EfiBltVideoToBltBuffer) {
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    if (SourceY + Height > Mode->VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (SourceX + Width > Mode->HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    //
+    // BltBuffer to Video: Source is BltBuffer, destination is Video
+    //
+    if (DestinationY + Height > Mode->VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (DestinationX + Width > Mode->HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+  }
+  //
+  // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
+  // We would not want a timer based event (Cursor, ...) to come in while we are
+  // doing this operation.
+  //
+  OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+  switch (BltOperation) {
+  case EfiBltVideoToBltBuffer:
+    for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {
+      Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (BltUint8 + DstY * Delta + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+      //
+      // Shuffle the packed bytes in the hardware buffer to match EFI_GRAPHICS_OUTPUT_BLT_PIXEL
+      //
+      VbeBuffer = ((UINT8 *) VbeFrameBuffer + (SrcY * BytesPerScanLine + SourceX * VbePixelWidth));
+      for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {
+        Pixel         = VbeBuffer[0] | VbeBuffer[1] << 8 | VbeBuffer[2] << 16 | VbeBuffer[3] << 24;
+        Blt->Red      = (UINT8) ((Pixel >> Mode->Red.Position) & Mode->Red.Mask);
+        Blt->Blue     = (UINT8) ((Pixel >> Mode->Blue.Position) & Mode->Blue.Mask);
+        Blt->Green    = (UINT8) ((Pixel >> Mode->Green.Position) & Mode->Green.Mask);
+        Blt->Reserved = 0;
+        Blt++;
+        VbeBuffer += VbePixelWidth;
+      }
+
+    }
+    break;
+
+  case EfiBltVideoToVideo:
+    for (Index = 0; Index < Height; Index++) {
+      if (DestinationY <= SourceY) {
+        SrcY  = SourceY + Index;
+        DstY  = DestinationY + Index;
+      } else {
+        SrcY  = SourceY + Height - Index - 1;
+        DstY  = DestinationY + Height - Index - 1;
+      }
+
+      VbeBuffer   = ((UINT8 *) VbeFrameBuffer + DstY * BytesPerScanLine + DestinationX * VbePixelWidth);
+      VbeBuffer1  = ((UINT8 *) VbeFrameBuffer + SrcY * BytesPerScanLine + SourceX * VbePixelWidth);
+
+      gBS->CopyMem (
+            VbeBuffer,
+            VbeBuffer1,
+            TotalBytes
+            );
+
+      //
+      // Update physical frame buffer.
+      //
+      CopyVideoBuffer (
+        PciIo,
+        VbeBuffer,
+        MemAddress,
+        DestinationX,
+        DstY,
+        TotalBytes,
+        VbePixelWidth,
+        BytesPerScanLine
+        );
+    }
+    break;
+
+  case EfiBltVideoFill:
+    VbeBuffer = (UINT8 *) ((UINTN) VbeFrameBuffer + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth);
+    Blt       = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltUint8;
+    //
+    // Shuffle the RGB fields in EFI_GRAPHICS_OUTPUT_BLT_PIXEL to match the hardware buffer
+    //
+    Pixel = ((Blt->Red & Mode->Red.Mask) << Mode->Red.Position) |
+      (
+        (Blt->Green & Mode->Green.Mask) <<
+        Mode->Green.Position
+      ) |
+          ((Blt->Blue & Mode->Blue.Mask) << Mode->Blue.Position);
+
+    for (Index = 0; Index < Width; Index++) {
+      gBS->CopyMem (
+            VbeBuffer,
+            &Pixel,
+            VbePixelWidth
+            );
+      VbeBuffer += VbePixelWidth;
+    }
+
+    VbeBuffer = (UINT8 *) ((UINTN) VbeFrameBuffer + (DestinationY * BytesPerScanLine) + DestinationX * VbePixelWidth);
+    for (DstY = DestinationY + 1; DstY < (Height + DestinationY); DstY++) {
+      gBS->CopyMem (
+            (VOID *) ((UINTN) VbeFrameBuffer + (DstY * BytesPerScanLine) + DestinationX * VbePixelWidth),
+            VbeBuffer,
+            TotalBytes
+            );
+    }
+
+    for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {
+      //
+      // Update physical frame buffer.
+      //
+      CopyVideoBuffer (
+        PciIo,
+        VbeBuffer,
+        MemAddress,
+        DestinationX,
+        DstY,
+        TotalBytes,
+        VbePixelWidth,
+        BytesPerScanLine
+        );
+    }
+    break;
+
+  case EfiBltBufferToVideo:
+    for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+      Blt       = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (BltUint8 + (SrcY * Delta) + (SourceX) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+      VbeBuffer = ((UINT8 *) VbeFrameBuffer + (DstY * BytesPerScanLine + DestinationX * VbePixelWidth));
+      for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {
+        //
+        // Shuffle the RGB fields in EFI_GRAPHICS_OUTPUT_BLT_PIXEL to match the hardware buffer
+        //
+        Pixel = ((Blt->Red & Mode->Red.Mask) << Mode->Red.Position) |
+          ((Blt->Green & Mode->Green.Mask) << Mode->Green.Position) |
+            ((Blt->Blue & Mode->Blue.Mask) << Mode->Blue.Position);
+        gBS->CopyMem (
+              VbeBuffer,
+              &Pixel,
+              VbePixelWidth
+              );
+        Blt++;
+        VbeBuffer += VbePixelWidth;
+      }
+
+      VbeBuffer = ((UINT8 *) VbeFrameBuffer + (DstY * BytesPerScanLine + DestinationX * VbePixelWidth));
+
+      //
+      // Update physical frame buffer.
+      //
+      CopyVideoBuffer (
+        PciIo,
+        VbeBuffer,
+        MemAddress,
+        DestinationX,
+        DstY,
+        TotalBytes,
+        VbePixelWidth,
+        BytesPerScanLine
+        );
+    }
+    break;
+
+    default: ;
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Graphics Output protocol instance to block transfer for VBE device.
+
+  @param  This                   Pointer to Graphics Output protocol instance
+  @param  BltBuffer              The data to transfer to screen
+  @param  BltOperation           The operation to perform
+  @param  SourceX                The X coordinate of the source for BltOperation
+  @param  SourceY                The Y coordinate of the source for BltOperation
+  @param  DestinationX           The X coordinate of the destination for
+                                 BltOperation
+  @param  DestinationY           The Y coordinate of the destination for
+                                 BltOperation
+  @param  Width                  The width of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Height                 The height of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Delta                  Not used for EfiBltVideoFill and
+                                 EfiBltVideoToVideo operation. If a Delta of 0 is
+                                 used, the entire BltBuffer will be operated on. If
+                                 a subrectangle of the BltBuffer is used, then
+                                 Delta represents the number of bytes in a row of
+                                 the BltBuffer.
+
+  @retval EFI_INVALID_PARAMETER  Invalid parameter passed in
+  @retval EFI_SUCCESS            Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputVbeBlt (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL       *This,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN  UINTN                              SourceX,
+  IN  UINTN                              SourceY,
+  IN  UINTN                              DestinationX,
+  IN  UINTN                              DestinationY,
+  IN  UINTN                              Width,
+  IN  UINTN                              Height,
+  IN  UINTN                              Delta
+  )
+{
+  BIOS_VIDEO_DEV                 *BiosVideoPrivate;
+  BIOS_VIDEO_MODE_DATA           *Mode;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BiosVideoPrivate  = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This);
+  Mode              = &BiosVideoPrivate->ModeData[This->Mode->Mode];
+
+  return BiosVideoVbeBltWorker (
+           BiosVideoPrivate,
+           BltBuffer,
+           BltOperation,
+           SourceX,
+           SourceY,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           Delta,
+           Mode
+           );
+}
+
+/**
+  Write graphics controller registers.
+
+  @param  PciIo                  Pointer to PciIo protocol instance of the
+                                 controller
+  @param  Address                Register address
+  @param  Data                   Data to be written to register
+
+  @return None
+
+**/
+VOID
+WriteGraphicsController (
+  IN  EFI_PCI_IO_PROTOCOL  *PciIo,
+  IN  UINTN                Address,
+  IN  UINTN                Data
+  )
+{
+  Address = Address | (Data << 8);
+  PciIo->Io.Write (
+              PciIo,
+              EfiPciIoWidthUint16,
+              EFI_PCI_IO_PASS_THROUGH_BAR,
+              VGA_GRAPHICS_CONTROLLER_ADDRESS_REGISTER,
+              1,
+              &Address
+              );
+}
+
+
+/**
+  Read the four bit plane of VGA frame buffer.
+
+  @param  PciIo                  Pointer to PciIo protocol instance of the
+                                 controller
+  @param  HardwareBuffer         Hardware VGA frame buffer address
+  @param  MemoryBuffer           Memory buffer address
+  @param  WidthInBytes           Number of bytes in a line to read
+  @param  Height                 Height of the area to read
+
+  @return None
+
+**/
+VOID
+VgaReadBitPlanes (
+  EFI_PCI_IO_PROTOCOL  *PciIo,
+  UINT8                *HardwareBuffer,
+  UINT8                *MemoryBuffer,
+  UINTN                WidthInBytes,
+  UINTN                Height
+  )
+{
+  UINTN BitPlane;
+  UINTN Rows;
+  UINTN FrameBufferOffset;
+  UINT8 *Source;
+  UINT8 *Destination;
+
+  //
+  // Program the Mode Register Write mode 0, Read mode 0
+  //
+  WriteGraphicsController (
+    PciIo,
+    VGA_GRAPHICS_CONTROLLER_MODE_REGISTER,
+    VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_0
+    );
+
+  for (BitPlane = 0, FrameBufferOffset = 0;
+       BitPlane < VGA_NUMBER_OF_BIT_PLANES;
+       BitPlane++, FrameBufferOffset += VGA_BYTES_PER_BIT_PLANE
+      ) {
+    //
+    // Program the Read Map Select Register to select the correct bit plane
+    //
+    WriteGraphicsController (
+      PciIo,
+      VGA_GRAPHICS_CONTROLLER_READ_MAP_SELECT_REGISTER,
+      BitPlane
+      );
+
+    Source      = HardwareBuffer;
+    Destination = MemoryBuffer + FrameBufferOffset;
+
+    for (Rows = 0; Rows < Height; Rows++, Source += VGA_BYTES_PER_SCAN_LINE, Destination += VGA_BYTES_PER_SCAN_LINE) {
+      PciIo->Mem.Read (
+                  PciIo,
+                  EfiPciIoWidthUint8,
+                  EFI_PCI_IO_PASS_THROUGH_BAR,
+                  (UINT64) (UINTN) Source,
+                  WidthInBytes,
+                  (VOID *) Destination
+                  );
+    }
+  }
+}
+
+
+/**
+  Internal routine to convert VGA color to Grahpics Output color.
+
+  @param  MemoryBuffer           Buffer containing VGA color
+  @param  CoordinateX            The X coordinate of pixel on screen
+  @param  CoordinateY            The Y coordinate of pixel on screen
+  @param  BltBuffer              Buffer to contain converted Grahpics Output color
+
+  @return None
+
+**/
+VOID
+VgaConvertToGraphicsOutputColor (
+  UINT8                          *MemoryBuffer,
+  UINTN                          CoordinateX,
+  UINTN                          CoordinateY,
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer
+  )
+{
+  UINTN Mask;
+  UINTN Bit;
+  UINTN Color;
+
+  MemoryBuffer += ((CoordinateY << 6) + (CoordinateY << 4) + (CoordinateX >> 3));
+  Mask = mVgaBitMaskTable[CoordinateX & 0x07];
+  for (Bit = 0x01, Color = 0; Bit < 0x10; Bit <<= 1, MemoryBuffer += VGA_BYTES_PER_BIT_PLANE) {
+    if ((*MemoryBuffer & Mask) != 0) {
+      Color |= Bit;
+    }
+  }
+
+  *BltBuffer = mVgaColorToGraphicsOutputColor[Color];
+}
+
+/**
+  Internal routine to convert Grahpics Output color to VGA color.
+
+  @param  BltBuffer              buffer containing Grahpics Output color
+
+  @return Converted VGA color
+
+**/
+UINT8
+VgaConvertColor (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL          *BltBuffer
+  )
+{
+  UINT8 Color;
+
+  Color = (UINT8) ((BltBuffer->Blue >> 7) | ((BltBuffer->Green >> 6) & 0x02) | ((BltBuffer->Red >> 5) & 0x04));
+  if ((BltBuffer->Red + BltBuffer->Green + BltBuffer->Blue) > 0x180) {
+    Color |= 0x08;
+  }
+
+  return Color;
+}
+
+
+/**
+  Grahpics Output protocol instance to block transfer for VGA device.
+
+  @param  This                   Pointer to Grahpics Output protocol instance
+  @param  BltBuffer              The data to transfer to screen
+  @param  BltOperation           The operation to perform
+  @param  SourceX                The X coordinate of the source for BltOperation
+  @param  SourceY                The Y coordinate of the source for BltOperation
+  @param  DestinationX           The X coordinate of the destination for
+                                 BltOperation
+  @param  DestinationY           The Y coordinate of the destination for
+                                 BltOperation
+  @param  Width                  The width of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Height                 The height of a rectangle in the blt rectangle in
+                                 pixels
+  @param  Delta                  Not used for EfiBltVideoFill and
+                                 EfiBltVideoToVideo operation. If a Delta of 0 is
+                                 used, the entire BltBuffer will be operated on. If
+                                 a subrectangle of the BltBuffer is used, then
+                                 Delta represents the number of bytes in a row of
+                                 the BltBuffer.
+
+  @retval EFI_INVALID_PARAMETER  Invalid parameter passed in
+  @retval EFI_SUCCESS            Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoGraphicsOutputVgaBlt (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL       *This,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN  UINTN                              SourceX,
+  IN  UINTN                              SourceY,
+  IN  UINTN                              DestinationX,
+  IN  UINTN                              DestinationY,
+  IN  UINTN                              Width,
+  IN  UINTN                              Height,
+  IN  UINTN                              Delta
+  )
+{
+  BIOS_VIDEO_DEV      *BiosVideoPrivate;
+  EFI_TPL             OriginalTPL;
+  UINT8               *MemAddress;
+  UINTN               BytesPerScanLine;
+  UINTN               Bit;
+  UINTN               Index;
+  UINTN               Index1;
+  UINTN               StartAddress;
+  UINTN               Bytes;
+  UINTN               Offset;
+  UINT8               LeftMask;
+  UINT8               RightMask;
+  UINTN               Address;
+  UINTN               AddressFix;
+  UINT8               *Address1;
+  UINT8               *SourceAddress;
+  UINT8               *DestinationAddress;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  UINT8               Data;
+  UINT8               PixelColor;
+  UINT8               *VgaFrameBuffer;
+  UINTN               SourceOffset;
+  UINTN               SourceWidth;
+  UINTN               Rows;
+  UINTN               Columns;
+  UINTN               CoordinateX;
+  UINTN               CoordinateY;
+  UINTN               CurrentMode;
+
+  if (This == NULL || ((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BiosVideoPrivate  = BIOS_VIDEO_DEV_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  CurrentMode = This->Mode->Mode;
+  PciIo             = BiosVideoPrivate->PciIo;
+  MemAddress        = BiosVideoPrivate->ModeData[CurrentMode].LinearFrameBuffer;
+  BytesPerScanLine  = BiosVideoPrivate->ModeData[CurrentMode].BytesPerScanLine >> 3;
+  VgaFrameBuffer    = BiosVideoPrivate->VgaFrameBuffer;
+
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // We need to fill the Virtual Screen buffer with the blt data.
+  // The virtual screen is upside down, as the first row is the bootom row of
+  // the image.
+  //
+  if (BltOperation == EfiBltVideoToBltBuffer) {
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    if (SourceY + Height > BiosVideoPrivate->ModeData[CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (SourceX + Width > BiosVideoPrivate->ModeData[CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    //
+    // BltBuffer to Video: Source is BltBuffer, destination is Video
+    //
+    if (DestinationY + Height > BiosVideoPrivate->ModeData[CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (DestinationX + Width > BiosVideoPrivate->ModeData[CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+  }
+  //
+  // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
+  // We would not want a timer based event (Cursor, ...) to come in while we are
+  // doing this operation.
+  //
+  OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+  //
+  // Compute some values we need for VGA
+  //
+  switch (BltOperation) {
+  case EfiBltVideoToBltBuffer:
+
+    SourceOffset  = (SourceY << 6) + (SourceY << 4) + (SourceX >> 3);
+    SourceWidth   = ((SourceX + Width - 1) >> 3) - (SourceX >> 3) + 1;
+
+    //
+    // Read all the pixels in the 4 bit planes into a memory buffer that looks like the VGA buffer
+    //
+    VgaReadBitPlanes (
+      PciIo,
+      MemAddress + SourceOffset,
+      VgaFrameBuffer + SourceOffset,
+      SourceWidth,
+      Height
+      );
+
+    //
+    // Convert VGA Bit Planes to a Graphics Output 32-bit color value
+    //
+    BltBuffer += (DestinationY * (Delta >> 2) + DestinationX);
+    for (Rows = 0, CoordinateY = SourceY; Rows < Height; Rows++, CoordinateY++, BltBuffer += (Delta >> 2)) {
+      for (Columns = 0, CoordinateX = SourceX; Columns < Width; Columns++, CoordinateX++, BltBuffer++) {
+        VgaConvertToGraphicsOutputColor (VgaFrameBuffer, CoordinateX, CoordinateY, BltBuffer);
+      }
+
+      BltBuffer -= Width;
+    }
+
+    break;
+
+  case EfiBltVideoToVideo:
+    //
+    // Check for an aligned Video to Video operation
+    //
+    if ((SourceX & 0x07) == 0x00 && (DestinationX & 0x07) == 0x00 && (Width & 0x07) == 0x00) {
+      //
+      // Program the Mode Register Write mode 1, Read mode 0
+      //
+      WriteGraphicsController (
+        PciIo,
+        VGA_GRAPHICS_CONTROLLER_MODE_REGISTER,
+        VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_1
+        );
+
+      SourceAddress       = (UINT8 *) (MemAddress + (SourceY << 6) + (SourceY << 4) + (SourceX >> 3));
+      DestinationAddress  = (UINT8 *) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3));
+      Bytes               = Width >> 3;
+      for (Index = 0, Offset = 0; Index < Height; Index++, Offset += BytesPerScanLine) {
+        PciIo->CopyMem (
+                PciIo,
+                EfiPciIoWidthUint8,
+                EFI_PCI_IO_PASS_THROUGH_BAR,
+                (UINT64) (UINTN) (DestinationAddress + Offset),
+                EFI_PCI_IO_PASS_THROUGH_BAR,
+                (UINT64) (UINTN) (SourceAddress + Offset),
+                Bytes
+                );
+      }
+    } else {
+      SourceOffset  = (SourceY << 6) + (SourceY << 4) + (SourceX >> 3);
+      SourceWidth   = ((SourceX + Width - 1) >> 3) - (SourceX >> 3) + 1;
+
+      //
+      // Read all the pixels in the 4 bit planes into a memory buffer that looks like the VGA buffer
+      //
+      VgaReadBitPlanes (
+        PciIo,
+        MemAddress + SourceOffset,
+        VgaFrameBuffer + SourceOffset,
+        SourceWidth,
+        Height
+        );
+    }
+
+    break;
+
+  case EfiBltVideoFill:
+    StartAddress  = (UINTN) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3));
+    Bytes         = ((DestinationX + Width - 1) >> 3) - (DestinationX >> 3);
+    LeftMask      = mVgaLeftMaskTable[DestinationX & 0x07];
+    RightMask     = mVgaRightMaskTable[(DestinationX + Width - 1) & 0x07];
+    if (Bytes == 0) {
+      LeftMask = (UINT8) (LeftMask & RightMask);
+      RightMask = 0;
+    }
+
+    if (LeftMask == 0xff) {
+      StartAddress--;
+      Bytes++;
+      LeftMask = 0;
+    }
+
+    if (RightMask == 0xff) {
+      Bytes++;
+      RightMask = 0;
+    }
+
+    PixelColor = VgaConvertColor (BltBuffer);
+
+    //
+    // Program the Mode Register Write mode 2, Read mode 0
+    //
+    WriteGraphicsController (
+      PciIo,
+      VGA_GRAPHICS_CONTROLLER_MODE_REGISTER,
+      VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2
+      );
+
+    //
+    // Program the Data Rotate/Function Select Register to replace
+    //
+    WriteGraphicsController (
+      PciIo,
+      VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER,
+      VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE
+      );
+
+    if (LeftMask != 0) {
+      //
+      // Program the BitMask register with the Left column mask
+      //
+      WriteGraphicsController (
+        PciIo,
+        VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER,
+        LeftMask
+        );
+
+      for (Index = 0, Address = StartAddress; Index < Height; Index++, Address += BytesPerScanLine) {
+        //
+        // Read data from the bit planes into the latches
+        //
+        PciIo->Mem.Read (
+                    PciIo,
+                    EfiPciIoWidthUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) (UINTN) Address,
+                    1,
+                    &Data
+                    );
+        //
+        // Write the lower 4 bits of PixelColor to the bit planes in the pixels enabled by BitMask
+        //
+        PciIo->Mem.Write (
+                    PciIo,
+                    EfiPciIoWidthUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) (UINTN) Address,
+                    1,
+                    &PixelColor
+                    );
+      }
+    }
+
+    if (Bytes > 1) {
+      //
+      // Program the BitMask register with the middle column mask of 0xff
+      //
+      WriteGraphicsController (
+        PciIo,
+        VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER,
+        0xff
+        );
+
+      for (Index = 0, Address = StartAddress + 1; Index < Height; Index++, Address += BytesPerScanLine) {
+        PciIo->Mem.Write (
+                    PciIo,
+                    EfiPciIoWidthFillUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) (UINTN) Address,
+                    Bytes - 1,
+                    &PixelColor
+                    );
+      }
+    }
+
+    if (RightMask != 0) {
+      //
+      // Program the BitMask register with the Right column mask
+      //
+      WriteGraphicsController (
+        PciIo,
+        VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER,
+        RightMask
+        );
+
+      for (Index = 0, Address = StartAddress + Bytes; Index < Height; Index++, Address += BytesPerScanLine) {
+        //
+        // Read data from the bit planes into the latches
+        //
+        PciIo->Mem.Read (
+                    PciIo,
+                    EfiPciIoWidthUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) (UINTN) Address,
+                    1,
+                    &Data
+                    );
+        //
+        // Write the lower 4 bits of PixelColor to the bit planes in the pixels enabled by BitMask
+        //
+        PciIo->Mem.Write (
+                    PciIo,
+                    EfiPciIoWidthUint8,
+                    EFI_PCI_IO_PASS_THROUGH_BAR,
+                    (UINT64) (UINTN) Address,
+                    1,
+                    &PixelColor
+                    );
+      }
+    }
+    break;
+
+  case EfiBltBufferToVideo:
+    StartAddress  = (UINTN) (MemAddress + (DestinationY << 6) + (DestinationY << 4) + (DestinationX >> 3));
+    LeftMask      = mVgaBitMaskTable[DestinationX & 0x07];
+
+    //
+    // Program the Mode Register Write mode 2, Read mode 0
+    //
+    WriteGraphicsController (
+      PciIo,
+      VGA_GRAPHICS_CONTROLLER_MODE_REGISTER,
+      VGA_GRAPHICS_CONTROLLER_READ_MODE_0 | VGA_GRAPHICS_CONTROLLER_WRITE_MODE_2
+      );
+
+    //
+    // Program the Data Rotate/Function Select Register to replace
+    //
+    WriteGraphicsController (
+      PciIo,
+      VGA_GRAPHICS_CONTROLLER_DATA_ROTATE_REGISTER,
+      VGA_GRAPHICS_CONTROLLER_FUNCTION_REPLACE
+      );
+
+    for (Index = 0, Address = StartAddress; Index < Height; Index++, Address += BytesPerScanLine) {
+      for (Index1 = 0; Index1 < Width; Index1++) {
+        BiosVideoPrivate->LineBuffer[Index1] = VgaConvertColor (&BltBuffer[(SourceY + Index) * (Delta >> 2) + SourceX + Index1]);
+      }
+      AddressFix = Address;
+
+      for (Bit = 0; Bit < 8; Bit++) {
+        //
+        // Program the BitMask register with the Left column mask
+        //
+        WriteGraphicsController (
+          PciIo,
+          VGA_GRAPHICS_CONTROLLER_BIT_MASK_REGISTER,
+          LeftMask
+          );
+
+        for (Index1 = Bit, Address1 = (UINT8 *) AddressFix; Index1 < Width; Index1 += 8, Address1++) {
+          //
+          // Read data from the bit planes into the latches
+          //
+          PciIo->Mem.Read (
+                      PciIo,
+                      EfiPciIoWidthUint8,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      (UINT64) (UINTN) Address1,
+                      1,
+                      &Data
+                      );
+
+          PciIo->Mem.Write (
+                      PciIo,
+                      EfiPciIoWidthUint8,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      (UINT64) (UINTN) Address1,
+                      1,
+                      &BiosVideoPrivate->LineBuffer[Index1]
+                      );
+        }
+
+        LeftMask = (UINT8) (LeftMask >> 1);
+        if (LeftMask == 0) {
+          LeftMask = 0x80;
+          AddressFix++;
+        }
+      }
+    }
+
+    break;
+
+    default: ;
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+
+  return EFI_SUCCESS;
+}
+
+//
+// VGA Mini Port Protocol Functions
+//
+
+/**
+  VgaMiniPort protocol interface to set mode.
+
+  @param  This                   Pointer to VgaMiniPort protocol instance
+  @param  ModeNumber             The index of the mode
+
+  @retval EFI_UNSUPPORTED        The requested mode is not supported
+  @retval EFI_SUCCESS            The requested mode is set successfully
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoVgaMiniPortSetMode (
+  IN  EFI_VGA_MINI_PORT_PROTOCOL  *This,
+  IN  UINTN                       ModeNumber
+  )
+{
+  BIOS_VIDEO_DEV        *BiosVideoPrivate;
+  EFI_IA32_REGISTER_SET Regs;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Make sure the ModeNumber is a valid value
+  //
+  if (ModeNumber >= This->MaxMode) {
+    return EFI_UNSUPPORTED;
+  }
+  //
+  // Get the device structure for this device
+  //
+  BiosVideoPrivate = BIOS_VIDEO_DEV_FROM_VGA_MINI_PORT_THIS (This);
+
+  switch (ModeNumber) {
+  case 0:
+    //
+    // Set the 80x25 Text VGA Mode
+    //
+    Regs.H.AH = 0x00;
+    Regs.H.AL = 0x83;
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+    Regs.H.AH = 0x11;
+    Regs.H.AL = 0x14;
+    Regs.H.BL = 0;
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+    break;
+
+  case 1:
+    //
+    // Set the 80x50 Text VGA Mode
+    //
+    Regs.H.AH = 0x00;
+    Regs.H.AL = 0x83;
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+    Regs.H.AH = 0x11;
+    Regs.H.AL = 0x12;
+    Regs.H.BL = 0;
+    BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+    break;
+
+  default:
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Event handler for Exit Boot Service.
+
+  @param  Event       The event that be siganlled when exiting boot service.
+  @param  Context     Pointer to instance of BIOS_VIDEO_DEV.
+
+**/
+VOID
+EFIAPI
+BiosVideoNotifyExitBootServices (
+  IN  EFI_EVENT Event,
+  IN  VOID      *Context
+  )
+{
+  BIOS_VIDEO_DEV         *BiosVideoPrivate;
+  EFI_IA32_REGISTER_SET  Regs;
+
+  BiosVideoPrivate  = (BIOS_VIDEO_DEV *)Context;
+
+  //
+  // Set the 80x25 Text VGA Mode
+  //
+  Regs.H.AH = 0x00;
+  Regs.H.AL = 0x03;
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  Regs.H.AH = 0x00;
+  Regs.H.AL = 0x83;
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+
+  Regs.H.AH = 0x11;
+  Regs.H.AL = 0x04;
+  Regs.H.BL = 0;
+  BiosVideoPrivate->LegacyBios->Int86 (BiosVideoPrivate->LegacyBios, 0x10, &Regs);
+}
+
+/**
+  The user Entry Point for module UefiBiosVideo. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoEntryPoint(
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  //
+  // Install driver model protocol(s).
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gBiosVideoDriverBinding,
+             ImageHandle,
+             &gBiosVideoComponentName,
+             &gBiosVideoComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Install Legacy BIOS GUID to mark this driver as a BIOS Thunk Driver
+  //
+  return gBS->InstallMultipleProtocolInterfaces (
+                &ImageHandle,
+                &gEfiLegacyBiosGuid,
+                NULL,
+                NULL
+                );
+}
+
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/ComponentName.c b/OvmfPkg/Csm/BiosThunk/VideoDxe/ComponentName.c
new file mode 100644
index 0000000000..dc914aa2f3
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/ComponentName.c
@@ -0,0 +1,306 @@
+/** @file
+
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "BiosVideo.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gBiosVideoComponentName = {
+  BiosVideoComponentNameGetDriverName,
+  BiosVideoComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gBiosVideoComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) BiosVideoComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) BiosVideoComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mBiosVideoDriverNameTable[] = {
+  {
+    "eng;en",
+    L"BIOS[INT10] Video Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mBiosVideoDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gBiosVideoComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+BiosVideoComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBbs.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBbs.c
new file mode 100644
index 0000000000..6b1dd344f3
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBbs.c
@@ -0,0 +1,377 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+#include <IndustryStandard/Pci.h>
+
+// Give floppy 3 states
+// FLOPPY_PRESENT_WITH_MEDIA  = Floppy controller present and media is inserted
+// FLOPPY_NOT_PRESENT = No floppy controller present
+// FLOPPY_PRESENT_NO_MEDIA = Floppy controller present but no media inserted
+//
+#define FLOPPY_NOT_PRESENT           0
+#define FLOPPY_PRESENT_WITH_MEDIA    1
+#define FLOPPY_PRESENT_NO_MEDIA      2
+
+BBS_TABLE           *mBbsTable;
+BOOLEAN             mBbsTableDoneFlag   = FALSE;
+BOOLEAN             IsHaveMediaInFloppy = TRUE;
+
+/**
+  Checks the state of the floppy and if media is inserted.
+
+  This routine checks the state of the floppy and if media is inserted.
+  There are 3 cases:
+  No floppy present         - Set BBS entry to ignore
+  Floppy present & no media - Set BBS entry to lowest priority. We cannot
+  set it to ignore since 16-bit CSM will
+  indicate no floppy and thus drive A: is
+  unusable. CSM-16 will not try floppy since
+  lowest priority and thus not incur boot
+  time penality.
+  Floppy present & media    - Set BBS entry to some priority.
+
+  @return  State of floppy media
+
+**/
+UINT8
+HasMediaInFloppy (
+  VOID
+  )
+{
+  EFI_STATUS                            Status;
+  UINTN                                 HandleCount;
+  EFI_HANDLE                            *HandleBuffer;
+  UINTN                                 Index;
+  EFI_ISA_IO_PROTOCOL                   *IsaIo;
+  EFI_BLOCK_IO_PROTOCOL                 *BlkIo;
+
+  HandleBuffer  = NULL;
+  HandleCount   = 0;
+
+  gBS->LocateHandleBuffer (
+        ByProtocol,
+        &gEfiIsaIoProtocolGuid,
+        NULL,
+        &HandleCount,
+        &HandleBuffer
+        );
+
+  //
+  // If don't find any ISA/IO protocol assume no floppy. Need for floppy
+  // free system
+  //
+  if (HandleCount == 0) {
+    return FLOPPY_NOT_PRESENT;
+  }
+
+  ASSERT (HandleBuffer != NULL);
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiIsaIoProtocolGuid,
+                    (VOID **) &IsaIo
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x604)) {
+      continue;
+    }
+    //
+    // Update blockio in case the floppy is inserted in during BdsTimeout
+    //
+    Status = gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);
+
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
+
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiBlockIoProtocolGuid,
+                    (VOID **) &BlkIo
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    if (BlkIo->Media->MediaPresent) {
+      FreePool (HandleBuffer);
+      return FLOPPY_PRESENT_WITH_MEDIA;
+    } else {
+      FreePool (HandleBuffer);
+      return FLOPPY_PRESENT_NO_MEDIA;
+    }
+  }
+
+  FreePool (HandleBuffer);
+
+  return FLOPPY_NOT_PRESENT;
+
+}
+
+
+/**
+  Complete build of BBS TABLE.
+
+  @param  Private                 Legacy BIOS Instance data
+  @param  BbsTable                BBS Table passed to 16-bit code
+
+  @retval EFI_SUCCESS             Removable media not present
+
+**/
+EFI_STATUS
+LegacyBiosBuildBbs (
+  IN  LEGACY_BIOS_INSTANCE      *Private,
+  IN  BBS_TABLE                 *BbsTable
+  )
+{
+  UINTN     BbsIndex;
+  HDD_INFO  *HddInfo;
+  UINTN     HddIndex;
+  UINTN     Index;
+
+  //
+  // First entry is floppy.
+  // Next 2*MAX_IDE_CONTROLLER entries are for onboard IDE.
+  // Next n entries are filled in after each ROM is dispatched.
+  //   Entry filled in if follow BBS spec. See LegacyPci.c
+  // Next entries are for non-BBS compliant ROMS. They are filled in by
+  //   16-bit code during Legacy16UpdateBbs invocation. Final BootPriority
+  //   occurs after that invocation.
+  //
+  // Floppy
+  // Set default state.
+  //
+  IsHaveMediaInFloppy = HasMediaInFloppy ();
+  if (IsHaveMediaInFloppy == FLOPPY_PRESENT_WITH_MEDIA) {
+    BbsTable[0].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+  } else {
+    if (IsHaveMediaInFloppy == FLOPPY_PRESENT_NO_MEDIA) {
+      BbsTable[0].BootPriority = BBS_LOWEST_PRIORITY;
+    } else {
+      BbsTable[0].BootPriority = BBS_IGNORE_ENTRY;
+    }
+  }
+
+  BbsTable[0].Bus                       = 0xff;
+  BbsTable[0].Device                    = 0xff;
+  BbsTable[0].Function                  = 0xff;
+  BbsTable[0].DeviceType                = BBS_FLOPPY;
+  BbsTable[0].Class                     = 01;
+  BbsTable[0].SubClass                  = 02;
+  BbsTable[0].StatusFlags.OldPosition   = 0;
+  BbsTable[0].StatusFlags.Reserved1     = 0;
+  BbsTable[0].StatusFlags.Enabled       = 0;
+  BbsTable[0].StatusFlags.Failed        = 0;
+  BbsTable[0].StatusFlags.MediaPresent  = 0;
+  BbsTable[0].StatusFlags.Reserved2     = 0;
+
+  //
+  // Onboard HDD - Note Each HDD controller controls 2 drives
+  //               Master & Slave
+  //
+  HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0];
+  //
+  // Get IDE Drive Info
+  //
+  LegacyBiosBuildIdeData (Private, &HddInfo, 0);
+
+  for (HddIndex = 0; HddIndex < MAX_IDE_CONTROLLER; HddIndex++) {
+
+    BbsIndex = HddIndex * 2 + 1;
+    for (Index = 0; Index < 2; ++Index) {
+
+      BbsTable[BbsIndex + Index].Bus                      = HddInfo[HddIndex].Bus;
+      BbsTable[BbsIndex + Index].Device                   = HddInfo[HddIndex].Device;
+      BbsTable[BbsIndex + Index].Function                 = HddInfo[HddIndex].Function;
+      BbsTable[BbsIndex + Index].Class                    = 01;
+      BbsTable[BbsIndex + Index].SubClass                 = 01;
+      BbsTable[BbsIndex + Index].StatusFlags.OldPosition  = 0;
+      BbsTable[BbsIndex + Index].StatusFlags.Reserved1    = 0;
+      BbsTable[BbsIndex + Index].StatusFlags.Enabled      = 0;
+      BbsTable[BbsIndex + Index].StatusFlags.Failed       = 0;
+      BbsTable[BbsIndex + Index].StatusFlags.MediaPresent = 0;
+      BbsTable[BbsIndex + Index].StatusFlags.Reserved2    = 0;
+
+      //
+      // If no controller found or no device found set to ignore
+      // else set to unprioritized and set device type
+      //
+      if (HddInfo[HddIndex].CommandBaseAddress == 0) {
+        BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY;
+      } else {
+        if (Index == 0) {
+          if ((HddInfo[HddIndex].Status & (HDD_MASTER_IDE | HDD_MASTER_ATAPI_CDROM | HDD_MASTER_ATAPI_ZIPDISK)) != 0) {
+            BbsTable[BbsIndex + Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+            if ((HddInfo[HddIndex].Status & HDD_MASTER_IDE) != 0) {
+              BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK;
+            } else if ((HddInfo[HddIndex].Status & HDD_MASTER_ATAPI_CDROM) != 0) {
+              BbsTable[BbsIndex + Index].DeviceType = BBS_CDROM;
+            } else {
+              //
+              // for ZIPDISK
+              //
+              BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK;
+            }
+          } else {
+            BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY;
+          }
+        } else {
+          if ((HddInfo[HddIndex].Status & (HDD_SLAVE_IDE | HDD_SLAVE_ATAPI_CDROM | HDD_SLAVE_ATAPI_ZIPDISK)) != 0) {
+            BbsTable[BbsIndex + Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+            if ((HddInfo[HddIndex].Status & HDD_SLAVE_IDE) != 0) {
+              BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK;
+            } else if ((HddInfo[HddIndex].Status & HDD_SLAVE_ATAPI_CDROM) != 0) {
+              BbsTable[BbsIndex + Index].DeviceType = BBS_CDROM;
+            } else {
+              //
+              // for ZIPDISK
+              //
+              BbsTable[BbsIndex + Index].DeviceType = BBS_HARDDISK;
+            }
+          } else {
+            BbsTable[BbsIndex + Index].BootPriority = BBS_IGNORE_ENTRY;
+          }
+        }
+      }
+    }
+  }
+
+  return EFI_SUCCESS;
+
+}
+
+
+/**
+  Get all BBS info
+
+  @param  This                    Protocol instance pointer.
+  @param  HddCount                Number of HDD_INFO structures
+  @param  HddInfo                 Onboard IDE controller information
+  @param  BbsCount                Number of BBS_TABLE structures
+  @param  BbsTable                List BBS entries
+
+  @retval EFI_SUCCESS             Tables returned
+  @retval EFI_NOT_FOUND           resource not found
+  @retval EFI_DEVICE_ERROR        can not get BBS table
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosGetBbsInfo (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  OUT UINT16                          *HddCount,
+  OUT HDD_INFO                        **HddInfo,
+  OUT UINT16                          *BbsCount,
+  OUT BBS_TABLE                       **BbsTable
+  )
+{
+  LEGACY_BIOS_INSTANCE              *Private;
+  EFI_IA32_REGISTER_SET             Regs;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
+//  HDD_INFO                          *LocalHddInfo;
+//  IN BBS_TABLE                      *LocalBbsTable;
+  UINTN                             NumHandles;
+  EFI_HANDLE                        *HandleBuffer;
+  UINTN                             Index;
+  UINTN                             TempData;
+  UINT32                            Granularity;
+
+  HandleBuffer            = NULL;
+
+  Private                 = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  EfiToLegacy16BootTable  = &Private->IntThunk->EfiToLegacy16BootTable;
+//  LocalHddInfo            = EfiToLegacy16BootTable->HddInfo;
+//  LocalBbsTable           = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
+
+  if (!mBbsTableDoneFlag) {
+    mBbsTable = Private->BbsTablePtr;
+
+    //
+    // Always enable disk controllers so 16-bit CSM code has valid information for all
+    // drives.
+    //
+    //
+    // Get PciRootBridgeIO protocol
+    //
+    gBS->LocateHandleBuffer (
+          ByProtocol,
+          &gEfiPciRootBridgeIoProtocolGuid,
+          NULL,
+          &NumHandles,
+          &HandleBuffer
+          );
+
+    if (NumHandles == 0) {
+      return EFI_NOT_FOUND;
+    }
+
+    mBbsTableDoneFlag = TRUE;
+    for (Index = 0; Index < NumHandles; Index++) {
+      //
+      // Connect PciRootBridgeIO protocol handle with FALSE parameter to let
+      // PCI bus driver enumerate all subsequent handles
+      //
+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE);
+
+    }
+
+    LegacyBiosBuildBbs (Private, mBbsTable);
+
+    Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xe0000, 0x20000, &Granularity);
+
+    //
+    // Call into Legacy16 code to add to BBS table for non BBS compliant OPROMs.
+    //
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+    Regs.X.AX = Legacy16UpdateBbs;
+
+    //
+    // Pass in handoff data
+    //
+    TempData  = (UINTN) EfiToLegacy16BootTable;
+    Regs.X.ES = NORMALIZE_EFI_SEGMENT ((UINT32) TempData);
+    Regs.X.BX = NORMALIZE_EFI_OFFSET ((UINT32) TempData);
+
+    Private->LegacyBios.FarCall86 (
+      This,
+      Private->Legacy16CallSegment,
+      Private->Legacy16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
+    Private->LegacyRegion->Lock (Private->LegacyRegion, 0xe0000, 0x20000, &Granularity);
+
+    if (Regs.X.AX != 0) {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool (HandleBuffer);
+  }
+
+  *HddCount = MAX_IDE_CONTROLLER;
+  *HddInfo  = EfiToLegacy16BootTable->HddInfo;
+  *BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
+  *BbsCount = (UINT16) (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE));
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBda.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBda.c
new file mode 100644
index 0000000000..aa6e07ab91
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBda.c
@@ -0,0 +1,62 @@
+/** @file
+  This code fills in BDA (0x400) and EBDA (pointed to by 0x4xx)
+  information. There is support for doing initializeation before
+  Legacy16 is loaded and before a legacy boot is attempted.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+/**
+  Fill in the standard BDA and EBDA stuff before Legacy16 load
+
+  @param  Private     Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosInitBda (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  )
+{
+  BDA_STRUC *Bda;
+  UINT8     *Ebda;
+
+  Bda   = (BDA_STRUC *) ((UINTN) 0x400);
+  Ebda  = (UINT8 *) ((UINTN) 0x9fc00);
+
+  ACCESS_PAGE0_CODE (
+    ZeroMem (Bda, 0x100);
+    //
+    // 640k-1k for EBDA
+    //
+    Bda->MemSize        = 0x27f;
+    Bda->KeyHead        = 0x1e;
+    Bda->KeyTail        = 0x1e;
+    Bda->FloppyData     = 0x00;
+    Bda->FloppyTimeout  = 0xff;
+
+    Bda->KeyStart       = 0x001E;
+    Bda->KeyEnd         = 0x003E;
+    Bda->KeyboardStatus = 0x10;
+    Bda->Ebda           = 0x9fc0;
+
+    //
+    // Move LPT time out here and zero out LPT4 since some SCSI OPROMS
+    // use this as scratch pad (LPT4 is Reserved)
+    //
+    Bda->Lpt1_2Timeout  = 0x1414;
+    Bda->Lpt3_4Timeout  = 0x1400;
+
+  );
+
+  ZeroMem (Ebda, 0x400);
+  *Ebda = 0x01;
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
new file mode 100644
index 0000000000..05e3ffd2bb
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
@@ -0,0 +1,1214 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+#define PHYSICAL_ADDRESS_TO_POINTER(Address)  ((VOID *) ((UINTN) Address))
+
+//
+// define maximum number of HDD system supports
+//
+#define MAX_HDD_ENTRIES 0x30
+
+//
+// Module Global:
+//  Since this driver will only ever produce one instance of the Private Data
+//  protocol you are not required to dynamically allocate the PrivateData.
+//
+LEGACY_BIOS_INSTANCE  mPrivateData;
+
+//
+// The SMBIOS table in EfiRuntimeServicesData memory
+//
+VOID                  *mRuntimeSmbiosEntryPoint = NULL;
+
+//
+// The SMBIOS table in EfiReservedMemoryType memory
+//
+EFI_PHYSICAL_ADDRESS  mReserveSmbiosEntryPoint = 0;
+EFI_PHYSICAL_ADDRESS  mStructureTableAddress   = 0;
+UINTN                 mStructureTablePages     = 0;
+BOOLEAN               mEndOfDxe                = FALSE;
+
+/**
+  Allocate memory for legacy usage. The memory is executable.
+
+  @param  AllocateType               The type of allocation to perform.
+  @param  MemoryType                 The type of memory to allocate.
+  @param  StartPageAddress           Start address of range
+  @param  Pages                      Number of pages to allocate
+  @param  Result                     Result of allocation
+
+  @retval EFI_SUCCESS                Legacy memory is allocated successfully.
+  @retval Other                      Legacy memory is not allocated.
+
+**/
+EFI_STATUS
+AllocateLegacyMemory (
+  IN  EFI_ALLOCATE_TYPE         AllocateType,
+  IN  EFI_MEMORY_TYPE           MemoryType,
+  IN  EFI_PHYSICAL_ADDRESS      StartPageAddress,
+  IN  UINTN                     Pages,
+  OUT EFI_PHYSICAL_ADDRESS      *Result
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_PHYSICAL_ADDRESS            MemPage;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;
+
+  //
+  // Allocate Pages of memory less <= StartPageAddress
+  //
+  MemPage = (EFI_PHYSICAL_ADDRESS) (UINTN) StartPageAddress;
+  Status = gBS->AllocatePages (
+                  AllocateType,
+                  MemoryType,
+                  Pages,
+                  &MemPage
+                  );
+  //
+  // Do not ASSERT on Status error but let caller decide since some cases
+  // memory is already taken but that is ok.
+  //
+  if (!EFI_ERROR (Status)) {
+    if (MemoryType != EfiBootServicesCode) {
+      //
+      // Make sure that the buffer can be used to store code.
+      //
+      Status = gDS->GetMemorySpaceDescriptor (MemPage, &MemDesc);
+      if (!EFI_ERROR (Status) && (MemDesc.Attributes & EFI_MEMORY_XP) != 0) {
+        Status = gDS->SetMemorySpaceAttributes (
+                        MemPage,
+                        EFI_PAGES_TO_SIZE (Pages),
+                        MemDesc.Attributes & (~EFI_MEMORY_XP)
+                        );
+      }
+      if (EFI_ERROR (Status)) {
+        gBS->FreePages (MemPage, Pages);
+      }
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    *Result = (EFI_PHYSICAL_ADDRESS) (UINTN) MemPage;
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000
+  64 KB blocks.
+
+  Note: inconsistency with the Framework CSM spec. Per the spec, this function may be
+  invoked only once. This limitation is relaxed to allow multiple calls in this implemenation.
+
+  @param  This                       Protocol instance pointer.
+  @param  LegacyMemorySize           Size of required region
+  @param  Region                     Region to use. 00 = Either 0xE0000 or 0xF0000
+                                     block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000
+                                     block
+  @param  Alignment                  Address alignment. Bit mapped. First non-zero
+                                     bit from right is alignment.
+  @param  LegacyMemoryAddress        Region Assigned
+
+  @retval EFI_SUCCESS                Region assigned
+  @retval EFI_ACCESS_DENIED          Procedure previously invoked
+  @retval Other                      Region not assigned
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosGetLegacyRegion (
+  IN    EFI_LEGACY_BIOS_PROTOCOL *This,
+  IN    UINTN                    LegacyMemorySize,
+  IN    UINTN                    Region,
+  IN    UINTN                    Alignment,
+  OUT   VOID                     **LegacyMemoryAddress
+  )
+{
+
+  LEGACY_BIOS_INSTANCE  *Private;
+  EFI_IA32_REGISTER_SET Regs;
+  EFI_STATUS            Status;
+  UINT32                Granularity;
+
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
+
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+  Regs.X.AX = Legacy16GetTableAddress;
+  Regs.X.BX = (UINT16) Region;
+  Regs.X.CX = (UINT16) LegacyMemorySize;
+  Regs.X.DX = (UINT16) Alignment;
+  Private->LegacyBios.FarCall86 (
+     &Private->LegacyBios,
+     Private->Legacy16CallSegment,
+     Private->Legacy16CallOffset,
+     &Regs,
+     NULL,
+     0
+     );
+
+  if (Regs.X.AX == 0) {
+    *LegacyMemoryAddress  = (VOID *) (((UINTN) Regs.X.DS << 4) + Regs.X.BX);
+    Status = EFI_SUCCESS;
+  } else {
+    Status = EFI_OUT_OF_RESOURCES;
+  }
+
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
+  Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
+
+  return Status;
+}
+
+
+/**
+  This function is called when copying data to the region assigned by
+  EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion().
+
+  @param  This                       Protocol instance pointer.
+  @param  LegacyMemorySize           Size of data to copy
+  @param  LegacyMemoryAddress        Legacy Region destination address Note: must
+                                     be in region assigned by
+                                     LegacyBiosGetLegacyRegion
+  @param  LegacyMemorySourceAddress  Source of data
+
+  @retval EFI_SUCCESS                The data was copied successfully.
+  @retval EFI_ACCESS_DENIED          Either the starting or ending address is out of bounds.
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosCopyLegacyRegion (
+  IN EFI_LEGACY_BIOS_PROTOCOL *This,
+  IN    UINTN                 LegacyMemorySize,
+  IN    VOID                  *LegacyMemoryAddress,
+  IN    VOID                  *LegacyMemorySourceAddress
+  )
+{
+
+  LEGACY_BIOS_INSTANCE  *Private;
+  UINT32                Granularity;
+
+  if ((LegacyMemoryAddress < (VOID *)(UINTN)0xE0000 ) ||
+      ((UINTN) LegacyMemoryAddress + LegacyMemorySize > (UINTN) 0x100000)
+        ) {
+    return EFI_ACCESS_DENIED;
+  }
+  //
+  // There is no protection from writes over lapping if this function is
+  // called multiple times.
+  //
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
+  CopyMem (LegacyMemoryAddress, LegacyMemorySourceAddress, LegacyMemorySize);
+
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
+  Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find
+  the $EFI table in the shadow area. Thunk into the Legacy16 code after it had
+  been shadowed.
+
+  @param  Private                    Legacy BIOS context data
+
+  @retval EFI_SUCCESS                Legacy16 code loaded
+  @retval Other                      No protocol installed, unload driver.
+
+**/
+EFI_STATUS
+ShadowAndStartLegacy16 (
+  IN  LEGACY_BIOS_INSTANCE  *Private
+  )
+{
+  EFI_STATUS                        Status;
+  UINT8                             *Ptr;
+  UINT8                             *PtrEnd;
+  BOOLEAN                           Done;
+  EFI_COMPATIBILITY16_TABLE         *Table;
+  UINT8                             CheckSum;
+  EFI_IA32_REGISTER_SET             Regs;
+  EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
+  VOID                              *LegacyBiosImage;
+  UINTN                             LegacyBiosImageSize;
+  UINTN                             E820Size;
+  UINT32                            *ClearPtr;
+  BBS_TABLE                         *BbsTable;
+  LEGACY_EFI_HDD_TABLE              *LegacyEfiHddTable;
+  UINTN                             Index;
+  UINT32                            TpmPointer;
+  VOID                              *TpmBinaryImage;
+  UINTN                             TpmBinaryImageSize;
+  UINTN                             Location;
+  UINTN                             Alignment;
+  UINTN                             TempData;
+  EFI_PHYSICAL_ADDRESS              Address;
+  UINT16                            OldMask;
+  UINT16                            NewMask;
+  UINT32                            Granularity;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;
+
+  Location  = 0;
+  Alignment = 0;
+
+  //
+  // we allocate the C/D/E/F segment as RT code so no one will use it any more.
+  //
+  Address = 0xC0000;
+  gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
+  if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {
+    //
+    // If it is already reserved, we should be safe, or else we allocate it.
+    //
+    Status = gBS->AllocatePages (
+                    AllocateAddress,
+                    EfiRuntimeServicesCode,
+                    0x40000/EFI_PAGE_SIZE,
+                    &Address
+                    );
+    if (EFI_ERROR (Status)) {
+      //
+      // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory.
+      //
+      DEBUG ((DEBUG_ERROR, "Failed to allocate the C/D/E/F segment Status = %r", Status));
+    }
+  }
+
+  //
+  // start testtest
+  //    GetTimerValue (&Ticker);
+  //
+  //  gRT->SetVariable (L"StartLegacy",
+  //                    &gEfiGlobalVariableGuid,
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+  //                    sizeof (UINT64),
+  //                    (VOID *)&Ticker
+  //                    );
+  // end testtest
+  //
+  EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformBinarySystemRom,
+                                          &LegacyBiosImage,
+                                          &LegacyBiosImageSize,
+                                          &Location,
+                                          &Alignment,
+                                          0,
+                                          0
+                                          );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Private->BiosStart            = (UINT32) (0x100000 - LegacyBiosImageSize);
+  Private->OptionRom            = 0xc0000;
+  Private->LegacyBiosImageSize  = (UINT32) LegacyBiosImageSize;
+
+  //
+  // Can only shadow into memory allocated for legacy useage.
+  //
+  ASSERT (Private->BiosStart > Private->OptionRom);
+
+  //
+  // Shadow Legacy BIOS. Turn on memory and copy image
+  //
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity);
+
+  ClearPtr = (VOID *) ((UINTN) 0xc0000);
+
+  //
+  // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused
+  // regions to be used by EMM386 etc.
+  //
+  SetMem ((VOID *) ClearPtr, (UINTN) (0x40000 - LegacyBiosImageSize), 0xff);
+
+  TempData = Private->BiosStart;
+
+  CopyMem (
+    (VOID *) TempData,
+    LegacyBiosImage,
+    (UINTN) LegacyBiosImageSize
+    );
+
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate);
+
+  //
+  // Search for Legacy16 table in Shadowed ROM
+  //
+  Done  = FALSE;
+  Table = NULL;
+  for (Ptr = (UINT8 *) TempData; Ptr < (UINT8 *) ((UINTN) 0x100000) && !Done; Ptr += 0x10) {
+    if (*(UINT32 *) Ptr == SIGNATURE_32 ('I', 'F', 'E', '$')) {
+      Table   = (EFI_COMPATIBILITY16_TABLE *) Ptr;
+      PtrEnd  = Ptr + Table->TableLength;
+      for (CheckSum = 0; Ptr < PtrEnd; Ptr++) {
+        CheckSum = (UINT8) (CheckSum +*Ptr);
+      }
+
+      Done = TRUE;
+    }
+  }
+
+  if (Table == NULL) {
+    DEBUG ((EFI_D_ERROR, "No Legacy16 table found\n"));
+    return EFI_NOT_FOUND;
+  }
+
+  if (!Done) {
+    //
+    // Legacy16 table header checksum error.
+    //
+    DEBUG ((EFI_D_ERROR, "Legacy16 table found with bad talbe header checksum\n"));
+  }
+
+  //
+  // Remember location of the Legacy16 table
+  //
+  Private->Legacy16Table            = Table;
+  Private->Legacy16CallSegment      = Table->Compatibility16CallSegment;
+  Private->Legacy16CallOffset       = Table->Compatibility16CallOffset;
+  EfiToLegacy16InitTable            = &Private->IntThunk->EfiToLegacy16InitTable;
+  Private->Legacy16InitPtr          = EfiToLegacy16InitTable;
+  Private->Legacy16BootPtr          = &Private->IntThunk->EfiToLegacy16BootTable;
+  Private->InternalIrqRoutingTable  = NULL;
+  Private->NumberIrqRoutingEntries  = 0;
+  Private->BbsTablePtr              = NULL;
+  Private->LegacyEfiHddTable        = NULL;
+  Private->DiskEnd                  = 0;
+  Private->Disk4075                 = 0;
+  Private->HddTablePtr              = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo;
+  Private->NumberHddControllers     = MAX_IDE_CONTROLLER;
+  Private->Dump[0]                  = 'D';
+  Private->Dump[1]                  = 'U';
+  Private->Dump[2]                  = 'M';
+  Private->Dump[3]                  = 'P';
+
+  ZeroMem (
+    Private->Legacy16BootPtr,
+    sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE)
+    );
+
+  //
+  // Store away a copy of the EFI System Table
+  //
+  Table->EfiSystemTable = (UINT32) (UINTN) gST;
+
+  //
+  // IPF CSM integration -Bug
+  //
+  // Construct the Legacy16 boot memory map. This sets up number of
+  // E820 entries.
+  //
+  LegacyBiosBuildE820 (Private, &E820Size);
+  //
+  // Initialize BDA and EBDA standard values needed to load Legacy16 code
+  //
+  LegacyBiosInitBda (Private);
+  LegacyBiosInitCmos (Private);
+
+  //
+  // All legacy interrupt should be masked when do initialization work from legacy 16 code.
+  //
+  Private->Legacy8259->GetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL);
+  NewMask = 0xFFFF;
+  Private->Legacy8259->SetMask(Private->Legacy8259, &NewMask, NULL, NULL, NULL);
+
+  //
+  // Call into Legacy16 code to do an INIT
+  //
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+  Regs.X.AX = Legacy16InitializeYourself;
+  Regs.X.ES = EFI_SEGMENT (*((UINT32 *) &EfiToLegacy16InitTable));
+  Regs.X.BX = EFI_OFFSET (*((UINT32 *) &EfiToLegacy16InitTable));
+
+  Private->LegacyBios.FarCall86 (
+    &Private->LegacyBios,
+    Table->Compatibility16CallSegment,
+    Table->Compatibility16CallOffset,
+    &Regs,
+    NULL,
+    0
+    );
+
+  //
+  // Restore original legacy interrupt mask value
+  //
+  Private->Legacy8259->SetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL);
+
+  if (Regs.X.AX != 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // start testtest
+  //  GetTimerValue (&Ticker);
+  //
+  //  gRT->SetVariable (L"BackFromInitYourself",
+  //                    &gEfiGlobalVariableGuid,
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+  //                    sizeof (UINT64),
+  //                    (VOID *)&Ticker
+  //                    );
+  // end testtest
+  //
+  // Copy E820 table after InitializeYourself is completed
+  //
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+  Regs.X.AX = Legacy16GetTableAddress;
+  Regs.X.CX = (UINT16) E820Size;
+  Regs.X.DX = 1;
+  Private->LegacyBios.FarCall86 (
+    &Private->LegacyBios,
+    Table->Compatibility16CallSegment,
+    Table->Compatibility16CallOffset,
+    &Regs,
+    NULL,
+    0
+    );
+
+  Table->E820Pointer  = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+  Table->E820Length   = (UINT32) E820Size;
+  if (Regs.X.AX != 0) {
+    DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n"));
+  } else {
+    TempData = Table->E820Pointer;
+    CopyMem ((VOID *) TempData, Private->E820Table, E820Size);
+  }
+  //
+  // Get PnPInstallationCheck Info.
+  //
+  Private->PnPInstallationCheckSegment  = Table->PnPInstallationCheckSegment;
+  Private->PnPInstallationCheckOffset   = Table->PnPInstallationCheckOffset;
+
+  //
+  // Check if PCI Express is supported. If yes, Save base address.
+  //
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformPciExpressBase,
+                                          NULL,
+                                          NULL,
+                                          &Location,
+                                          &Alignment,
+                                          0,
+                                          0
+                                          );
+  if (!EFI_ERROR (Status)) {
+    Private->Legacy16Table->PciExpressBase  = (UINT32)Location;
+    Location = 0;
+  }
+  //
+  // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it
+  // into, copy it and update pointer to binary image. This needs to be
+  // done prior to any OPROM for security purposes.
+  //
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformBinaryTpmBinary,
+                                          &TpmBinaryImage,
+                                          &TpmBinaryImageSize,
+                                          &Location,
+                                          &Alignment,
+                                          0,
+                                          0
+                                          );
+  if (!EFI_ERROR (Status)) {
+
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+    Regs.X.AX = Legacy16GetTableAddress;
+    Regs.X.CX = (UINT16) TpmBinaryImageSize;
+    Regs.X.DX = 1;
+    Private->LegacyBios.FarCall86 (
+      &Private->LegacyBios,
+      Table->Compatibility16CallSegment,
+      Table->Compatibility16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    TpmPointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+    if (Regs.X.AX != 0) {
+      DEBUG ((EFI_D_ERROR, "TPM cannot be loaded\n"));
+    } else {
+      CopyMem ((VOID *) (UINTN)TpmPointer, TpmBinaryImage, TpmBinaryImageSize);
+      Table->TpmSegment = Regs.X.DS;
+      Table->TpmOffset  = Regs.X.BX;
+
+    }
+  }
+  //
+  // Lock the Legacy BIOS region
+  //
+  Private->Cpu->FlushDataCache (Private->Cpu, Private->BiosStart, (UINT32) LegacyBiosImageSize, EfiCpuFlushTypeWriteBackInvalidate);
+  Private->LegacyRegion->Lock (Private->LegacyRegion, Private->BiosStart, (UINT32) LegacyBiosImageSize, &Granularity);
+
+  //
+  // Get the BbsTable from LOW_MEMORY_THUNK
+  //
+  BbsTable = (BBS_TABLE *)(UINTN)Private->IntThunk->BbsTable;
+  ZeroMem ((VOID *)BbsTable, sizeof (Private->IntThunk->BbsTable));
+
+  EfiToLegacy16BootTable->BbsTable  = (UINT32)(UINTN)BbsTable;
+  Private->BbsTablePtr              = (VOID *) BbsTable;
+  //
+  // Skip Floppy and possible onboard IDE drives
+  //
+  EfiToLegacy16BootTable->NumberBbsEntries = 1 + 2 * MAX_IDE_CONTROLLER;
+
+  for (Index = 0; Index < (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); Index++) {
+    BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY;
+  }
+  //
+  // Allocate space for Legacy HDD table
+  //
+  LegacyEfiHddTable = (LEGACY_EFI_HDD_TABLE *) AllocateZeroPool ((UINTN) MAX_HDD_ENTRIES * sizeof (LEGACY_EFI_HDD_TABLE));
+  ASSERT (LegacyEfiHddTable);
+
+  Private->LegacyEfiHddTable      = LegacyEfiHddTable;
+  Private->LegacyEfiHddTableIndex = 0x00;
+
+  //
+  // start testtest
+  //  GetTimerValue (&Ticker);
+  //
+  //  gRT->SetVariable (L"EndOfLoadFv",
+  //                    &gEfiGlobalVariableGuid,
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+  //                    sizeof (UINT64),
+  //                    (VOID *)&Ticker
+  //                    );
+  // end testtest
+  //
+  return EFI_SUCCESS;
+}
+
+/**
+  Shadow all legacy16 OPROMs that haven't been shadowed.
+  Warning: Use this with caution. This routine disconnects all EFI
+  drivers. If used externally then caller must re-connect EFI
+  drivers.
+
+  @param  This                    Protocol instance pointer.
+
+  @retval EFI_SUCCESS             OPROMs shadowed
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosShadowAllLegacyOproms (
+  IN EFI_LEGACY_BIOS_PROTOCOL *This
+  )
+{
+  LEGACY_BIOS_INSTANCE  *Private;
+
+  //
+  //  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL    *LegacyBiosPlatform;
+  //  EFI_LEGACY16_TABLE                   *Legacy16Table;
+  //
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+
+  //
+  //  LegacyBiosPlatform       = Private->LegacyBiosPlatform;
+  //  Legacy16Table            = Private->Legacy16Table;
+  //
+  // Shadow PCI ROMs. We must do this near the end since this will kick
+  // of Native EFI drivers that may be needed to collect info for Legacy16
+  //
+  //  WARNING: PciIo is gone after this call.
+  //
+  PciProgramAllInterruptLineRegisters (Private);
+
+  PciShadowRoms (Private);
+
+  //
+  // Shadow PXE base code, BIS etc.
+  //
+  //  LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform,
+  //                       &Private->OptionRom,
+  //                       Legacy16Table);
+  //
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the PCI BIOS interface version.
+
+  @param  Private  Driver private data.
+
+  @return The PCI interface version number in Binary Coded Decimal (BCD) format.
+          E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00
+
+**/
+UINT16
+GetPciInterfaceVersion (
+  IN LEGACY_BIOS_INSTANCE *Private
+  )
+{
+  EFI_IA32_REGISTER_SET Reg;
+  BOOLEAN               ThunkFailed;
+  UINT16                PciInterfaceVersion;
+
+  PciInterfaceVersion = 0;
+
+  Reg.X.AX = 0xB101;
+  Reg.E.EDI = 0;
+
+  ThunkFailed = Private->LegacyBios.Int86 (&Private->LegacyBios, 0x1A, &Reg);
+  if (!ThunkFailed) {
+    //
+    // From PCI Firmware 3.0 Specification:
+    //   If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the
+    //   contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the
+    //   presence of the PCI function set. [BX] will further indicate the version level, with enough
+    //   granularity to allow for incremental changes in the code that don't affect the function interface.
+    //   Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10
+    //   would be returned as a 02h in the [BH] registers and 10h in the [BL] registers.
+    //
+    if ((Reg.X.Flags.CF == 0) && (Reg.H.AH == 0) && (Reg.E.EDX == SIGNATURE_32 ('P', 'C', 'I', ' '))) {
+      PciInterfaceVersion = Reg.X.BX;
+    }
+  }
+  return PciInterfaceVersion;
+}
+
+/**
+  Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.
+  SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.
+
+  @param  Event                 Event whose notification function is being invoked.
+  @param  Context               The pointer to the notification function's context,
+                                which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+InstallSmbiosEventCallback (
+  IN EFI_EVENT                Event,
+  IN VOID                     *Context
+  )
+{
+  EFI_STATUS                  Status;
+  SMBIOS_TABLE_ENTRY_POINT    *EntryPointStructure;
+
+  //
+  // Get SMBIOS table from EFI configuration table
+  //
+  Status = EfiGetSystemConfigurationTable (
+            &gEfiSmbiosTableGuid,
+            &mRuntimeSmbiosEntryPoint
+            );
+  if ((EFI_ERROR (Status)) || (mRuntimeSmbiosEntryPoint == NULL)) {
+    return;
+  }
+
+  EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) mRuntimeSmbiosEntryPoint;
+
+  //
+  // Allocate memory for SMBIOS Entry Point Structure.
+  // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.
+  //
+  if (mReserveSmbiosEntryPoint == 0) {
+    //
+    // Entrypoint structure with fixed size is allocated only once.
+    //
+    mReserveSmbiosEntryPoint = SIZE_4GB - 1;
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiReservedMemoryType,
+                    EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength)),
+                    &mReserveSmbiosEntryPoint
+                    );
+    if (EFI_ERROR (Status)) {
+      mReserveSmbiosEntryPoint = 0;
+      return;
+    }
+    DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Entry Point Structure\n"));
+  }
+
+  if ((mStructureTableAddress != 0) &&
+      (mStructureTablePages < EFI_SIZE_TO_PAGES ((UINT32)EntryPointStructure->TableLength))) {
+    //
+    // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate
+    //
+    gBS->FreePages (mStructureTableAddress, mStructureTablePages);
+    mStructureTableAddress = 0;
+    mStructureTablePages   = 0;
+    DEBUG ((EFI_D_INFO, "Original size is not enough. Re-allocate the memory.\n"));
+  }
+
+  if (mStructureTableAddress == 0) {
+    //
+    // Allocate reserved memory below 4GB.
+    // Smbios spec requires the structure table is below 4GB.
+    //
+    mStructureTableAddress = SIZE_4GB - 1;
+    mStructureTablePages   = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiReservedMemoryType,
+                    mStructureTablePages,
+                    &mStructureTableAddress
+                    );
+    if (EFI_ERROR (Status)) {
+      gBS->FreePages (
+        mReserveSmbiosEntryPoint,
+        EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength))
+        );
+      mReserveSmbiosEntryPoint = 0;
+      mStructureTableAddress   = 0;
+      mStructureTablePages     = 0;
+      return;
+    }
+    DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Structure Table\n"));
+  }
+}
+
+/**
+  Callback function to toggle EndOfDxe status. NULL pointer detection needs
+  this status to decide if it's necessary to change attributes of page 0.
+
+  @param  Event            Event whose notification function is being invoked.
+  @param  Context          The pointer to the notification function's context,
+                           which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+ToggleEndOfDxeStatus (
+  IN EFI_EVENT                Event,
+  IN VOID                     *Context
+  )
+{
+  mEndOfDxe = TRUE;
+  return;
+}
+
+/**
+  Install Driver to produce Legacy BIOS protocol.
+
+  @param  ImageHandle  Handle of driver image.
+  @param  SystemTable  Pointer to system table.
+
+  @retval EFI_SUCCESS  Legacy BIOS protocol installed
+  @retval No protocol installed, unload driver.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosInstall (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS                         Status;
+  LEGACY_BIOS_INSTANCE               *Private;
+  EFI_TO_COMPATIBILITY16_INIT_TABLE  *EfiToLegacy16InitTable;
+  EFI_PHYSICAL_ADDRESS               MemoryAddress;
+  EFI_PHYSICAL_ADDRESS               EbdaReservedBaseAddress;
+  VOID                               *MemoryPtr;
+  EFI_PHYSICAL_ADDRESS               MemoryAddressUnder1MB;
+  UINTN                              Index;
+  UINT32                             *BaseVectorMaster;
+  EFI_PHYSICAL_ADDRESS               StartAddress;
+  UINT32                             *ClearPtr;
+  EFI_PHYSICAL_ADDRESS               MemStart;
+  UINT32                             IntRedirCode;
+  UINT32                             Granularity;
+  BOOLEAN                            DecodeOn;
+  UINT32                             MemorySize;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR    Descriptor;
+  UINT64                             Length;
+  UINT8                              *SecureBoot;
+  EFI_EVENT                          InstallSmbiosEvent;
+  EFI_EVENT                          EndOfDxeEvent;
+
+  //
+  // Load this driver's image to memory
+  //
+  Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // When UEFI Secure Boot is enabled, CSM module will not start any more.
+  //
+  SecureBoot = NULL;
+  GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);
+  if ((SecureBoot != NULL) && (*SecureBoot == SECURE_BOOT_MODE_ENABLE)) {
+    FreePool (SecureBoot);
+    return EFI_SECURITY_VIOLATION;
+  }
+
+  if (SecureBoot != NULL) {
+    FreePool (SecureBoot);
+  }
+
+  Private = &mPrivateData;
+  ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE));
+
+  //
+  // Grab a copy of all the protocols we depend on. Any error would
+  // be a dispatcher bug!.
+  //
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Private->Timer);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **) &Private->LegacyBiosPlatform);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &Private->Legacy8259);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **) &Private->LegacyInterrupt);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Locate Memory Test Protocol if exists
+  //
+  Status = gBS->LocateProtocol (
+                  &gEfiGenericMemTestProtocolGuid,
+                  NULL,
+                  (VOID **) &Private->GenericMemoryTest
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Make sure all memory from 0-640K is tested
+  //
+  for (StartAddress = 0; StartAddress < 0xa0000; ) {
+    gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
+    if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
+      StartAddress = Descriptor.BaseAddress + Descriptor.Length;
+      continue;
+    }
+    Length = MIN (Descriptor.Length, 0xa0000 - StartAddress);
+    Private->GenericMemoryTest->CompatibleRangeTest (
+                                  Private->GenericMemoryTest,
+                                  StartAddress,
+                                  Length
+                                  );
+    StartAddress = StartAddress + Length;
+  }
+  //
+  // Make sure all memory from 1MB to 16MB is tested and added to memory map
+  //
+  for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) {
+    gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
+    if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
+      StartAddress = Descriptor.BaseAddress + Descriptor.Length;
+      continue;
+    }
+    Length = MIN (Descriptor.Length, BASE_16MB - StartAddress);
+    Private->GenericMemoryTest->CompatibleRangeTest (
+                                  Private->GenericMemoryTest,
+                                  StartAddress,
+                                  Length
+                                  );
+    StartAddress = StartAddress + Length;
+  }
+
+  Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE;
+
+  Private->LegacyBios.Int86 = LegacyBiosInt86;
+  Private->LegacyBios.FarCall86 = LegacyBiosFarCall86;
+  Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom;
+  Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom;
+  Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot;
+  Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus;
+  Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo;
+  Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms;
+  Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi;
+  Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion;
+  Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion;
+  Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice;
+
+  Private->ImageHandle = ImageHandle;
+
+  //
+  // Enable read attribute of legacy region.
+  //
+  DecodeOn = TRUE;
+  Private->LegacyRegion->Decode (
+                           Private->LegacyRegion,
+                           0xc0000,
+                           0x40000,
+                           &Granularity,
+                           &DecodeOn
+                           );
+  //
+  // Set Cachebility for legacy region
+  // BUGBUG: Comments about this legacy region cacheability setting
+  //         This setting will make D865GCHProduction CSM Unhappy
+  //
+  if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) {
+    gDS->SetMemorySpaceAttributes (
+           0x0,
+           0xA0000,
+           EFI_MEMORY_WB
+           );
+    gDS->SetMemorySpaceAttributes (
+           0xc0000,
+           0x40000,
+           EFI_MEMORY_WB
+           );
+  }
+
+  gDS->SetMemorySpaceAttributes (
+         0xA0000,
+         0x20000,
+         EFI_MEMORY_UC
+         );
+
+  //
+  // Allocate 0 - 4K for real mode interupt vectors and BDA.
+  //
+  AllocateLegacyMemory (
+    AllocateAddress,
+    EfiReservedMemoryType,
+    0,
+    1,
+    &MemoryAddress
+    );
+  ASSERT (MemoryAddress == 0x000000000);
+
+  ClearPtr = (VOID *) ((UINTN) 0x0000);
+
+  //
+  // Initialize region from 0x0000 to 4k. This initializes interrupt vector
+  // range.
+  //
+  ACCESS_PAGE0_CODE (
+    gBS->SetMem ((VOID *) ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K);
+    ZeroMem ((VOID *) ((UINTN)ClearPtr + 0x400), 0xC00);
+  );
+
+  //
+  // Allocate pages for OPROM usage
+  //
+  MemorySize = PcdGet32 (PcdEbdaReservedMemorySize);
+  ASSERT ((MemorySize & 0xFFF) == 0);
+
+  Status = AllocateLegacyMemory (
+             AllocateAddress,
+             EfiReservedMemoryType,
+             CONVENTIONAL_MEMORY_TOP - MemorySize,
+             EFI_SIZE_TO_PAGES (MemorySize),
+             &MemoryAddress
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  ZeroMem ((VOID *) ((UINTN) MemoryAddress), MemorySize);
+
+  //
+  // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that
+  // don't use PMM but look for zeroed memory. Note that various non-BBS
+  // OpROMs expect different areas to be free
+  //
+  EbdaReservedBaseAddress = MemoryAddress;
+  MemoryAddress = PcdGet32 (PcdOpromReservedMemoryBase);
+  MemorySize    = PcdGet32 (PcdOpromReservedMemorySize);
+  //
+  // Check if base address and size for reserved memory are 4KB aligned.
+  //
+  ASSERT ((MemoryAddress & 0xFFF) == 0);
+  ASSERT ((MemorySize & 0xFFF) == 0);
+  //
+  // Check if the reserved memory is below EBDA reserved range.
+  //
+  ASSERT ((MemoryAddress < EbdaReservedBaseAddress) && ((MemoryAddress + MemorySize - 1) < EbdaReservedBaseAddress));
+  for (MemStart = MemoryAddress; MemStart < MemoryAddress + MemorySize; MemStart += 0x1000) {
+    Status = AllocateLegacyMemory (
+               AllocateAddress,
+               EfiBootServicesCode,
+               MemStart,
+               1,
+               &StartAddress
+               );
+    if (!EFI_ERROR (Status)) {
+      MemoryPtr = (VOID *) ((UINTN) StartAddress);
+      ZeroMem (MemoryPtr, 0x1000);
+    } else {
+      DEBUG ((EFI_D_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart));
+    }
+  }
+
+  //
+  // Allocate low PMM memory and zero it out
+  //
+  MemorySize = PcdGet32 (PcdLowPmmMemorySize);
+  ASSERT ((MemorySize & 0xFFF) == 0);
+  Status = AllocateLegacyMemory (
+             AllocateMaxAddress,
+             EfiBootServicesCode,
+             CONVENTIONAL_MEMORY_TOP,
+             EFI_SIZE_TO_PAGES (MemorySize),
+             &MemoryAddressUnder1MB
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  ZeroMem ((VOID *) ((UINTN) MemoryAddressUnder1MB), MemorySize);
+
+  //
+  // Allocate space for thunker and Init Thunker
+  //
+  Status = AllocateLegacyMemory (
+             AllocateMaxAddress,
+             EfiReservedMemoryType,
+             CONVENTIONAL_MEMORY_TOP,
+             (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2,
+             &MemoryAddress
+             );
+  ASSERT_EFI_ERROR (Status);
+  Private->IntThunk                   = (LOW_MEMORY_THUNK *) (UINTN) MemoryAddress;
+  EfiToLegacy16InitTable                   = &Private->IntThunk->EfiToLegacy16InitTable;
+  EfiToLegacy16InitTable->ThunkStart       = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;
+  EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32) (sizeof (LOW_MEMORY_THUNK));
+
+  Status = LegacyBiosInitializeThunk (Private);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Init the legacy memory map in memory < 1 MB.
+  //
+  EfiToLegacy16InitTable->BiosLessThan1MB         = (UINT32) MemoryAddressUnder1MB;
+  EfiToLegacy16InitTable->LowPmmMemory            = (UINT32) MemoryAddressUnder1MB;
+  EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = MemorySize;
+
+  MemorySize = PcdGet32 (PcdHighPmmMemorySize);
+  ASSERT ((MemorySize & 0xFFF) == 0);
+  //
+  // Allocate high PMM Memory under 16 MB
+  //
+  Status = AllocateLegacyMemory (
+             AllocateMaxAddress,
+             EfiBootServicesCode,
+             0x1000000,
+             EFI_SIZE_TO_PAGES (MemorySize),
+             &MemoryAddress
+             );
+  if (EFI_ERROR (Status)) {
+    //
+    // If it fails, allocate high PMM Memory under 4GB
+    //
+    Status = AllocateLegacyMemory (
+               AllocateMaxAddress,
+               EfiBootServicesCode,
+               0xFFFFFFFF,
+               EFI_SIZE_TO_PAGES (MemorySize),
+               &MemoryAddress
+               );
+  }
+  if (!EFI_ERROR (Status)) {
+    EfiToLegacy16InitTable->HiPmmMemory            = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;
+    EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = MemorySize;
+  }
+
+  //
+  //  ShutdownAPs();
+  //
+  // Start the Legacy BIOS;
+  //
+  Status = ShadowAndStartLegacy16 (Private);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Initialize interrupt redirection code and entries;
+  // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
+  //
+  CopyMem (
+         Private->IntThunk->InterruptRedirectionCode,
+         (VOID *) (UINTN) InterruptRedirectionTemplate,
+         sizeof (Private->IntThunk->InterruptRedirectionCode)
+         );
+
+  //
+  // Save Unexpected interrupt vector so can restore it just prior to boot
+  //
+  ACCESS_PAGE0_CODE (
+    BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
+    Private->BiosUnexpectedInt = BaseVectorMaster[0];
+    IntRedirCode = (UINT32) (UINTN) Private->IntThunk->InterruptRedirectionCode;
+    for (Index = 0; Index < 8; Index++) {
+      BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4);
+    }
+  );
+
+  //
+  // Save EFI value
+  //
+  Private->ThunkSeg = (UINT16) (EFI_SEGMENT (IntRedirCode));
+
+  //
+  // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists
+  //
+  InstallSmbiosEventCallback (NULL, NULL);
+
+  //
+  // Create callback function to update the size of reserved memory after LegacyBiosDxe starts
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  InstallSmbiosEventCallback,
+                  NULL,
+                  &gEfiSmbiosTableGuid,
+                  &InstallSmbiosEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Create callback to update status of EndOfDxe, which is needed by NULL
+  // pointer detection
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  ToggleEndOfDxeStatus,
+                  NULL,
+                  &gEfiEndOfDxeEventGroupGuid,
+                  &EndOfDxeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Make a new handle and install the protocol
+  //
+  Private->Handle = NULL;
+  Status = gBS->InstallProtocolInterface (
+                  &Private->Handle,
+                  &gEfiLegacyBiosProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Private->LegacyBios
+                  );
+  Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private);
+
+  DEBUG ((EFI_D_INFO, "CSM16 PCI BIOS Interface Version: %02x.%02x\n",
+          (UINT8) (Private->Csm16PciInterfaceVersion >> 8),
+          (UINT8) Private->Csm16PciInterfaceVersion
+        ));
+  ASSERT (Private->Csm16PciInterfaceVersion != 0);
+  return Status;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBootSupport.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBootSupport.c
new file mode 100644
index 0000000000..211750c012
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBootSupport.c
@@ -0,0 +1,2173 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+#include <IndustryStandard/Pci.h>
+
+#define BOOT_LEGACY_OS              0
+#define BOOT_EFI_OS                 1
+#define BOOT_UNCONVENTIONAL_DEVICE  2
+
+UINT32              mLoadOptionsSize    = 0;
+UINTN               mBootMode           = BOOT_LEGACY_OS;
+VOID                *mLoadOptions       = NULL;
+BBS_BBS_DEVICE_PATH *mBbsDevicePathPtr  = NULL;
+BBS_BBS_DEVICE_PATH mBbsDevicePathNode;
+UDC_ATTRIBUTES      mAttributes         = { 0, 0, 0, 0 };
+UINTN               mBbsEntry           = 0;
+VOID                *mBeerData          = NULL;
+VOID                *mServiceAreaData   = NULL;
+UINT64              mLowWater           = 0xffffffffffffffffULL;
+
+extern BBS_TABLE           *mBbsTable;
+
+extern VOID                  *mRuntimeSmbiosEntryPoint;
+extern EFI_PHYSICAL_ADDRESS  mReserveSmbiosEntryPoint;
+extern EFI_PHYSICAL_ADDRESS  mStructureTableAddress;
+
+/**
+  Print the BBS Table.
+
+  @param BbsTable   The BBS table.
+
+
+**/
+VOID
+PrintBbsTable (
+  IN BBS_TABLE *BbsTable
+  )
+{
+  UINT16 Index;
+  UINT16 SubIndex;
+  CHAR8  *String;
+
+  DEBUG ((EFI_D_INFO, "\n"));
+  DEBUG ((EFI_D_INFO, " NO  Prio bb/dd/ff cl/sc Type Stat segm:offs mfgs:mfgo dess:deso\n"));
+  DEBUG ((EFI_D_INFO, "=================================================================\n"));
+  for (Index = 0; Index < MAX_BBS_ENTRIES; Index++) {
+    //
+    // Filter
+    //
+    if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) {
+      continue;
+    }
+
+    DEBUG ((
+      EFI_D_INFO,
+      " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x",
+      (UINTN) Index,
+      (UINTN) BbsTable[Index].BootPriority,
+      (UINTN) BbsTable[Index].Bus,
+      (UINTN) BbsTable[Index].Device,
+      (UINTN) BbsTable[Index].Function,
+      (UINTN) BbsTable[Index].Class,
+      (UINTN) BbsTable[Index].SubClass,
+      (UINTN) BbsTable[Index].DeviceType,
+      (UINTN) * (UINT16 *) &BbsTable[Index].StatusFlags
+      ));
+    DEBUG ((
+      EFI_D_INFO,
+      " %04x:%04x %04x:%04x %04x:%04x",
+      (UINTN) BbsTable[Index].BootHandlerSegment,
+      (UINTN) BbsTable[Index].BootHandlerOffset,
+      (UINTN) BbsTable[Index].MfgStringSegment,
+      (UINTN) BbsTable[Index].MfgStringOffset,
+      (UINTN) BbsTable[Index].DescStringSegment,
+      (UINTN) BbsTable[Index].DescStringOffset
+      ));
+
+    //
+    // Print DescString
+    //
+    String = (CHAR8 *)(((UINTN)BbsTable[Index].DescStringSegment << 4) + BbsTable[Index].DescStringOffset);
+    if (String != NULL) {
+      DEBUG ((EFI_D_INFO," ("));
+      for (SubIndex = 0; String[SubIndex] != 0; SubIndex++) {
+        DEBUG ((EFI_D_INFO, "%c", String[SubIndex]));
+      }
+      DEBUG ((EFI_D_INFO,")"));
+    }
+    DEBUG ((EFI_D_INFO,"\n"));
+  }
+
+  DEBUG ((EFI_D_INFO, "\n"));
+
+  return ;
+}
+
+/**
+  Print the BBS Table.
+
+  @param HddInfo   The HddInfo table.
+
+
+**/
+VOID
+PrintHddInfo (
+  IN HDD_INFO *HddInfo
+  )
+{
+  UINTN Index;
+
+  DEBUG ((EFI_D_INFO, "\n"));
+  for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
+    DEBUG ((EFI_D_INFO, "Index - %04x\n", Index));
+    DEBUG ((EFI_D_INFO, "  Status    - %04x\n", (UINTN)HddInfo[Index].Status));
+    DEBUG ((EFI_D_INFO, "  B/D/F     - %02x/%02x/%02x\n", (UINTN)HddInfo[Index].Bus, (UINTN)HddInfo[Index].Device, (UINTN)HddInfo[Index].Function));
+    DEBUG ((EFI_D_INFO, "  Command   - %04x\n", HddInfo[Index].CommandBaseAddress));
+    DEBUG ((EFI_D_INFO, "  Control   - %04x\n", HddInfo[Index].ControlBaseAddress));
+    DEBUG ((EFI_D_INFO, "  BusMaster - %04x\n", HddInfo[Index].BusMasterAddress));
+    DEBUG ((EFI_D_INFO, "  HddIrq    - %02x\n", HddInfo[Index].HddIrq));
+    DEBUG ((EFI_D_INFO, "  IdentifyDrive[0].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[0].Raw[0]));
+    DEBUG ((EFI_D_INFO, "  IdentifyDrive[1].Raw[0] - %x\n", HddInfo[Index].IdentifyDrive[1].Raw[0]));
+  }
+
+  DEBUG ((EFI_D_INFO, "\n"));
+
+  return ;
+}
+
+/**
+  Print the PCI Interrupt Line and Interrupt Pin registers.
+**/
+VOID
+PrintPciInterruptRegister (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       Index;
+  EFI_HANDLE                  *Handles;
+  UINTN                       HandleNum;
+  EFI_PCI_IO_PROTOCOL         *PciIo;
+  UINT8                       Interrupt[2];
+  UINTN                       Segment;
+  UINTN                       Bus;
+  UINTN                       Device;
+  UINTN                       Function;
+
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiPciIoProtocolGuid,
+         NULL,
+         &HandleNum,
+         &Handles
+         );
+
+  Bus      = 0;
+  Device   = 0;
+  Function = 0;
+
+  DEBUG ((EFI_D_INFO, "\n"));
+  DEBUG ((EFI_D_INFO, " bb/dd/ff interrupt line interrupt pin\n"));
+  DEBUG ((EFI_D_INFO, "======================================\n"));
+  for (Index = 0; Index < HandleNum; Index++) {
+    Status = gBS->HandleProtocol (Handles[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
+    if (!EFI_ERROR (Status)) {
+      Status = PciIo->Pci.Read (
+                            PciIo,
+                            EfiPciIoWidthUint8,
+                            PCI_INT_LINE_OFFSET,
+                            2,
+                            Interrupt
+                            );
+    }
+    if (!EFI_ERROR (Status)) {
+      Status = PciIo->GetLocation (
+                        PciIo,
+                        &Segment,
+                        &Bus,
+                        &Device,
+                        &Function
+                        );
+    }
+    if (!EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_INFO, " %02x/%02x/%02x 0x%02x           0x%02x\n",
+              Bus, Device, Function, Interrupt[0], Interrupt[1]));
+    }
+  }
+  DEBUG ((EFI_D_INFO, "\n"));
+
+  if (Handles != NULL) {
+    FreePool (Handles);
+  }
+}
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+
+  @param  IdentifyDriveData       ATA Identify Data
+
+**/
+VOID
+UpdateIdentifyDriveData (
+  IN  UINT8     *IdentifyDriveData
+  );
+
+/**
+  Update SIO data.
+
+  @param  Private                 Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS             Removable media not present
+
+**/
+EFI_STATUS
+UpdateSioData (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  )
+{
+  EFI_STATUS                          Status;
+  UINTN                               Index;
+  UINTN                               Index1;
+  UINT8                               LegacyInterrupts[16];
+  EFI_LEGACY_IRQ_ROUTING_ENTRY        *RoutingTable;
+  UINTN                               RoutingTableEntries;
+  EFI_LEGACY_IRQ_PRIORITY_TABLE_ENTRY *IrqPriorityTable;
+  UINTN                               NumberPriorityEntries;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE   *EfiToLegacy16BootTable;
+  UINT8                               HddIrq;
+  UINT16                              LegacyInt;
+  UINT16                              LegMask;
+  UINT32                              Register;
+  UINTN                               HandleCount;
+  EFI_HANDLE                          *HandleBuffer;
+  EFI_ISA_IO_PROTOCOL                 *IsaIo;
+
+  LegacyInt               = 0;
+  HandleBuffer            = NULL;
+
+  EfiToLegacy16BootTable  = &Private->IntThunk->EfiToLegacy16BootTable;
+  LegacyBiosBuildSioData (Private);
+  SetMem (LegacyInterrupts, sizeof (LegacyInterrupts), 0);
+
+  //
+  // Create list of legacy interrupts.
+  //
+  for (Index = 0; Index < 4; Index++) {
+    LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Serial[Index].Irq;
+  }
+
+  for (Index = 4; Index < 7; Index++) {
+    LegacyInterrupts[Index] = EfiToLegacy16BootTable->SioData.Parallel[Index - 4].Irq;
+  }
+
+  LegacyInterrupts[7] = EfiToLegacy16BootTable->SioData.Floppy.Irq;
+
+  //
+  // Get Legacy Hdd IRQs. If native mode treat as PCI
+  //
+  for (Index = 0; Index < 2; Index++) {
+    HddIrq = EfiToLegacy16BootTable->HddInfo[Index].HddIrq;
+    if ((HddIrq != 0) && ((HddIrq == 15) || (HddIrq == 14))) {
+      LegacyInterrupts[Index + 8] = HddIrq;
+    }
+  }
+
+  Private->LegacyBiosPlatform->GetRoutingTable (
+                                Private->LegacyBiosPlatform,
+                                (VOID *) &RoutingTable,
+                                &RoutingTableEntries,
+                                NULL,
+                                NULL,
+                                (VOID **) &IrqPriorityTable,
+                                &NumberPriorityEntries
+                                );
+  //
+  // Remove legacy interrupts from the list of PCI interrupts available.
+  //
+  for (Index = 0; Index <= 0x0b; Index++) {
+    for (Index1 = 0; Index1 <= NumberPriorityEntries; Index1++) {
+      if (LegacyInterrupts[Index] != 0) {
+        LegacyInt = (UINT16) (LegacyInt | (1 << LegacyInterrupts[Index]));
+        if (LegacyInterrupts[Index] == IrqPriorityTable[Index1].Irq) {
+          IrqPriorityTable[Index1].Used = LEGACY_USED;
+        }
+      }
+    }
+  }
+
+  Private->Legacy8259->GetMask (
+                        Private->Legacy8259,
+                        &LegMask,
+                        NULL,
+                        NULL,
+                        NULL
+                        );
+
+  //
+  // Set SIO interrupts and disable mouse. Let mouse driver
+  // re-enable it.
+  //
+  LegMask = (UINT16) ((LegMask &~LegacyInt) | 0x1000);
+  Private->Legacy8259->SetMask (
+                        Private->Legacy8259,
+                        &LegMask,
+                        NULL,
+                        NULL,
+                        NULL
+                        );
+
+  //
+  // Disable mouse in keyboard controller
+  //
+  Register = 0xA7;
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiIsaIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiIsaIoProtocolGuid,
+                    (VOID **) &IsaIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+    IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, 0x64, 1, &Register);
+
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool (HandleBuffer);
+  }
+
+  return EFI_SUCCESS;
+
+}
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+  This requires updating the checksum, if it exists.
+
+  @param  IdentifyDriveData       ATA Identify Data
+  @param  Checksum                checksum of the ATA Identify Data
+
+  @retval EFI_SUCCESS             checksum calculated
+  @retval EFI_SECURITY_VIOLATION  IdentifyData invalid
+
+**/
+EFI_STATUS
+CalculateIdentifyDriveChecksum (
+  IN  UINT8     *IdentifyDriveData,
+  OUT UINT8     *Checksum
+  )
+{
+  UINTN Index;
+  UINT8 LocalChecksum;
+  LocalChecksum = 0;
+  *Checksum     = 0;
+  if (IdentifyDriveData[510] != 0xA5) {
+    return EFI_SECURITY_VIOLATION;
+  }
+
+  for (Index = 0; Index < 512; Index++) {
+    LocalChecksum = (UINT8) (LocalChecksum + IdentifyDriveData[Index]);
+  }
+
+  *Checksum = LocalChecksum;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+
+  @param  IdentifyDriveData       ATA Identify Data
+
+
+**/
+VOID
+UpdateIdentifyDriveData (
+  IN  UINT8     *IdentifyDriveData
+  )
+{
+  UINT16          NumberCylinders;
+  UINT16          NumberHeads;
+  UINT16          NumberSectorsTrack;
+  UINT32          CapacityInSectors;
+  UINT8           OriginalChecksum;
+  UINT8           FinalChecksum;
+  EFI_STATUS      Status;
+  ATAPI_IDENTIFY  *ReadInfo;
+
+  //
+  // Status indicates if Integrity byte is correct. Checksum should be
+  // 0 if valid.
+  //
+  ReadInfo  = (ATAPI_IDENTIFY *) IdentifyDriveData;
+  Status    = CalculateIdentifyDriveChecksum (IdentifyDriveData, &OriginalChecksum);
+  if (OriginalChecksum != 0) {
+    Status = EFI_SECURITY_VIOLATION;
+  }
+  //
+  // If NumberCylinders = 0 then do data(Controller present but don drive attached).
+  //
+  NumberCylinders = ReadInfo->Raw[1];
+  if (NumberCylinders != 0) {
+    ReadInfo->Raw[54]   = NumberCylinders;
+
+    NumberHeads         = ReadInfo->Raw[3];
+    ReadInfo->Raw[55]   = NumberHeads;
+
+    NumberSectorsTrack  = ReadInfo->Raw[6];
+    ReadInfo->Raw[56]   = NumberSectorsTrack;
+
+    //
+    // Copy Multisector info and set valid bit.
+    //
+    ReadInfo->Raw[59] = (UINT16) (ReadInfo->Raw[47] + 0x100);
+    CapacityInSectors = (UINT32) ((UINT32) (NumberCylinders) * (UINT32) (NumberHeads) * (UINT32) (NumberSectorsTrack));
+    ReadInfo->Raw[57] = (UINT16) (CapacityInSectors >> 16);
+    ReadInfo->Raw[58] = (UINT16) (CapacityInSectors & 0xffff);
+    if (Status == EFI_SUCCESS) {
+      //
+      // Forece checksum byte to 0 and get new checksum.
+      //
+      ReadInfo->Raw[255] &= 0xff;
+      CalculateIdentifyDriveChecksum (IdentifyDriveData, &FinalChecksum);
+
+      //
+      // Force new checksum such that sum is 0.
+      //
+      FinalChecksum = (UINT8) ((UINT8)0 - FinalChecksum);
+      ReadInfo->Raw[255] = (UINT16) (ReadInfo->Raw[255] | (FinalChecksum << 8));
+    }
+  }
+}
+
+/**
+  Identify drive data must be updated to actual parameters before boot.
+  Do for all drives.
+
+  @param  Private                 Legacy BIOS Instance data
+
+
+**/
+VOID
+UpdateAllIdentifyDriveData (
+  IN LEGACY_BIOS_INSTANCE                 *Private
+  )
+{
+  UINTN     Index;
+  HDD_INFO  *HddInfo;
+
+  HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0];
+
+  for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
+    //
+    // Each controller can have 2 devices. Update for each device
+    //
+    if ((HddInfo[Index].Status & HDD_MASTER_IDE) != 0) {
+      UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[0].Raw[0]));
+    }
+
+    if ((HddInfo[Index].Status & HDD_SLAVE_IDE) != 0) {
+      UpdateIdentifyDriveData ((UINT8 *) (&HddInfo[Index].IdentifyDrive[1].Raw[0]));
+    }
+  }
+}
+
+/**
+  Enable ide controller.  This gets disabled when LegacyBoot.c is about
+  to run the Option ROMs.
+
+  @param  Private        Legacy BIOS Instance data
+
+
+**/
+VOID
+EnableIdeController (
+  IN LEGACY_BIOS_INSTANCE              *Private
+  )
+{
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  EFI_STATUS          Status;
+  EFI_HANDLE          IdeController;
+  UINT8               ByteBuffer;
+  UINTN               HandleCount;
+  EFI_HANDLE          *HandleBuffer;
+
+  Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformIdeHandle,
+                                          0,
+                                          &HandleBuffer,
+                                          &HandleCount,
+                                          NULL
+                                          );
+  if (!EFI_ERROR (Status)) {
+    IdeController = HandleBuffer[0];
+    Status = gBS->HandleProtocol (
+                    IdeController,
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    ByteBuffer = 0x1f;
+    if (!EFI_ERROR (Status)) {
+      PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x04, 1, &ByteBuffer);
+    }
+  }
+}
+
+
+/**
+  Enable ide controller.  This gets disabled when LegacyBoot.c is about
+  to run the Option ROMs.
+
+  @param  Private                 Legacy BIOS Instance data
+
+
+**/
+VOID
+EnableAllControllers (
+  IN LEGACY_BIOS_INSTANCE              *Private
+  )
+{
+  UINTN               HandleCount;
+  EFI_HANDLE          *HandleBuffer;
+  UINTN               Index;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE01          PciConfigHeader;
+  EFI_STATUS          Status;
+
+  //
+  //
+  //
+  EnableIdeController (Private);
+
+  //
+  // Assumption is table is built from low bus to high bus numbers.
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    PciIo->Pci.Read (
+                PciIo,
+                EfiPciIoWidthUint32,
+                0,
+                sizeof (PciConfigHeader) / sizeof (UINT32),
+                &PciConfigHeader
+                );
+
+    //
+    // We do not enable PPB here. This is for HotPlug Consideration.
+    // The Platform HotPlug Driver is responsible for Padding enough hot plug
+    // resources. It is also responsible for enable this bridge. If it
+    // does not pad it. It will cause some early Windows fail to installation.
+    // If the platform driver does not pad resource for PPB, PPB should be in
+    // un-enabled state to let Windows know that this PPB is not configured by
+    // BIOS. So Windows will allocate default resource for PPB.
+    //
+    // The reason for why we enable the command register is:
+    // The CSM will use the IO bar to detect some IRQ status, if the command
+    // is disabled, the IO resource will be out of scope.
+    // For example:
+    // We installed a legacy IRQ handle for a PCI IDE controller. When IRQ
+    // comes up, the handle will check the IO space to identify is the
+    // controller generated the IRQ source.
+    // If the IO command is not enabled, the IRQ handler will has wrong
+    // information. It will cause IRQ storm when the correctly IRQ handler fails
+    // to run.
+    //
+    if (!(IS_PCI_VGA (&PciConfigHeader)     ||
+          IS_PCI_OLD_VGA (&PciConfigHeader) ||
+          IS_PCI_IDE (&PciConfigHeader)     ||
+          IS_PCI_P2P (&PciConfigHeader)     ||
+          IS_PCI_P2P_SUB (&PciConfigHeader) ||
+          IS_PCI_LPC (&PciConfigHeader)     )) {
+
+      PciConfigHeader.Hdr.Command |= 0x1f;
+
+      PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 4, 1, &PciConfigHeader.Hdr.Command);
+    }
+  }
+}
+
+/**
+  The following routines are identical in operation, so combine
+  for code compaction:
+  EfiGetPlatformBinaryGetMpTable
+  EfiGetPlatformBinaryGetOemIntData
+  EfiGetPlatformBinaryGetOem32Data
+  EfiGetPlatformBinaryGetOem16Data
+
+  @param  This                    Protocol instance pointer.
+  @param  Id                      Table/Data identifier
+
+  @retval EFI_SUCCESS             Success
+  @retval EFI_INVALID_PARAMETER   Invalid ID
+  @retval EFI_OUT_OF_RESOURCES    no resource to get data or table
+
+**/
+EFI_STATUS
+LegacyGetDataOrTable (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  IN EFI_GET_PLATFORM_INFO_MODE       Id
+  )
+{
+  VOID                              *Table;
+  UINT32                            TablePtr;
+  UINTN                             TableSize;
+  UINTN                             Alignment;
+  UINTN                             Location;
+  EFI_STATUS                        Status;
+  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
+  EFI_COMPATIBILITY16_TABLE         *Legacy16Table;
+  EFI_IA32_REGISTER_SET             Regs;
+  LEGACY_BIOS_INSTANCE              *Private;
+
+  Private             = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+
+  LegacyBiosPlatform  = Private->LegacyBiosPlatform;
+  Legacy16Table       = Private->Legacy16Table;
+
+  //
+  // Phase 1 - get an address allocated in 16-bit code
+  //
+  while (TRUE) {
+    switch (Id) {
+    case EfiGetPlatformBinaryMpTable:
+    case EfiGetPlatformBinaryOemIntData:
+    case EfiGetPlatformBinaryOem32Data:
+    case EfiGetPlatformBinaryOem16Data:
+      {
+        Status = LegacyBiosPlatform->GetPlatformInfo (
+                                      LegacyBiosPlatform,
+                                      Id,
+                                      (VOID *) &Table,
+                                      &TableSize,
+                                      &Location,
+                                      &Alignment,
+                                      0,
+                                      0
+                                      );
+        DEBUG ((EFI_D_INFO, "LegacyGetDataOrTable - ID: %x, %r\n", (UINTN)Id, Status));
+        DEBUG ((EFI_D_INFO, "  Table - %x, Size - %x, Location - %x, Alignment - %x\n", (UINTN)Table, (UINTN)TableSize, (UINTN)Location, (UINTN)Alignment));
+        break;
+      }
+
+    default:
+      {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+    Regs.X.AX = Legacy16GetTableAddress;
+    Regs.X.CX = (UINT16) TableSize;
+    Regs.X.BX = (UINT16) Location;
+    Regs.X.DX = (UINT16) Alignment;
+    Private->LegacyBios.FarCall86 (
+      This,
+      Private->Legacy16CallSegment,
+      Private->Legacy16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    if (Regs.X.AX != 0) {
+      DEBUG ((EFI_D_ERROR, "Table ID %x length insufficient\n", Id));
+      return EFI_OUT_OF_RESOURCES;
+    } else {
+      break;
+    }
+  }
+  //
+  // Phase 2 Call routine second time with address to allow address adjustment
+  //
+  Status = LegacyBiosPlatform->GetPlatformInfo (
+                                LegacyBiosPlatform,
+                                Id,
+                                (VOID *) &Table,
+                                &TableSize,
+                                &Location,
+                                &Alignment,
+                                Regs.X.DS,
+                                Regs.X.BX
+                                );
+  switch (Id) {
+  case EfiGetPlatformBinaryMpTable:
+    {
+      Legacy16Table->MpTablePtr     = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+      Legacy16Table->MpTableLength  = (UINT32)TableSize;
+      DEBUG ((EFI_D_INFO, "MP table in legacy region - %x\n", (UINTN)Legacy16Table->MpTablePtr));
+      break;
+    }
+
+  case EfiGetPlatformBinaryOemIntData:
+    {
+
+      Legacy16Table->OemIntSegment  = Regs.X.DS;
+      Legacy16Table->OemIntOffset   = Regs.X.BX;
+      DEBUG ((EFI_D_INFO, "OemInt table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->OemIntSegment, (UINTN)Legacy16Table->OemIntOffset));
+      break;
+    }
+
+  case EfiGetPlatformBinaryOem32Data:
+    {
+      Legacy16Table->Oem32Segment = Regs.X.DS;
+      Legacy16Table->Oem32Offset  = Regs.X.BX;
+      DEBUG ((EFI_D_INFO, "Oem32 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem32Segment, (UINTN)Legacy16Table->Oem32Offset));
+      break;
+    }
+
+  case EfiGetPlatformBinaryOem16Data:
+    {
+      //
+      //          Legacy16Table->Oem16Segment = Regs.X.DS;
+      //          Legacy16Table->Oem16Offset  = Regs.X.BX;
+      DEBUG ((EFI_D_INFO, "Oem16 table in legacy region - %04x:%04x\n", (UINTN)Legacy16Table->Oem16Segment, (UINTN)Legacy16Table->Oem16Offset));
+      break;
+    }
+
+  default:
+    {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Phase 3 Copy table to final location
+  //
+  TablePtr = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+
+  CopyMem (
+    (VOID *) (UINTN)TablePtr,
+    Table,
+    TableSize
+    );
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Copy SMBIOS table to EfiReservedMemoryType of memory for legacy boot.
+
+**/
+VOID
+CreateSmbiosTableInReservedMemory (
+  VOID
+  )
+{
+  SMBIOS_TABLE_ENTRY_POINT    *EntryPointStructure;
+
+  if ((mRuntimeSmbiosEntryPoint == NULL) ||
+      (mReserveSmbiosEntryPoint == 0) ||
+      (mStructureTableAddress == 0)) {
+    return;
+  }
+
+  EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) mRuntimeSmbiosEntryPoint;
+
+  //
+  // Copy SMBIOS Entry Point Structure
+  //
+  CopyMem (
+    (VOID *)(UINTN) mReserveSmbiosEntryPoint,
+    EntryPointStructure,
+    EntryPointStructure->EntryPointLength
+  );
+
+  //
+  // Copy SMBIOS Structure Table into EfiReservedMemoryType memory
+  //
+  CopyMem (
+    (VOID *)(UINTN) mStructureTableAddress,
+    (VOID *)(UINTN) EntryPointStructure->TableAddress,
+    EntryPointStructure->TableLength
+  );
+
+  //
+  // Update TableAddress in Entry Point Structure
+  //
+  EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *)(UINTN) mReserveSmbiosEntryPoint;
+  EntryPointStructure->TableAddress = (UINT32)(UINTN) mStructureTableAddress;
+
+  //
+  // Fixup checksums in the Entry Point Structure
+  //
+  EntryPointStructure->IntermediateChecksum = 0;
+  EntryPointStructure->EntryPointStructureChecksum = 0;
+
+  EntryPointStructure->IntermediateChecksum =
+    CalculateCheckSum8 (
+      (UINT8 *) EntryPointStructure + OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString),
+      EntryPointStructure->EntryPointLength - OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString)
+      );
+  EntryPointStructure->EntryPointStructureChecksum =
+    CalculateCheckSum8 ((UINT8 *) EntryPointStructure, EntryPointStructure->EntryPointLength);
+}
+
+/**
+  Assign drive number to legacy HDD drives prior to booting an EFI
+  aware OS so the OS can access drives without an EFI driver.
+  Note: BBS compliant drives ARE NOT available until this call by
+  either shell or EFI.
+
+  @param  This                    Protocol instance pointer.
+
+  @retval EFI_SUCCESS             Drive numbers assigned
+
+**/
+EFI_STATUS
+GenericLegacyBoot (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This
+  )
+{
+  EFI_STATUS                        Status;
+  LEGACY_BIOS_INSTANCE              *Private;
+  EFI_IA32_REGISTER_SET             Regs;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
+  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
+  UINTN                             CopySize;
+  VOID                              *AcpiPtr;
+  HDD_INFO                          *HddInfo;
+  HDD_INFO                          *LocalHddInfo;
+  UINTN                             Index;
+  EFI_COMPATIBILITY16_TABLE         *Legacy16Table;
+  UINT32                            *BdaPtr;
+  UINT16                            HddCount;
+  UINT16                            BbsCount;
+  BBS_TABLE                         *LocalBbsTable;
+  UINT32                            *BaseVectorMaster;
+  EFI_TIME                          BootTime;
+  UINT32                            LocalTime;
+  EFI_HANDLE                        IdeController;
+  UINTN                             HandleCount;
+  EFI_HANDLE                        *HandleBuffer;
+  VOID                              *AcpiTable;
+  UINTN                             ShadowAddress;
+  UINT32                            Granularity;
+
+  LocalHddInfo  = NULL;
+  HddCount      = 0;
+  BbsCount      = 0;
+  LocalBbsTable = NULL;
+
+  Private       = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  DEBUG_CODE (
+    DEBUG ((EFI_D_ERROR, "Start of legacy boot\n"));
+  );
+
+  Legacy16Table                         = Private->Legacy16Table;
+  EfiToLegacy16BootTable                = &Private->IntThunk->EfiToLegacy16BootTable;
+  HddInfo = &EfiToLegacy16BootTable->HddInfo[0];
+
+  LegacyBiosPlatform = Private->LegacyBiosPlatform;
+
+  EfiToLegacy16BootTable->MajorVersion = EFI_TO_LEGACY_MAJOR_VERSION;
+  EfiToLegacy16BootTable->MinorVersion = EFI_TO_LEGACY_MINOR_VERSION;
+
+  //
+  // If booting to a legacy OS then force HDD drives to the appropriate
+  // boot mode by calling GetIdeHandle.
+  // A reconnect -r can force all HDDs back to native mode.
+  //
+  IdeController = NULL;
+  if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
+    Status = LegacyBiosPlatform->GetPlatformHandle (
+                                  Private->LegacyBiosPlatform,
+                                  EfiGetPlatformIdeHandle,
+                                  0,
+                                  &HandleBuffer,
+                                  &HandleCount,
+                                  NULL
+                                  );
+    if (!EFI_ERROR (Status)) {
+      IdeController = HandleBuffer[0];
+    }
+  }
+  //
+  // Unlock the Legacy BIOS region
+  //
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           0xE0000,
+                           0x20000,
+                           &Granularity
+                           );
+
+  //
+  // Reconstruct the Legacy16 boot memory map
+  //
+  LegacyBiosBuildE820 (Private, &CopySize);
+  if (CopySize > Private->Legacy16Table->E820Length) {
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+    Regs.X.AX = Legacy16GetTableAddress;
+    Regs.X.CX = (UINT16) CopySize;
+    Private->LegacyBios.FarCall86 (
+      &Private->LegacyBios,
+      Private->Legacy16Table->Compatibility16CallSegment,
+      Private->Legacy16Table->Compatibility16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    Private->Legacy16Table->E820Pointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+    Private->Legacy16Table->E820Length  = (UINT32) CopySize;
+    if (Regs.X.AX != 0) {
+      DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n"));
+    } else {
+      CopyMem (
+        (VOID *)(UINTN) Private->Legacy16Table->E820Pointer,
+        Private->E820Table,
+        CopySize
+        );
+    }
+  } else {
+    CopyMem (
+      (VOID *)(UINTN) Private->Legacy16Table->E820Pointer,
+      Private->E820Table,
+      CopySize
+      );
+    Private->Legacy16Table->E820Length = (UINT32) CopySize;
+  }
+
+  //
+  // We do not ASSERT if SmbiosTable not found. It is possbile that a platform does not produce SmbiosTable.
+  //
+  if (mReserveSmbiosEntryPoint == 0) {
+    DEBUG ((EFI_D_INFO, "Smbios table is not found!\n"));
+  }
+  CreateSmbiosTableInReservedMemory ();
+  EfiToLegacy16BootTable->SmbiosTable = (UINT32)(UINTN)mReserveSmbiosEntryPoint;
+
+  AcpiTable = NULL;
+  Status = EfiGetSystemConfigurationTable (
+             &gEfiAcpi20TableGuid,
+             &AcpiTable
+             );
+  if (EFI_ERROR (Status)) {
+    Status = EfiGetSystemConfigurationTable (
+               &gEfiAcpi10TableGuid,
+               &AcpiTable
+               );
+  }
+  //
+  // We do not ASSERT if AcpiTable not found. It is possbile that a platform does not produce AcpiTable.
+  //
+  if (AcpiTable == NULL) {
+    DEBUG ((EFI_D_INFO, "ACPI table is not found!\n"));
+  }
+  EfiToLegacy16BootTable->AcpiTable = (UINT32)(UINTN)AcpiTable;
+
+  //
+  // Get RSD Ptr table rev at offset 15 decimal
+  // Rev = 0 Length is 20 decimal
+  // Rev != 0 Length is UINT32 at offset 20 decimal
+  //
+  if (AcpiTable != NULL) {
+
+    AcpiPtr = AcpiTable;
+    if (*((UINT8 *) AcpiPtr + 15) == 0) {
+      CopySize = 20;
+    } else {
+      AcpiPtr   = ((UINT8 *) AcpiPtr + 20);
+      CopySize  = (*(UINT32 *) AcpiPtr);
+    }
+
+    CopyMem (
+      (VOID *)(UINTN) Private->Legacy16Table->AcpiRsdPtrPointer,
+      AcpiTable,
+      CopySize
+      );
+  }
+  //
+  // Make sure all PCI Interrupt Line register are programmed to match 8259
+  //
+  PciProgramAllInterruptLineRegisters (Private);
+
+  //
+  // Unlock the Legacy BIOS region as PciProgramAllInterruptLineRegisters
+  // can lock it.
+  //
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           Private->BiosStart,
+                           Private->LegacyBiosImageSize,
+                           &Granularity
+                           );
+
+  //
+  // Configure Legacy Device Magic
+  //
+  // Only do this code if booting legacy OS
+  //
+  if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
+    UpdateSioData (Private);
+  }
+  //
+  // Setup BDA and EBDA standard areas before Legacy Boot
+  //
+  ACCESS_PAGE0_CODE (
+    LegacyBiosCompleteBdaBeforeBoot (Private);
+  );
+  LegacyBiosCompleteStandardCmosBeforeBoot (Private);
+
+  //
+  // We must build IDE data, if it hasn't been done, before PciShadowRoms
+  // to insure EFI drivers are connected.
+  //
+  LegacyBiosBuildIdeData (Private, &HddInfo, 1);
+  UpdateAllIdentifyDriveData (Private);
+
+  //
+  // Clear IO BAR, if IDE controller in legacy mode.
+  //
+  InitLegacyIdeController (IdeController);
+
+  //
+  // Generate number of ticks since midnight for BDA. DOS requires this
+  // for its time. We have to make assumptions as to how long following
+  // code takes since after PciShadowRoms PciIo is gone. Place result in
+  // 40:6C-6F
+  //
+  // Adjust value by 1 second.
+  //
+  gRT->GetTime (&BootTime, NULL);
+  LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second;
+  LocalTime += 1;
+
+  //
+  // Multiply result by 18.2 for number of ticks since midnight.
+  // Use 182/10 to avoid floating point math.
+  //
+  LocalTime = (LocalTime * 182) / 10;
+  ACCESS_PAGE0_CODE (
+    BdaPtr    = (UINT32 *) (UINTN)0x46C;
+    *BdaPtr   = LocalTime;
+  );
+
+  //
+  // Shadow PCI ROMs. We must do this near the end since this will kick
+  // of Native EFI drivers that may be needed to collect info for Legacy16
+  //
+  //  WARNING: PciIo is gone after this call.
+  //
+  PciShadowRoms (Private);
+
+  //
+  // Shadow PXE base code, BIS etc.
+  //
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity);
+  ShadowAddress = Private->OptionRom;
+  Private->LegacyBiosPlatform->PlatformHooks (
+                                 Private->LegacyBiosPlatform,
+                                 EfiPlatformHookShadowServiceRoms,
+                                 0,
+                                 0,
+                                 &ShadowAddress,
+                                 Legacy16Table,
+                                 NULL
+                                 );
+  Private->OptionRom = (UINT32)ShadowAddress;
+  //
+  // Register Legacy SMI Handler
+  //
+  LegacyBiosPlatform->SmmInit (
+                        LegacyBiosPlatform,
+                        EfiToLegacy16BootTable
+                        );
+
+  //
+  // Let platform code know the boot options
+  //
+  LegacyBiosGetBbsInfo (
+    This,
+    &HddCount,
+    &LocalHddInfo,
+    &BbsCount,
+    &LocalBbsTable
+    );
+
+  DEBUG_CODE (
+    PrintPciInterruptRegister ();
+    PrintBbsTable (LocalBbsTable);
+    PrintHddInfo (LocalHddInfo);
+    );
+  //
+  // If drive wasn't spun up then BuildIdeData may have found new drives.
+  // Need to update BBS boot priority.
+  //
+  for (Index = 0; Index < MAX_IDE_CONTROLLER; Index++) {
+    if ((LocalHddInfo[Index].IdentifyDrive[0].Raw[0] != 0) &&
+        (LocalBbsTable[2 * Index + 1].BootPriority == BBS_IGNORE_ENTRY)
+        ) {
+      LocalBbsTable[2 * Index + 1].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+    }
+
+    if ((LocalHddInfo[Index].IdentifyDrive[1].Raw[0] != 0) &&
+        (LocalBbsTable[2 * Index + 2].BootPriority == BBS_IGNORE_ENTRY)
+        ) {
+      LocalBbsTable[2 * Index + 2].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+    }
+  }
+
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           0xc0000,
+                           0x40000,
+                           &Granularity
+                           );
+
+  LegacyBiosPlatform->PrepareToBoot (
+                        LegacyBiosPlatform,
+                        mBbsDevicePathPtr,
+                        mBbsTable,
+                        mLoadOptionsSize,
+                        mLoadOptions,
+                        (VOID *) &Private->IntThunk->EfiToLegacy16BootTable
+                        );
+
+  //
+  // If no boot device return to BDS
+  //
+  if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
+    for (Index = 0; Index < BbsCount; Index++){
+      if ((LocalBbsTable[Index].BootPriority != BBS_DO_NOT_BOOT_FROM) &&
+          (LocalBbsTable[Index].BootPriority != BBS_UNPRIORITIZED_ENTRY) &&
+          (LocalBbsTable[Index].BootPriority != BBS_IGNORE_ENTRY)) {
+        break;
+      }
+    }
+    if (Index == BbsCount) {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+  //
+  // Let the Legacy16 code know the device path type for legacy boot
+  //
+  EfiToLegacy16BootTable->DevicePathType = mBbsDevicePathPtr->DeviceType;
+
+  //
+  // Copy MP table, if it exists.
+  //
+  LegacyGetDataOrTable (This, EfiGetPlatformBinaryMpTable);
+
+  if (!Private->LegacyBootEntered) {
+    //
+    // Copy OEM INT Data, if it exists. Note: This code treats any data
+    // as a bag of bits and knows nothing of the contents nor cares.
+    // Contents are IBV specific.
+    //
+    LegacyGetDataOrTable (This, EfiGetPlatformBinaryOemIntData);
+
+    //
+    // Copy OEM16 Data, if it exists.Note: This code treats any data
+    // as a bag of bits and knows nothing of the contents nor cares.
+    // Contents are IBV specific.
+    //
+    LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem16Data);
+
+    //
+    // Copy OEM32 Data, if it exists.Note: This code treats any data
+    // as a bag of bits and knows nothing of the contents nor cares.
+    // Contents are IBV specific.
+    //
+    LegacyGetDataOrTable (This, EfiGetPlatformBinaryOem32Data);
+  }
+
+  //
+  // Call into Legacy16 code to prepare for INT 19h
+  //
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+  Regs.X.AX = Legacy16PrepareToBoot;
+
+  //
+  // Pass in handoff data
+  //
+  Regs.X.ES = NORMALIZE_EFI_SEGMENT ((UINTN)EfiToLegacy16BootTable);
+  Regs.X.BX = NORMALIZE_EFI_OFFSET ((UINTN)EfiToLegacy16BootTable);
+
+  Private->LegacyBios.FarCall86 (
+    This,
+    Private->Legacy16CallSegment,
+    Private->Legacy16CallOffset,
+    &Regs,
+    NULL,
+    0
+    );
+
+  if (Regs.X.AX != 0) {
+    return EFI_DEVICE_ERROR;
+  }
+  //
+  // Lock the Legacy BIOS region
+  //
+  Private->LegacyRegion->Lock (
+                           Private->LegacyRegion,
+                           0xc0000,
+                           0x40000,
+                           &Granularity
+                           );
+
+  if ((Private->Legacy16Table->TableLength >= OFFSET_OF (EFI_COMPATIBILITY16_TABLE, HiPermanentMemoryAddress)) &&
+      ((Private->Legacy16Table->UmaAddress != 0) && (Private->Legacy16Table->UmaSize != 0))) {
+    //
+    // Here we could reduce UmaAddress down as far as Private->OptionRom, taking into
+    // account the granularity of the access control.
+    //
+    DEBUG((EFI_D_INFO, "Unlocking UMB RAM region 0x%x-0x%x\n", Private->Legacy16Table->UmaAddress,
+                        Private->Legacy16Table->UmaAddress + Private->Legacy16Table->UmaSize));
+
+    Private->LegacyRegion->UnLock (
+                             Private->LegacyRegion,
+                             Private->Legacy16Table->UmaAddress,
+                             Private->Legacy16Table->UmaSize,
+                             &Granularity
+                             );
+  }
+
+  //
+  // Lock attributes of the Legacy Region if chipset supports
+  //
+  Private->LegacyRegion->BootLock (
+                           Private->LegacyRegion,
+                           0xc0000,
+                           0x40000,
+                           &Granularity
+                           );
+
+  //
+  // Call into Legacy16 code to do the INT 19h
+  //
+  EnableAllControllers (Private);
+  if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
+
+    //
+    // Signal all the events that are waiting on EVT_SIGNAL_LEGACY_BOOT
+    //
+    EfiSignalEventLegacyBoot ();
+
+    //
+    // Report Status Code to indicate legacy boot event was signalled
+    //
+    REPORT_STATUS_CODE (
+      EFI_PROGRESS_CODE,
+      (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_LEGACY_BOOT_EVENT)
+      );
+
+    DEBUG ((EFI_D_INFO, "Legacy INT19 Boot...\n"));
+
+    //
+    // Disable DXE Timer while executing in real mode
+    //
+    Private->Timer->SetTimerPeriod (Private->Timer, 0);
+
+    //
+    // Save and disable interrupt of debug timer
+    //
+    SaveAndSetDebugTimerInterrupt (FALSE);
+
+
+    //
+    // Put the 8259 into its legacy mode by reprogramming the vector bases
+    //
+    Private->Legacy8259->SetVectorBase (Private->Legacy8259, LEGACY_MODE_BASE_VECTOR_MASTER, LEGACY_MODE_BASE_VECTOR_SLAVE);
+    //
+    // PC History
+    //   The original PC used INT8-F for master PIC. Since these mapped over
+    //   processor exceptions TIANO moved the master PIC to INT68-6F.
+    // We need to set these back to the Legacy16 unexpected interrupt(saved
+    // in LegacyBios.c) since some OS see that these have values different from
+    // what is expected and invoke them. Since the legacy OS corrupts EFI
+    // memory, there is no handler for these interrupts and OS blows up.
+    //
+    // We need to save the TIANO values for the rare case that the Legacy16
+    // code cannot boot but knows memory hasn't been destroyed.
+    //
+    // To compound the problem, video takes over one of these INTS and must be
+    // be left.
+    // @bug - determine if video hooks INT(in which case we must find new
+    //          set of TIANO vectors) or takes it over.
+    //
+    //
+    ACCESS_PAGE0_CODE (
+      BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
+      for (Index = 0; Index < 8; Index++) {
+        Private->ThunkSavedInt[Index] = BaseVectorMaster[Index];
+        if (Private->ThunkSeg == (UINT16) (BaseVectorMaster[Index] >> 16)) {
+          BaseVectorMaster[Index] = (UINT32) (Private->BiosUnexpectedInt);
+        }
+      }
+    );
+
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+    Regs.X.AX = Legacy16Boot;
+
+    Private->LegacyBios.FarCall86 (
+      This,
+      Private->Legacy16CallSegment,
+      Private->Legacy16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    ACCESS_PAGE0_CODE (
+      BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
+      for (Index = 0; Index < 8; Index++) {
+        BaseVectorMaster[Index] = Private->ThunkSavedInt[Index];
+      }
+    );
+  }
+  Private->LegacyBootEntered = TRUE;
+  if ((mBootMode == BOOT_LEGACY_OS) || (mBootMode == BOOT_UNCONVENTIONAL_DEVICE)) {
+    //
+    // Should never return unless never passed control to 0:7c00(first stage
+    // OS loader) and only then if no bootable device found.
+    //
+    return EFI_DEVICE_ERROR;
+  } else {
+    //
+    // If boot to EFI then expect to return to caller
+    //
+    return EFI_SUCCESS;
+  }
+}
+
+
+/**
+  Assign drive number to legacy HDD drives prior to booting an EFI
+  aware OS so the OS can access drives without an EFI driver.
+  Note: BBS compliant drives ARE NOT available until this call by
+  either shell or EFI.
+
+  @param  This                    Protocol instance pointer.
+  @param  BbsCount                Number of BBS_TABLE structures
+  @param  BbsTable                List BBS entries
+
+  @retval EFI_SUCCESS             Drive numbers assigned
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosPrepareToBootEfi (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  OUT UINT16                          *BbsCount,
+  OUT BBS_TABLE                       **BbsTable
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
+  LEGACY_BIOS_INSTANCE              *Private;
+
+  Private                 = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  EfiToLegacy16BootTable  = &Private->IntThunk->EfiToLegacy16BootTable;
+  mBootMode               = BOOT_EFI_OS;
+  mBbsDevicePathPtr       = NULL;
+  Status                  = GenericLegacyBoot (This);
+  *BbsTable               = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
+  *BbsCount               = (UINT16) (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE));
+  return Status;
+}
+
+/**
+  To boot from an unconventional device like parties and/or execute HDD diagnostics.
+
+  @param  This            Protocol instance pointer.
+  @param  Attributes      How to interpret the other input parameters
+  @param  BbsEntry        The 0-based index into the BbsTable for the parent
+                          device.
+  @param  BeerData        Pointer to the 128 bytes of ram BEER data.
+  @param  ServiceAreaData Pointer to the 64 bytes of raw Service Area data. The
+                          caller must provide a pointer to the specific Service
+                          Area and not the start all Service Areas.
+
+  @retval EFI_INVALID_PARAMETER if error. Does NOT return if no error.
+
+***/
+EFI_STATUS
+EFIAPI
+LegacyBiosBootUnconventionalDevice (
+  IN EFI_LEGACY_BIOS_PROTOCOL         *This,
+  IN UDC_ATTRIBUTES                   Attributes,
+  IN UINTN                            BbsEntry,
+  IN VOID                             *BeerData,
+  IN VOID                             *ServiceAreaData
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
+  LEGACY_BIOS_INSTANCE              *Private;
+  UD_TABLE                          *UcdTable;
+  UINTN                             Index;
+  UINT16                            BootPriority;
+  BBS_TABLE                         *BbsTable;
+
+  BootPriority = 0;
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  mBootMode = BOOT_UNCONVENTIONAL_DEVICE;
+  mBbsDevicePathPtr = &mBbsDevicePathNode;
+  mAttributes = Attributes;
+  mBbsEntry = BbsEntry;
+  mBeerData = BeerData, mServiceAreaData = ServiceAreaData;
+
+  EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
+
+  //
+  // Do input parameter checking
+  //
+  if ((Attributes.DirectoryServiceValidity == 0) &&
+      (Attributes.RabcaUsedFlag == 0) &&
+      (Attributes.ExecuteHddDiagnosticsFlag == 0)
+      ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((Attributes.DirectoryServiceValidity != 0) && (ServiceAreaData == NULL)) ||
+      (((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag) != 0) && (BeerData == NULL))
+      ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  UcdTable = (UD_TABLE *) AllocatePool (
+                            sizeof (UD_TABLE)
+                            );
+  if (NULL == UcdTable) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  EfiToLegacy16BootTable->UnconventionalDeviceTable = (UINT32)(UINTN)UcdTable;
+  UcdTable->Attributes = Attributes;
+  UcdTable->BbsTableEntryNumberForParentDevice = (UINT8) BbsEntry;
+  //
+  // Force all existing BBS entries to DoNotBoot. This allows 16-bit CSM
+  // to assign drive numbers but bot boot from. Only newly created entries
+  // will be valid.
+  //
+  BbsTable = (BBS_TABLE*)(UINTN)EfiToLegacy16BootTable->BbsTable;
+  for (Index = 0; Index < EfiToLegacy16BootTable->NumberBbsEntries; Index++) {
+    BbsTable[Index].BootPriority = BBS_DO_NOT_BOOT_FROM;
+  }
+  //
+  // If parent is onboard IDE then assign controller & device number
+  // else they are 0.
+  //
+  if (BbsEntry < MAX_IDE_CONTROLLER * 2) {
+    UcdTable->DeviceNumber = (UINT8) ((BbsEntry - 1) % 2);
+  }
+
+  if (BeerData != NULL) {
+    CopyMem (
+      (VOID *) UcdTable->BeerData,
+      BeerData,
+      (UINTN) 128
+      );
+  }
+
+  if (ServiceAreaData != NULL) {
+    CopyMem (
+      (VOID *) UcdTable->ServiceAreaData,
+      ServiceAreaData,
+      (UINTN) 64
+      );
+  }
+  //
+  // For each new entry do the following:
+  //   1. Increment current number of BBS entries
+  //   2. Copy parent entry to new entry.
+  //   3. Zero out BootHandler Offset & segment
+  //   4. Set appropriate device type. BEV(0x80) for HDD diagnostics
+  //      and Floppy(0x01) for PARTIES boot.
+  //   5. Assign new priority.
+  //
+  if ((Attributes.ExecuteHddDiagnosticsFlag) != 0) {
+    EfiToLegacy16BootTable->NumberBbsEntries += 1;
+
+    CopyMem (
+      (VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority,
+      (VOID *) &BbsTable[BbsEntry].BootPriority,
+      sizeof (BBS_TABLE)
+      );
+
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset  = 0;
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0;
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType         = 0x80;
+
+    UcdTable->BbsTableEntryNumberForHddDiag = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1);
+
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority;
+    BootPriority += 1;
+
+    //
+    // Set device type as BBS_TYPE_DEV for PARTIES diagnostic
+    //
+    mBbsDevicePathNode.DeviceType = BBS_TYPE_BEV;
+  }
+
+  if (((Attributes.DirectoryServiceValidity | Attributes.RabcaUsedFlag)) != 0) {
+    EfiToLegacy16BootTable->NumberBbsEntries += 1;
+    CopyMem (
+      (VOID *) &BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority,
+      (VOID *) &BbsTable[BbsEntry].BootPriority,
+      sizeof (BBS_TABLE)
+      );
+
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerOffset  = 0;
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootHandlerSegment = 0;
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].DeviceType         = 0x01;
+    UcdTable->BbsTableEntryNumberForBoot = (UINT8) (EfiToLegacy16BootTable->NumberBbsEntries - 1);
+    BbsTable[EfiToLegacy16BootTable->NumberBbsEntries].BootPriority = BootPriority;
+
+    //
+    // Set device type as BBS_TYPE_FLOPPY for PARTIES boot as floppy
+    //
+    mBbsDevicePathNode.DeviceType = BBS_TYPE_FLOPPY;
+  }
+  //
+  // Build the BBS Device Path for this boot selection
+  //
+  mBbsDevicePathNode.Header.Type    = BBS_DEVICE_PATH;
+  mBbsDevicePathNode.Header.SubType = BBS_BBS_DP;
+  SetDevicePathNodeLength (&mBbsDevicePathNode.Header, sizeof (BBS_BBS_DEVICE_PATH));
+  mBbsDevicePathNode.StatusFlag = 0;
+  mBbsDevicePathNode.String[0]  = 0;
+
+  Status                        = GenericLegacyBoot (This);
+  return Status;
+}
+
+/**
+  Attempt to legacy boot the BootOption. If the EFI contexted has been
+  compromised this function will not return.
+
+  @param  This             Protocol instance pointer.
+  @param  BbsDevicePath    EFI Device Path from BootXXXX variable.
+  @param  LoadOptionsSize  Size of LoadOption in size.
+  @param  LoadOptions      LoadOption from BootXXXX variable
+
+  @retval EFI_SUCCESS      Removable media not present
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosLegacyBoot (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN  BBS_BBS_DEVICE_PATH               *BbsDevicePath,
+  IN  UINT32                            LoadOptionsSize,
+  IN  VOID                              *LoadOptions
+  )
+{
+  EFI_STATUS  Status;
+
+  mBbsDevicePathPtr = BbsDevicePath;
+  mLoadOptionsSize  = LoadOptionsSize;
+  mLoadOptions      = LoadOptions;
+  mBootMode         = BOOT_LEGACY_OS;
+  Status            = GenericLegacyBoot (This);
+
+  return Status;
+}
+
+/**
+  Convert EFI Memory Type to E820 Memory Type.
+
+  @param  Type  EFI Memory Type
+
+  @return ACPI Memory Type for EFI Memory Type
+
+**/
+EFI_ACPI_MEMORY_TYPE
+EfiMemoryTypeToE820Type (
+  IN  UINT32    Type
+  )
+{
+  switch (Type) {
+  case EfiLoaderCode:
+  case EfiLoaderData:
+  case EfiBootServicesCode:
+  case EfiBootServicesData:
+  case EfiConventionalMemory:
+  //
+  // The memory of EfiRuntimeServicesCode and EfiRuntimeServicesData are
+  // usable memory for legacy OS, because legacy OS is not aware of EFI runtime concept.
+  // In ACPI specification, EfiRuntimeServiceCode and EfiRuntimeServiceData
+  // should be mapped to AddressRangeReserved. This statement is for UEFI OS, not for legacy OS.
+  //
+  case EfiRuntimeServicesCode:
+  case EfiRuntimeServicesData:
+    return EfiAcpiAddressRangeMemory;
+
+  case EfiPersistentMemory:
+    return EfiAddressRangePersistentMemory;
+
+  case EfiACPIReclaimMemory:
+    return EfiAcpiAddressRangeACPI;
+
+  case EfiACPIMemoryNVS:
+    return EfiAcpiAddressRangeNVS;
+
+  //
+  // All other types map to reserved.
+  // Adding the code just waists FLASH space.
+  //
+  //  case  EfiReservedMemoryType:
+  //  case  EfiUnusableMemory:
+  //  case  EfiMemoryMappedIO:
+  //  case  EfiMemoryMappedIOPortSpace:
+  //  case  EfiPalCode:
+  //
+  default:
+    return EfiAcpiAddressRangeReserved;
+  }
+}
+
+/**
+  Build the E820 table.
+
+  @param  Private  Legacy BIOS Instance data
+  @param  Size     Size of E820 Table
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildE820 (
+  IN  LEGACY_BIOS_INSTANCE    *Private,
+  OUT UINTN                   *Size
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_E820_ENTRY64            *E820Table;
+  EFI_MEMORY_DESCRIPTOR       *EfiMemoryMap;
+  EFI_MEMORY_DESCRIPTOR       *EfiMemoryMapEnd;
+  EFI_MEMORY_DESCRIPTOR       *EfiEntry;
+  EFI_MEMORY_DESCRIPTOR       *NextEfiEntry;
+  EFI_MEMORY_DESCRIPTOR       TempEfiEntry;
+  UINTN                       EfiMemoryMapSize;
+  UINTN                       EfiMapKey;
+  UINTN                       EfiDescriptorSize;
+  UINT32                      EfiDescriptorVersion;
+  UINTN                       Index;
+  EFI_PEI_HOB_POINTERS        Hob;
+  EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;
+  UINTN                       TempIndex;
+  UINTN                       IndexSort;
+  UINTN                       TempNextIndex;
+  EFI_E820_ENTRY64            TempE820;
+  EFI_ACPI_MEMORY_TYPE        TempType;
+  BOOLEAN                     ChangedFlag;
+  UINTN                       Above1MIndex;
+  UINT64                      MemoryBlockLength;
+
+  E820Table = (EFI_E820_ENTRY64 *) Private->E820Table;
+
+  //
+  // Get the EFI memory map.
+  //
+  EfiMemoryMapSize  = 0;
+  EfiMemoryMap      = NULL;
+  Status = gBS->GetMemoryMap (
+                  &EfiMemoryMapSize,
+                  EfiMemoryMap,
+                  &EfiMapKey,
+                  &EfiDescriptorSize,
+                  &EfiDescriptorVersion
+                  );
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+
+  do {
+    //
+    // Use size returned for the AllocatePool.
+    // We don't just multiply by 2 since the "for" loop below terminates on
+    // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwise
+    // we process bogus entries and create bogus E820 entries.
+    //
+    EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);
+    ASSERT (EfiMemoryMap != NULL);
+    Status = gBS->GetMemoryMap (
+                    &EfiMemoryMapSize,
+                    EfiMemoryMap,
+                    &EfiMapKey,
+                    &EfiDescriptorSize,
+                    &EfiDescriptorVersion
+                    );
+    if (EFI_ERROR (Status)) {
+      FreePool (EfiMemoryMap);
+    }
+  } while (Status == EFI_BUFFER_TOO_SMALL);
+
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Punch in the E820 table for memory less than 1 MB.
+  // Assume ZeroMem () has been done on data structure.
+  //
+  //
+  // First entry is 0 to (640k - EBDA)
+  //
+  ACCESS_PAGE0_CODE (
+    E820Table[0].BaseAddr  = 0;
+    E820Table[0].Length    = (UINT64) ((*(UINT16 *) (UINTN)0x40E) << 4);
+    E820Table[0].Type      = EfiAcpiAddressRangeMemory;
+  );
+
+  //
+  // Second entry is (640k - EBDA) to 640k
+  //
+  E820Table[1].BaseAddr  = E820Table[0].Length;
+  E820Table[1].Length    = (UINT64) ((640 * 1024) - E820Table[0].Length);
+  E820Table[1].Type      = EfiAcpiAddressRangeReserved;
+
+  //
+  // Third Entry is legacy BIOS
+  // DO NOT CLAIM region from 0xA0000-0xDFFFF. OS can use free areas
+  // to page in memory under 1MB.
+  // Omit region from 0xE0000 to start of BIOS, if any. This can be
+  // used for a multiple reasons including OPROMS.
+  //
+
+  //
+  // The CSM binary image size is not the actually size that CSM binary used,
+  // to avoid memory corrupt, we declare the 0E0000 - 0FFFFF is used by CSM binary.
+  //
+  E820Table[2].BaseAddr  = 0xE0000;
+  E820Table[2].Length    = 0x20000;
+  E820Table[2].Type      = EfiAcpiAddressRangeReserved;
+
+  Above1MIndex = 2;
+
+  //
+  // Process the EFI map to produce E820 map;
+  //
+
+  //
+  // Sort memory map from low to high
+  //
+  EfiEntry        = EfiMemoryMap;
+  NextEfiEntry    = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
+  EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
+  while (EfiEntry < EfiMemoryMapEnd) {
+    while (NextEfiEntry < EfiMemoryMapEnd) {
+      if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {
+        CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
+        CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
+        CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
+      }
+
+      NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);
+    }
+
+    EfiEntry      = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
+    NextEfiEntry  = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
+  }
+
+  EfiEntry        = EfiMemoryMap;
+  EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
+  for (Index = Above1MIndex; (EfiEntry < EfiMemoryMapEnd) && (Index < EFI_MAX_E820_ENTRY - 1); ) {
+    MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));
+    if ((EfiEntry->PhysicalStart + MemoryBlockLength) < 0x100000) {
+      //
+      // Skip the memory block if under 1MB
+      //
+    } else {
+      if (EfiEntry->PhysicalStart < 0x100000) {
+        //
+        // When the memory block spans below 1MB, ensure the memory block start address is at least 1MB
+        //
+        MemoryBlockLength       -= 0x100000 - EfiEntry->PhysicalStart;
+        EfiEntry->PhysicalStart =  0x100000;
+      }
+
+      //
+      // Convert memory type to E820 type
+      //
+      TempType = EfiMemoryTypeToE820Type (EfiEntry->Type);
+
+      if ((E820Table[Index].Type == TempType) && (EfiEntry->PhysicalStart == (E820Table[Index].BaseAddr + E820Table[Index].Length))) {
+        //
+        // Grow an existing entry
+        //
+        E820Table[Index].Length += MemoryBlockLength;
+      } else {
+        //
+        // Make a new entry
+        //
+        ++Index;
+        E820Table[Index].BaseAddr  = EfiEntry->PhysicalStart;
+        E820Table[Index].Length    = MemoryBlockLength;
+        E820Table[Index].Type      = TempType;
+      }
+    }
+    EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
+  }
+
+  FreePool (EfiMemoryMap);
+
+  //
+  // Process the reserved memory map to produce E820 map ;
+  //
+  for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
+    if (Hob.Raw != NULL && GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+      ResourceHob = Hob.ResourceDescriptor;
+      if (((ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO) ||
+          (ResourceHob->ResourceType == EFI_RESOURCE_FIRMWARE_DEVICE)  ||
+          (ResourceHob->ResourceType == EFI_RESOURCE_MEMORY_RESERVED)    ) &&
+          (ResourceHob->PhysicalStart > 0x100000) &&
+          (Index < EFI_MAX_E820_ENTRY - 1)) {
+        ++Index;
+        E820Table[Index].BaseAddr  = ResourceHob->PhysicalStart;
+        E820Table[Index].Length    = ResourceHob->ResourceLength;
+        E820Table[Index].Type      = EfiAcpiAddressRangeReserved;
+      }
+    }
+  }
+
+  Index ++;
+  Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index;
+  Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index;
+  Private->NumberE820Entries = (UINT32)Index;
+  *Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64));
+
+  //
+  // Sort E820Table from low to high
+  //
+  for (TempIndex = 0; TempIndex < Index; TempIndex++) {
+    ChangedFlag = FALSE;
+    for (TempNextIndex = 1; TempNextIndex < Index - TempIndex; TempNextIndex++) {
+      if (E820Table[TempNextIndex - 1].BaseAddr > E820Table[TempNextIndex].BaseAddr) {
+        ChangedFlag                       = TRUE;
+        TempE820.BaseAddr                 = E820Table[TempNextIndex - 1].BaseAddr;
+        TempE820.Length                   = E820Table[TempNextIndex - 1].Length;
+        TempE820.Type                     = E820Table[TempNextIndex - 1].Type;
+
+        E820Table[TempNextIndex - 1].BaseAddr  = E820Table[TempNextIndex].BaseAddr;
+        E820Table[TempNextIndex - 1].Length    = E820Table[TempNextIndex].Length;
+        E820Table[TempNextIndex - 1].Type      = E820Table[TempNextIndex].Type;
+
+        E820Table[TempNextIndex].BaseAddr      = TempE820.BaseAddr;
+        E820Table[TempNextIndex].Length        = TempE820.Length;
+        E820Table[TempNextIndex].Type          = TempE820.Type;
+      }
+    }
+
+    if (!ChangedFlag) {
+      break;
+    }
+  }
+
+  //
+  // Remove the overlap range
+  //
+  for (TempIndex = 1; TempIndex < Index; TempIndex++) {
+    if (E820Table[TempIndex - 1].BaseAddr <= E820Table[TempIndex].BaseAddr &&
+        ((E820Table[TempIndex - 1].BaseAddr + E820Table[TempIndex - 1].Length) >=
+         (E820Table[TempIndex].BaseAddr +E820Table[TempIndex].Length))) {
+        //
+        //Overlap range is found
+        //
+        ASSERT (E820Table[TempIndex - 1].Type == E820Table[TempIndex].Type);
+
+        if (TempIndex == Index - 1) {
+          E820Table[TempIndex].BaseAddr = 0;
+          E820Table[TempIndex].Length   = 0;
+          E820Table[TempIndex].Type     = (EFI_ACPI_MEMORY_TYPE) 0;
+          Index--;
+          break;
+        } else {
+          for (IndexSort = TempIndex; IndexSort < Index - 1; IndexSort ++) {
+            E820Table[IndexSort].BaseAddr = E820Table[IndexSort + 1].BaseAddr;
+            E820Table[IndexSort].Length   = E820Table[IndexSort + 1].Length;
+            E820Table[IndexSort].Type     = E820Table[IndexSort + 1].Type;
+          }
+          Index--;
+       }
+    }
+  }
+
+
+
+  Private->IntThunk->EfiToLegacy16InitTable.NumberE820Entries = (UINT32)Index;
+  Private->IntThunk->EfiToLegacy16BootTable.NumberE820Entries = (UINT32)Index;
+  Private->NumberE820Entries = (UINT32)Index;
+  *Size = (UINTN) (Index * sizeof (EFI_E820_ENTRY64));
+
+  //
+  // Determine OS usable memory above 1MB
+  //
+  Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb = 0x0000;
+  for (TempIndex = Above1MIndex; TempIndex < Index; TempIndex++) {
+    if (E820Table[TempIndex].BaseAddr >= 0x100000 && E820Table[TempIndex].BaseAddr < 0x100000000ULL) { // not include above 4G memory
+      //
+      // ACPIReclaimMemory is also usable memory for ACPI OS, after OS dumps all ACPI tables.
+      //
+      if ((E820Table[TempIndex].Type == EfiAcpiAddressRangeMemory) || (E820Table[TempIndex].Type == EfiAcpiAddressRangeACPI)) {
+        Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb += (UINT32) (E820Table[TempIndex].Length);
+      } else {
+        break; // break at first not normal memory, because SMM may use reserved memory.
+      }
+    }
+  }
+
+  Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb = Private->IntThunk->EfiToLegacy16BootTable.OsMemoryAbove1Mb;
+
+  //
+  // Print DEBUG information
+  //
+  for (TempIndex = 0; TempIndex < Index; TempIndex++) {
+    DEBUG((EFI_D_INFO, "E820[%2d]: 0x%016lx - 0x%016lx, Type = %d\n",
+      TempIndex,
+      E820Table[TempIndex].BaseAddr,
+      (E820Table[TempIndex].BaseAddr + E820Table[TempIndex].Length),
+      E820Table[TempIndex].Type
+      ));
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Fill in the standard BDA and EBDA stuff prior to legacy Boot
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosCompleteBdaBeforeBoot (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  )
+{
+  BDA_STRUC                   *Bda;
+  UINT16                      MachineConfig;
+  DEVICE_PRODUCER_DATA_HEADER *SioPtr;
+
+  Bda           = (BDA_STRUC *) ((UINTN) 0x400);
+  MachineConfig = 0;
+
+  SioPtr        = &(Private->IntThunk->EfiToLegacy16BootTable.SioData);
+  Bda->Com1     = SioPtr->Serial[0].Address;
+  Bda->Com2     = SioPtr->Serial[1].Address;
+  Bda->Com3     = SioPtr->Serial[2].Address;
+  Bda->Com4     = SioPtr->Serial[3].Address;
+
+  if (SioPtr->Serial[0].Address != 0x00) {
+    MachineConfig += 0x200;
+  }
+
+  if (SioPtr->Serial[1].Address != 0x00) {
+    MachineConfig += 0x200;
+  }
+
+  if (SioPtr->Serial[2].Address != 0x00) {
+    MachineConfig += 0x200;
+  }
+
+  if (SioPtr->Serial[3].Address != 0x00) {
+    MachineConfig += 0x200;
+  }
+
+  Bda->Lpt1 = SioPtr->Parallel[0].Address;
+  Bda->Lpt2 = SioPtr->Parallel[1].Address;
+  Bda->Lpt3 = SioPtr->Parallel[2].Address;
+
+  if (SioPtr->Parallel[0].Address != 0x00) {
+    MachineConfig += 0x4000;
+  }
+
+  if (SioPtr->Parallel[1].Address != 0x00) {
+    MachineConfig += 0x4000;
+  }
+
+  if (SioPtr->Parallel[2].Address != 0x00) {
+    MachineConfig += 0x4000;
+  }
+
+  Bda->NumberOfDrives = (UINT8) (Bda->NumberOfDrives + Private->IdeDriveCount);
+  if (SioPtr->Floppy.NumberOfFloppy != 0x00) {
+    MachineConfig     = (UINT16) (MachineConfig + 0x01 + (SioPtr->Floppy.NumberOfFloppy - 1) * 0x40);
+    Bda->FloppyXRate  = 0x07;
+  }
+
+  Bda->Lpt1_2Timeout  = 0x1414;
+  Bda->Lpt3_4Timeout  = 0x1414;
+  Bda->Com1_2Timeout  = 0x0101;
+  Bda->Com3_4Timeout  = 0x0101;
+
+  //
+  // Force VGA and Coprocessor, indicate 101/102 keyboard
+  //
+  MachineConfig       = (UINT16) (MachineConfig + 0x00 + 0x02 + (SioPtr->MousePresent * 0x04));
+  Bda->MachineConfig  = MachineConfig;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Fill in the standard BDA for Keyboard LEDs
+
+  @param  This         Protocol instance pointer.
+  @param  Leds         Current LED status
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosUpdateKeyboardLedStatus (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN  UINT8                             Leds
+  )
+{
+  LEGACY_BIOS_INSTANCE  *Private;
+  BDA_STRUC             *Bda;
+  UINT8                 LocalLeds;
+  EFI_IA32_REGISTER_SET Regs;
+
+  Private             = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+
+  ACCESS_PAGE0_CODE (
+    Bda                 = (BDA_STRUC *) ((UINTN) 0x400);
+    LocalLeds           = Leds;
+    Bda->LedStatus      = (UINT8) ((Bda->LedStatus &~0x07) | LocalLeds);
+    LocalLeds           = (UINT8) (LocalLeds << 4);
+    Bda->ShiftStatus    = (UINT8) ((Bda->ShiftStatus &~0x70) | LocalLeds);
+    LocalLeds           = (UINT8) (Leds & 0x20);
+    Bda->KeyboardStatus = (UINT8) ((Bda->KeyboardStatus &~0x20) | LocalLeds);
+  );
+
+  //
+  // Call into Legacy16 code to allow it to do any processing
+  //
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+  Regs.X.AX = Legacy16SetKeyboardLeds;
+  Regs.H.CL = Leds;
+
+  Private->LegacyBios.FarCall86 (
+    &Private->LegacyBios,
+    Private->Legacy16Table->Compatibility16CallSegment,
+    Private->Legacy16Table->Compatibility16CallOffset,
+    &Regs,
+    NULL,
+    0
+    );
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Fill in the standard CMOS stuff prior to legacy Boot
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosCompleteStandardCmosBeforeBoot (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  )
+{
+  UINT8   Bda;
+  UINT8   Floppy;
+  UINT32  Size;
+
+  //
+  // Update CMOS locations
+  // 10 floppy
+  // 12,19,1A - ignore as OS don't use them and there is no standard due
+  //            to large capacity drives
+  // CMOS 14 = BDA 40:10 plus bit 3(display enabled)
+  //
+  ACCESS_PAGE0_CODE (
+    Bda = (UINT8)(*((UINT8 *)((UINTN)0x410)) | BIT3);
+  );
+
+  //
+  // Force display enabled
+  //
+  Floppy = 0x00;
+  if ((Bda & BIT0) != 0) {
+    Floppy = BIT6;
+  }
+
+  //
+  // Check if 2.88MB floppy set
+  //
+  if ((Bda & (BIT7 | BIT6)) != 0) {
+    Floppy = (UINT8)(Floppy | BIT1);
+  }
+
+  LegacyWriteStandardCmos (CMOS_10, Floppy);
+  LegacyWriteStandardCmos (CMOS_14, Bda);
+
+  //
+  // Force Status Register A to set rate selection bits and divider
+  //
+  LegacyWriteStandardCmos (CMOS_0A, 0x26);
+
+  //
+  // redo memory size since it can change
+  //
+  Size = (15 * SIZE_1MB) >> 10;
+  if (Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb < (15 * SIZE_1MB)) {
+    Size  = Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb >> 10;
+  }
+
+  LegacyWriteStandardCmos (CMOS_17, (UINT8)(Size & 0xFF));
+  LegacyWriteStandardCmos (CMOS_30, (UINT8)(Size & 0xFF));
+  LegacyWriteStandardCmos (CMOS_18, (UINT8)(Size >> 8));
+  LegacyWriteStandardCmos (CMOS_31, (UINT8)(Size >> 8));
+
+  LegacyCalculateWriteStandardCmosChecksum ();
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Relocate this image under 4G memory for IPF.
+
+  @param  ImageHandle  Handle of driver image.
+  @param  SystemTable  Pointer to system table.
+
+  @retval EFI_SUCCESS  Image successfully relocated.
+  @retval EFI_ABORTED  Failed to relocate image.
+
+**/
+EFI_STATUS
+RelocateImageUnder4GIfNeeded (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyCmos.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyCmos.c
new file mode 100644
index 0000000000..de25e06184
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyCmos.c
@@ -0,0 +1,117 @@
+/** @file
+  This code fills in standard CMOS values and updates the standard CMOS
+  checksum. The Legacy16 code or LegacyBiosPlatform.c is responsible for
+  non-standard CMOS locations and non-standard checksums.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+/**
+  Read CMOS register through index/data port.
+
+  @param[in]  Index   The index of the CMOS register to read.
+
+  @return  The data value from the CMOS register specified by Index.
+
+**/
+UINT8
+LegacyReadStandardCmos (
+  IN UINT8  Index
+  )
+{
+  IoWrite8 (PORT_70, Index);
+  return IoRead8 (PORT_71);
+}
+
+/**
+  Write CMOS register through index/data port.
+
+  @param[in]  Index  The index of the CMOS register to write.
+  @param[in]  Value  The value of CMOS register to write.
+
+  @return  The value written to the CMOS register specified by Index.
+
+**/
+UINT8
+LegacyWriteStandardCmos (
+  IN UINT8  Index,
+  IN UINT8  Value
+  )
+{
+  IoWrite8 (PORT_70, Index);
+  return IoWrite8 (PORT_71, Value);
+}
+
+/**
+  Calculate the new standard CMOS checksum and write it.
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  Calculate 16-bit checksum successfully
+
+**/
+EFI_STATUS
+LegacyCalculateWriteStandardCmosChecksum (
+  VOID
+  )
+{
+  UINT8   Register;
+  UINT16  Checksum;
+
+  for (Checksum = 0, Register = 0x10; Register < 0x2e; Register++) {
+    Checksum = (UINT16)(Checksum + LegacyReadStandardCmos (Register));
+  }
+  LegacyWriteStandardCmos (CMOS_2E, (UINT8)(Checksum >> 8));
+  LegacyWriteStandardCmos (CMOS_2F, (UINT8)(Checksum & 0xff));
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Fill in the standard CMOS stuff before Legacy16 load
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosInitCmos (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  )
+{
+  UINT32  Size;
+
+  //
+  //  Clear all errors except RTC lost power
+  //
+  LegacyWriteStandardCmos (CMOS_0E, (UINT8)(LegacyReadStandardCmos (CMOS_0E) & BIT7));
+
+  //
+  // Update CMOS locations 15,16,17,18,30,31 and 32
+  // CMOS 16,15 = 640Kb = 0x280
+  // CMOS 18,17 = 31,30 = 15Mb max in 1Kb increments =0x3C00 max
+  // CMOS 32 = 0x20
+  //
+  LegacyWriteStandardCmos (CMOS_15, 0x80);
+  LegacyWriteStandardCmos (CMOS_16, 0x02);
+
+  Size = 15 * SIZE_1MB;
+  if (Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb < (15 * SIZE_1MB)) {
+    Size  = Private->IntThunk->EfiToLegacy16InitTable.OsMemoryAbove1Mb >> 10;
+  }
+
+  LegacyWriteStandardCmos (CMOS_17, (UINT8)(Size & 0xFF));
+  LegacyWriteStandardCmos (CMOS_30, (UINT8)(Size & 0xFF));
+  LegacyWriteStandardCmos (CMOS_18, (UINT8)(Size >> 8));
+  LegacyWriteStandardCmos (CMOS_31, (UINT8)(Size >> 8));
+
+  LegacyCalculateWriteStandardCmosChecksum ();
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyIde.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyIde.c
new file mode 100644
index 0000000000..789f48370e
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyIde.c
@@ -0,0 +1,310 @@
+/** @file
+  Collect IDE information from Native EFI Driver
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+BOOLEAN mIdeDataBuiltFlag = FALSE;
+
+/**
+  Collect IDE Inquiry data from the IDE disks
+
+  @param  Private        Legacy BIOS Instance data
+  @param  HddInfo        Hdd Information
+  @param  Flag           Reconnect IdeController or not
+
+  @retval EFI_SUCCESS    It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildIdeData (
+  IN  LEGACY_BIOS_INSTANCE      *Private,
+  IN  HDD_INFO                  **HddInfo,
+  IN  UINT16                    Flag
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HANDLE                IdeController;
+  UINTN                     HandleCount;
+  EFI_HANDLE                *HandleBuffer;
+  UINTN                     Index;
+  EFI_DISK_INFO_PROTOCOL    *DiskInfo;
+  UINT32                    IdeChannel;
+  UINT32                    IdeDevice;
+  UINT32                    Size;
+  UINT8                     *InquiryData;
+  UINT32                    InquiryDataSize;
+  HDD_INFO                  *LocalHddInfo;
+  UINT32                    PciIndex;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
+  EFI_DEVICE_PATH_PROTOCOL  *TempDevicePathNode;
+  PCI_DEVICE_PATH           *PciDevicePath;
+
+  //
+  // Only build data once
+  // We have a problem with GetBbsInfo in that it can be invoked two
+  // places. Once in BDS, when all EFI drivers are connected and once in
+  // LegacyBoot after all EFI drivers are disconnected causing this routine
+  // to hang. In LegacyBoot this function is also called before EFI drivers
+  // are disconnected.
+  // Cases covered
+  //    GetBbsInfo invoked in BDS. Both invocations in LegacyBoot ignored.
+  //    GetBbsInfo not invoked in BDS. First invocation of this function
+  //       proceeds normally and second via GetBbsInfo ignored.
+  //
+  PciDevicePath = NULL;
+  LocalHddInfo  = *HddInfo;
+  Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformIdeHandle,
+                                          0,
+                                          &HandleBuffer,
+                                          &HandleCount,
+                                          (VOID *) &LocalHddInfo
+                                          );
+  if (!EFI_ERROR (Status)) {
+    IdeController = HandleBuffer[0];
+    //
+    // Force IDE drive spin up!
+    //
+    if (Flag != 0) {
+      gBS->DisconnectController (
+            IdeController,
+            NULL,
+            NULL
+            );
+    }
+
+    gBS->ConnectController (IdeController, NULL, NULL, FALSE);
+
+    //
+    // Do GetIdeHandle twice since disconnect/reconnect will switch to native mode
+    // And GetIdeHandle will switch to Legacy mode, if required.
+    //
+    Private->LegacyBiosPlatform->GetPlatformHandle (
+                                  Private->LegacyBiosPlatform,
+                                  EfiGetPlatformIdeHandle,
+                                  0,
+                                  &HandleBuffer,
+                                  &HandleCount,
+                                  (VOID *) &LocalHddInfo
+                                  );
+  }
+
+  mIdeDataBuiltFlag = TRUE;
+
+  //
+  // Get Identity command from all drives
+  //
+  gBS->LocateHandleBuffer (
+        ByProtocol,
+        &gEfiDiskInfoProtocolGuid,
+        NULL,
+        &HandleCount,
+        &HandleBuffer
+        );
+
+  Private->IdeDriveCount = (UINT8) HandleCount;
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiDiskInfoProtocolGuid,
+                    (VOID **) &DiskInfo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
+      //
+      //  Locate which PCI device
+      //
+      Status = gBS->HandleProtocol (
+                      HandleBuffer[Index],
+                      &gEfiDevicePathProtocolGuid,
+                      (VOID *) &DevicePath
+                      );
+      ASSERT_EFI_ERROR (Status);
+
+      DevicePathNode = DevicePath;
+      while (!IsDevicePathEnd (DevicePathNode)) {
+        TempDevicePathNode = NextDevicePathNode (DevicePathNode);
+        if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&
+              ( DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&
+              ( DevicePathType(TempDevicePathNode) == MESSAGING_DEVICE_PATH) &&
+              ( DevicePathSubType(TempDevicePathNode) == MSG_ATAPI_DP) ) {
+          PciDevicePath = (PCI_DEVICE_PATH *) DevicePathNode;
+          break;
+        }
+        DevicePathNode = NextDevicePathNode (DevicePathNode);
+      }
+
+      if (PciDevicePath == NULL) {
+        continue;
+      }
+
+      //
+      // Find start of PCI device in HddInfo. The assumption of the data
+      // structure is 2 controllers(channels) per PCI device and each
+      // controller can have 2 drives(devices).
+      // HddInfo[PciIndex+0].[0] = Channel[0].Device[0] Primary Master
+      // HddInfo[PciIndex+0].[1] = Channel[0].Device[1] Primary Slave
+      // HddInfo[PciIndex+1].[0] = Channel[1].Device[0] Secondary Master
+      // HddInfo[PciIndex+1].[1] = Channel[1].Device[1] Secondary Slave
+      // @bug eventually need to pass in max number of entries
+      // for end of for loop
+      //
+      for (PciIndex = 0; PciIndex < 8; PciIndex++) {
+        if ((PciDevicePath->Device == LocalHddInfo[PciIndex].Device) &&
+            (PciDevicePath->Function == LocalHddInfo[PciIndex].Function)
+            ) {
+          break;
+        }
+      }
+
+      if (PciIndex == 8) {
+        continue;
+      }
+
+      Status = DiskInfo->WhichIde (DiskInfo, &IdeChannel, &IdeDevice);
+      if (!EFI_ERROR (Status)) {
+        Size = sizeof (ATAPI_IDENTIFY);
+        DiskInfo->Identify (
+                    DiskInfo,
+                    &LocalHddInfo[PciIndex + IdeChannel].IdentifyDrive[IdeDevice],
+                    &Size
+                    );
+        if (IdeChannel == 0) {
+          LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_PRIMARY;
+        } else if (IdeChannel == 1) {
+          LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SECONDARY;
+        }
+
+        InquiryData     = NULL;
+        InquiryDataSize = 0;
+        Status = DiskInfo->Inquiry (
+                             DiskInfo,
+                             NULL,
+                             &InquiryDataSize
+                             );
+        if (Status == EFI_BUFFER_TOO_SMALL) {
+          InquiryData = (UINT8 *) AllocatePool (
+                                  InquiryDataSize
+                                  );
+          if (InquiryData != NULL) {
+            Status = DiskInfo->Inquiry (
+                                 DiskInfo,
+                                 InquiryData,
+                                 &InquiryDataSize
+                                 );
+          }
+        } else {
+          Status = EFI_DEVICE_ERROR;
+        }
+
+        //
+        // If ATAPI device then Inquiry will pass and ATA fail.
+        //
+        if (!EFI_ERROR (Status)) {
+          ASSERT (InquiryData != NULL);
+          //
+          // If IdeDevice = 0 then set master bit, else slave bit
+          //
+          if (IdeDevice == 0) {
+            if ((InquiryData[0] & 0x1f) == 0x05) {
+              LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_CDROM;
+            } else if ((InquiryData[0] & 0x1f) == 0x00) {
+              LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_ATAPI_ZIPDISK;
+            }
+          } else {
+            if ((InquiryData[0] & 0x1f) == 0x05) {
+              LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_CDROM;
+            } else if ((InquiryData[0] & 0x1f) == 0x00) {
+              LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_ATAPI_ZIPDISK;
+            }
+          }
+          FreePool (InquiryData);
+        } else {
+          if (IdeDevice == 0) {
+            LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_MASTER_IDE;
+          } else {
+            LocalHddInfo[PciIndex + IdeChannel].Status |= HDD_SLAVE_IDE;
+          }
+        }
+      }
+    }
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool (HandleBuffer);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  If the IDE channel is in compatibility (legacy) mode, remove all
+  PCI I/O BAR addresses from the controller.
+
+  @param  IdeController  The handle of target IDE controller
+
+
+**/
+VOID
+InitLegacyIdeController (
+  IN EFI_HANDLE                        IdeController
+  )
+{
+  EFI_PCI_IO_PROTOCOL               *PciIo;
+  UINT32                            IOBarClear;
+  EFI_STATUS                        Status;
+  PCI_TYPE00                        PciData;
+
+  //
+  // If the IDE channel is in compatibility (legacy) mode, remove all
+  // PCI I/O BAR addresses from the controller.  Some software gets
+  // confused if an IDE controller is in compatibility (legacy) mode
+  // and has PCI I/O resources allocated
+  //
+  Status = gBS->HandleProtocol (
+                  IdeController,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **)&PciIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return ;
+  }
+
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (PciData), &PciData);
+  if (EFI_ERROR (Status)) {
+    return ;
+  }
+
+  //
+  // Check whether this is IDE
+  //
+  if ((PciData.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE) ||
+      (PciData.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) {
+    return ;
+  }
+
+  //
+  // Clear bar for legacy IDE
+  //
+  IOBarClear = 0x00;
+  if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_PNE) == 0) {
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x10, 1, &IOBarClear);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x14, 1, &IOBarClear);
+  }
+  if ((PciData.Hdr.ClassCode[0] & IDE_PI_REGISTER_SNE) == 0) {
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x18, 1, &IOBarClear);
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1C, 1, &IOBarClear);
+  }
+
+  return ;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c
new file mode 100644
index 0000000000..dc1f760876
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c
@@ -0,0 +1,3083 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+#include <IndustryStandard/Pci30.h>
+
+#define PCI_START_ADDRESS(x)   (((x) + 0x7ff) & ~0x7ff)
+
+#define MAX_BRIDGE_INDEX  0x20
+typedef struct {
+  UINTN PciSegment;
+  UINTN PciBus;
+  UINTN PciDevice;
+  UINTN PciFunction;
+  UINT8 PrimaryBus;
+  UINT8 SecondaryBus;
+  UINT8 SubordinateBus;
+} BRIDGE_TABLE;
+
+#define ROM_MAX_ENTRIES 24
+BRIDGE_TABLE                        Bridges[MAX_BRIDGE_INDEX];
+UINTN                               SortedBridgeIndex[MAX_BRIDGE_INDEX];
+UINTN                               NumberOfBridges;
+LEGACY_PNP_EXPANSION_HEADER  *mBasePnpPtr;
+UINT16                              mBbsRomSegment;
+UINTN                               mHandleCount;
+EFI_HANDLE                          mVgaHandle;
+BOOLEAN                             mIgnoreBbsUpdateFlag;
+BOOLEAN                             mVgaInstallationInProgress  = FALSE;
+UINT32                              mRomCount                   = 0x00;
+ROM_INSTANCE_ENTRY                  mRomEntry[ROM_MAX_ENTRIES];
+EDKII_IOMMU_PROTOCOL                *mIoMmu;
+
+/**
+  Query shadowed legacy ROM parameters registered by RomShadow() previously.
+
+  @param  PciHandle        PCI device whos ROM has been shadowed
+  @param  DiskStart        DiskStart value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom
+  @param  DiskEnd          DiskEnd value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom
+  @param  RomShadowAddress Address where ROM was shadowed
+  @param  ShadowedSize     Runtime size of ROM
+
+  @retval EFI_SUCCESS      Query Logging successful.
+  @retval EFI_NOT_FOUND    No logged data found about PciHandle.
+
+**/
+EFI_STATUS
+GetShadowedRomParameters (
+  IN EFI_HANDLE                         PciHandle,
+  OUT UINT8                             *DiskStart,         OPTIONAL
+  OUT UINT8                             *DiskEnd,           OPTIONAL
+  OUT VOID                              **RomShadowAddress, OPTIONAL
+  OUT UINTN                             *ShadowedSize       OPTIONAL
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  UINTN               Index;
+  UINTN               PciSegment;
+  UINTN               PciBus;
+  UINTN               PciDevice;
+  UINTN               PciFunction;
+
+  //
+  // Get the PCI I/O Protocol on PciHandle
+  //
+  Status = gBS->HandleProtocol (
+                  PciHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Get the location of the PCI device
+  //
+  PciIo->GetLocation (
+           PciIo,
+           &PciSegment,
+           &PciBus,
+           &PciDevice,
+           &PciFunction
+           );
+
+  for(Index = 0; Index < mRomCount; Index++) {
+    if ((mRomEntry[Index].PciSegment == PciSegment) &&
+        (mRomEntry[Index].PciBus == PciBus)         &&
+        (mRomEntry[Index].PciDevice == PciDevice)   &&
+        (mRomEntry[Index].PciFunction == PciFunction)) {
+      break;
+    }
+  }
+
+  if (Index == mRomCount) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (DiskStart != NULL) {
+    *DiskStart = mRomEntry[Index].DiskStart;
+  }
+
+  if (DiskEnd != NULL) {
+    *DiskEnd = mRomEntry[Index].DiskEnd;
+  }
+
+  if (RomShadowAddress != NULL) {
+    *RomShadowAddress = (VOID *)(UINTN)mRomEntry[Index].ShadowAddress;
+  }
+
+  if (ShadowedSize != NULL) {
+    *ShadowedSize = mRomEntry[Index].ShadowedSize;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Every legacy ROM that is shadowed by the Legacy BIOS driver will be
+  registered into this API so that the policy code can know what has
+  happend
+
+  @param  PciHandle              PCI device whos ROM is being shadowed
+  @param  ShadowAddress          Address that ROM was shadowed
+  @param  ShadowedSize           Runtime size of ROM
+  @param  DiskStart              DiskStart value from
+                                 EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom
+  @param  DiskEnd                DiskEnd value from
+                                 EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom
+
+  @retval EFI_SUCCESS            Logging successful.
+  @retval EFI_OUT_OF_RESOURCES   No remaining room for registering another option
+                                 ROM.
+
+**/
+EFI_STATUS
+RomShadow (
+  IN  EFI_HANDLE                                  PciHandle,
+  IN  UINT32                                      ShadowAddress,
+  IN  UINT32                                      ShadowedSize,
+  IN  UINT8                                       DiskStart,
+  IN  UINT8                                       DiskEnd
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+
+  //
+  // See if there is room to register another option ROM
+  //
+  if (mRomCount >= ROM_MAX_ENTRIES) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  //
+  // Get the PCI I/O Protocol on PciHandle
+  //
+  Status = gBS->HandleProtocol (
+                  PciHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Get the location of the PCI device
+  //
+  PciIo->GetLocation (
+           PciIo,
+           &mRomEntry[mRomCount].PciSegment,
+           &mRomEntry[mRomCount].PciBus,
+           &mRomEntry[mRomCount].PciDevice,
+           &mRomEntry[mRomCount].PciFunction
+           );
+  mRomEntry[mRomCount].ShadowAddress = ShadowAddress;
+  mRomEntry[mRomCount].ShadowedSize  = ShadowedSize;
+  mRomEntry[mRomCount].DiskStart     = DiskStart;
+  mRomEntry[mRomCount].DiskEnd       = DiskEnd;
+
+  mRomCount++;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Return EFI_SUCCESS if PciHandle has had a legacy BIOS ROM shadowed. This
+  information represents every call to RomShadow ()
+
+  @param  PciHandle              PCI device to get status for
+
+  @retval EFI_SUCCESS            Legacy ROM loaded for this device
+  @retval EFI_NOT_FOUND          No Legacy ROM loaded for this device
+
+**/
+EFI_STATUS
+IsLegacyRom (
+  IN  EFI_HANDLE                PciHandle
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  UINTN               Index;
+  UINTN               Segment;
+  UINTN               Bus;
+  UINTN               Device;
+  UINTN               Function;
+
+  //
+  // Get the PCI I/O Protocol on PciHandle
+  //
+  Status = gBS->HandleProtocol (
+                  PciHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Get the location of the PCI device
+  //
+  PciIo->GetLocation (
+           PciIo,
+           &Segment,
+           &Bus,
+           &Device,
+           &Function
+           );
+
+  //
+  // See if the option ROM from PciHandle has been previously posted
+  //
+  for (Index = 0; Index < mRomCount; Index++) {
+    if (mRomEntry[Index].PciSegment == Segment &&
+        mRomEntry[Index].PciBus == Bus &&
+        mRomEntry[Index].PciDevice == Device &&
+        mRomEntry[Index].PciFunction == Function
+        ) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Find the PC-AT ROM Image in the raw PCI Option ROM. Also return the
+  related information from the header.
+
+  @param  Csm16Revision           The PCI interface version of underlying CSM16
+  @param  VendorId                Vendor ID of the PCI device
+  @param  DeviceId                Device ID of the PCI device
+  @param  Rom                     On input pointing to beginning of the raw PCI OpROM
+                                  On output pointing to the first legacy PCI OpROM
+  @param  ImageSize               On input is the size of Raw PCI Rom
+                                  On output is the size of the first legacy PCI ROM
+  @param  MaxRuntimeImageLength   The max runtime image length only valid if OpRomRevision >= 3
+  @param  OpRomRevision           Revision of the PCI Rom
+  @param  ConfigUtilityCodeHeader Pointer to Configuration Utility Code Header
+
+  @retval EFI_SUCCESS             Successfully find the legacy PCI ROM
+  @retval EFI_NOT_FOUND           Failed to find the legacy PCI ROM
+
+**/
+EFI_STATUS
+GetPciLegacyRom (
+  IN     UINT16 Csm16Revision,
+  IN     UINT16 VendorId,
+  IN     UINT16 DeviceId,
+  IN OUT VOID   **Rom,
+  IN OUT UINTN  *ImageSize,
+  OUT    UINTN  *MaxRuntimeImageLength,   OPTIONAL
+  OUT    UINT8  *OpRomRevision,           OPTIONAL
+  OUT    VOID   **ConfigUtilityCodeHeader OPTIONAL
+  )
+{
+  BOOLEAN                 Match;
+  UINT16                  *DeviceIdList;
+  EFI_PCI_ROM_HEADER      RomHeader;
+  PCI_3_0_DATA_STRUCTURE  *Pcir;
+  VOID                    *BackupImage;
+  VOID                    *BestImage;
+
+
+  if (*ImageSize < sizeof (EFI_PCI_ROM_HEADER)) {
+    return EFI_NOT_FOUND;
+  }
+
+  BestImage     = NULL;
+  BackupImage   = NULL;
+  RomHeader.Raw = *Rom;
+  while (RomHeader.Generic->Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+    if (RomHeader.Generic->PcirOffset == 0 ||
+        (RomHeader.Generic->PcirOffset & 3) !=0 ||
+        *ImageSize < RomHeader.Raw - (UINT8 *) *Rom + RomHeader.Generic->PcirOffset + sizeof (PCI_DATA_STRUCTURE)) {
+      break;
+    }
+
+    Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset);
+    //
+    // Check signature in the PCI Data Structure.
+    //
+    if (Pcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
+      break;
+    }
+
+    if (((UINTN)RomHeader.Raw - (UINTN)*Rom) + Pcir->ImageLength * 512 > *ImageSize) {
+      break;
+    }
+
+    if (Pcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
+      Match = FALSE;
+      if (Pcir->VendorId == VendorId) {
+        if (Pcir->DeviceId == DeviceId) {
+          Match = TRUE;
+        } else if ((Pcir->Revision >= 3) && (Pcir->DeviceListOffset != 0)) {
+          DeviceIdList = (UINT16 *)(((UINT8 *) Pcir) + Pcir->DeviceListOffset);
+          //
+          // Checking the device list
+          //
+          while (*DeviceIdList != 0) {
+            if (*DeviceIdList == DeviceId) {
+              Match = TRUE;
+              break;
+            }
+            DeviceIdList ++;
+          }
+        }
+      }
+
+      if (Match) {
+        if (Csm16Revision >= 0x0300) {
+          //
+          // Case 1: CSM16 3.0
+          //
+          if (Pcir->Revision >= 3) {
+            //
+            // case 1.1: meets OpRom 3.0
+            //           Perfect!!!
+            //
+            BestImage  = RomHeader.Raw;
+            break;
+          } else {
+            //
+            // case 1.2: meets OpRom 2.x
+            //           Store it and try to find the OpRom 3.0
+            //
+            BackupImage = RomHeader.Raw;
+          }
+        } else {
+          //
+          // Case 2: CSM16 2.x
+          //
+          if (Pcir->Revision >= 3) {
+            //
+            // case 2.1: meets OpRom 3.0
+            //           Store it and try to find the OpRom 2.x
+            //
+            BackupImage = RomHeader.Raw;
+          } else {
+            //
+            // case 2.2: meets OpRom 2.x
+            //           Perfect!!!
+            //
+            BestImage   = RomHeader.Raw;
+            break;
+          }
+        }
+      } else {
+        DEBUG ((EFI_D_ERROR, "GetPciLegacyRom - OpRom not match (%04x-%04x)\n", (UINTN)VendorId, (UINTN)DeviceId));
+      }
+    }
+
+    if ((Pcir->Indicator & 0x80) == 0x80) {
+      break;
+    } else {
+      RomHeader.Raw += 512 * Pcir->ImageLength;
+    }
+  }
+
+  if (BestImage == NULL) {
+    if (BackupImage == NULL) {
+      return EFI_NOT_FOUND;
+    }
+    //
+    // The versions of CSM16 and OpRom don't match exactly
+    //
+    BestImage = BackupImage;
+  }
+  RomHeader.Raw = BestImage;
+  Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset);
+  *Rom       = BestImage;
+  *ImageSize = Pcir->ImageLength * 512;
+
+  if (MaxRuntimeImageLength != NULL) {
+    if (Pcir->Revision < 3) {
+      *MaxRuntimeImageLength = 0;
+    } else {
+      *MaxRuntimeImageLength = Pcir->MaxRuntimeImageLength * 512;
+    }
+  }
+
+  if (OpRomRevision != NULL) {
+    //
+    // Optional return PCI Data Structure revision
+    //
+    if (Pcir->Length >= 0x1C) {
+      *OpRomRevision = Pcir->Revision;
+    } else {
+      *OpRomRevision = 0;
+    }
+  }
+
+  if (ConfigUtilityCodeHeader != NULL) {
+    //
+    // Optional return ConfigUtilityCodeHeaderOffset supported by the PC-AT ROM
+    //
+    if ((Pcir->Revision < 3) || (Pcir->ConfigUtilityCodeHeaderOffset == 0)) {
+      *ConfigUtilityCodeHeader = NULL;
+    } else {
+      *ConfigUtilityCodeHeader = RomHeader.Raw + Pcir->ConfigUtilityCodeHeaderOffset;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Build a table of bridge info for PIRQ translation.
+
+  @param  RoutingTable         RoutingTable obtained from Platform.
+  @param  RoutingTableEntries  Number of RoutingTable entries.
+
+  @retval EFI_SUCCESS          New Subordinate bus.
+  @retval EFI_NOT_FOUND        No more Subordinate busses.
+
+**/
+EFI_STATUS
+CreateBridgeTable (
+  IN EFI_LEGACY_IRQ_ROUTING_ENTRY         *RoutingTable,
+  IN UINTN                                RoutingTableEntries
+  )
+{
+  EFI_STATUS          Status;
+  UINTN               HandleCount;
+  EFI_HANDLE          *HandleBuffer;
+  UINTN               BridgeIndex;
+  UINTN               Index;
+  UINTN               Index1;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE01          PciConfigHeader;
+  BRIDGE_TABLE        SlotBridges[MAX_BRIDGE_INDEX];
+  UINTN               SlotBridgeIndex;
+
+  BridgeIndex = 0x00;
+  SlotBridgeIndex = 0x00;
+
+  //
+  // Assumption is table is built from low bus to high bus numbers.
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    PciIo->Pci.Read (
+                 PciIo,
+                 EfiPciIoWidthUint32,
+                 0,
+                 sizeof (PciConfigHeader) / sizeof (UINT32),
+                 &PciConfigHeader
+                 );
+
+    if (IS_PCI_P2P (&PciConfigHeader) && (BridgeIndex < MAX_BRIDGE_INDEX)) {
+      PciIo->GetLocation (
+               PciIo,
+               &Bridges[BridgeIndex].PciSegment,
+               &Bridges[BridgeIndex].PciBus,
+               &Bridges[BridgeIndex].PciDevice,
+               &Bridges[BridgeIndex].PciFunction
+               );
+
+      Bridges[BridgeIndex].PrimaryBus     = PciConfigHeader.Bridge.PrimaryBus;
+
+      Bridges[BridgeIndex].SecondaryBus   = PciConfigHeader.Bridge.SecondaryBus;
+
+      Bridges[BridgeIndex].SubordinateBus = PciConfigHeader.Bridge.SubordinateBus;
+
+      for (Index1 = 0; Index1 < RoutingTableEntries; Index1++){
+        //
+        // Test whether we have found the Bridge in the slot, must be the one that directly interfaced to the board
+        // Once we find one, store it in the SlotBridges[]
+        //
+        if ((RoutingTable[Index1].Slot != 0) && (Bridges[BridgeIndex].PrimaryBus == RoutingTable[Index1].Bus)
+           && ((Bridges[BridgeIndex].PciDevice << 3) == RoutingTable[Index1].Device)) {
+          CopyMem (&SlotBridges[SlotBridgeIndex], &Bridges[BridgeIndex], sizeof (BRIDGE_TABLE));
+          SlotBridgeIndex++;
+
+          break;
+        }
+      }
+
+      ++BridgeIndex;
+    }
+  }
+
+  //
+  // Pack up Bridges by removing those useless ones
+  //
+  for (Index = 0; Index < BridgeIndex;){
+    for (Index1 = 0; Index1 < SlotBridgeIndex; Index1++) {
+      if (((Bridges[Index].PciBus == SlotBridges[Index1].PrimaryBus) && (Bridges[Index].PciDevice == SlotBridges[Index1].PciDevice)) ||
+        ((Bridges[Index].PciBus >= SlotBridges[Index1].SecondaryBus) && (Bridges[Index].PciBus <= SlotBridges[Index1].SubordinateBus))) {
+        //
+        // We have found one that meets our criteria
+        //
+        Index++;
+        break;
+      }
+    }
+
+    //
+    // This one doesn't meet criteria, pack it
+    //
+    if (Index1 >= SlotBridgeIndex) {
+      for (Index1 = Index; BridgeIndex > 1 && Index1 < BridgeIndex - 1 ; Index1++) {
+        CopyMem (&Bridges[Index1], &Bridges[Index1 + 1], sizeof (BRIDGE_TABLE));
+      }
+
+      BridgeIndex--;
+    }
+  }
+
+  NumberOfBridges = BridgeIndex;
+
+  //
+  // Sort bridges low to high by Secondary bus followed by subordinate bus
+  //
+  if (NumberOfBridges > 1) {
+    Index = 0;
+    do {
+      SortedBridgeIndex[Index] = Index;
+      ++Index;
+    } while (Index < NumberOfBridges);
+
+    for (Index = 0; Index < NumberOfBridges - 1; Index++) {
+      for (Index1 = Index + 1; Index1 < NumberOfBridges; Index1++) {
+        if (Bridges[Index].SecondaryBus > Bridges[Index1].SecondaryBus) {
+          SortedBridgeIndex[Index]  = Index1;
+          SortedBridgeIndex[Index1] = Index;
+        }
+
+        if ((Bridges[Index].SecondaryBus == Bridges[Index1].SecondaryBus) &&
+            (Bridges[Index].SubordinateBus > Bridges[Index1].SubordinateBus)
+            ) {
+          SortedBridgeIndex[Index]  = Index1;
+          SortedBridgeIndex[Index1] = Index;
+        }
+      }
+    }
+  }
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Find base Bridge for device.
+
+  @param  Private                Legacy  BIOS Instance data
+  @param  PciBus                 Input = Bus of device.
+  @param  PciDevice              Input = Device.
+  @param  RoutingTable           The platform specific routing table
+  @param  RoutingTableEntries    Number of entries in table
+
+  @retval EFI_SUCCESS            At base bus.
+  @retval EFI_NOT_FOUND          Behind a bridge.
+
+**/
+EFI_STATUS
+GetBaseBus (
+  IN  LEGACY_BIOS_INSTANCE        *Private,
+  IN UINTN                        PciBus,
+  IN UINTN                        PciDevice,
+  IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable,
+  IN UINTN                        RoutingTableEntries
+  )
+{
+  UINTN Index;
+  for (Index = 0; Index < RoutingTableEntries; Index++) {
+    if ((RoutingTable[Index].Bus == PciBus) && (RoutingTable[Index].Device == (PciDevice << 3))) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Translate PIRQ through busses
+
+  @param  Private              Legacy  BIOS Instance data
+  @param  PciBus               Input = Bus of device. Output = Translated Bus
+  @param  PciDevice            Input = Device. Output = Translated Device
+  @param  PciFunction          Input = Function. Output = Translated Function
+  @param  PirqIndex            Input = Original PIRQ index. If single function
+                                  device then 0, otherwise 0-3.
+                               Output = Translated Index
+
+  @retval EFI_SUCCESS          Pirq successfully translated.
+  @retval EFI_NOT_FOUND        The device is not behind any known bridge.
+
+**/
+EFI_STATUS
+TranslateBusPirq (
+  IN  LEGACY_BIOS_INSTANCE            *Private,
+  IN OUT UINTN                        *PciBus,
+  IN OUT UINTN                        *PciDevice,
+  IN OUT UINTN                        *PciFunction,
+  IN OUT UINT8                        *PirqIndex
+  )
+{
+  /*
+  This routine traverses the PCI busses from base slot
+  and translates the PIRQ register to the appropriate one.
+
+  Example:
+
+  Bus 0, Device 1 is PCI-PCI bridge that all PCI slots reside on.
+    Primary bus# = 0
+    Secondary bus # = 1
+    Subordinate bus # is highest bus # behind this bus
+       Bus 1, Device 0 is Slot 0 and is not a bridge.
+       Bus 1, Device 1 is Slot 1 and is a bridge.
+         Slot PIRQ routing is A,B,C,D.
+         Primary bus # = 1
+         Secondary bus # = 2
+         Subordinate bus # = 5
+            Bus 2, Device 6 is a bridge. It has no bridges behind it.
+              Primary bus # = 2
+              Secondary bus # = 3
+              Subordinate bus # = 3
+              Bridge PIRQ routing is C,D,A,B
+            Bus 2, Device 7 is a bridge. It has 1 bridge behind it.
+              Primary bus # = 2
+              Secondary bus = 4   Device 6 takes bus 2.
+              Subordinate bus = 5.
+              Bridge PIRQ routing is D,A,B,C
+                 Bus 4, Device 2 is a bridge. It has no bridges behind it.
+                   Primary bus # = 4
+                   Secondary bus # = 5
+                   Subordinate bus = 5
+                   Bridge PIRQ routing is B,C,D,A
+                      Bus 5, Device 1 is to be programmed.
+                         Device PIRQ routing is C,D,A,B
+
+
+Search busses starting from slot bus for final bus >= Secondary bus and
+final bus <= Suborninate bus. Assumption is bus entries increase in bus
+number.
+Starting PIRQ is A,B,C,D.
+Bus 2, Device 7 satisfies search criteria. Rotate (A,B,C,D) left by device
+  7 modulo 4 giving (D,A,B,C).
+Bus 4, Device 2 satisfies search criteria. Rotate (D,A,B,C) left by 2 giving
+  (B,C,D,A).
+No other busses match criteria. Device to be programmed is Bus 5, Device 1.
+Rotate (B,C,D,A) by 1 giving C,D,A,B. Translated PIRQ is C.
+
+*/
+  UINTN LocalBus;
+  UINTN LocalDevice;
+  UINTN BaseBus;
+  UINTN BaseDevice;
+  UINTN BaseFunction;
+  UINT8 LocalPirqIndex;
+  BOOLEAN BaseIndexFlag;
+  UINTN BridgeIndex;
+  UINTN SBridgeIndex;
+  BaseIndexFlag   = FALSE;
+  BridgeIndex     = 0x00;
+
+  LocalPirqIndex  = *PirqIndex;
+  LocalBus        = *PciBus;
+  LocalDevice     = *PciDevice;
+  BaseBus         = *PciBus;
+  BaseDevice      = *PciDevice;
+  BaseFunction    = *PciFunction;
+
+  //
+  // LocalPirqIndex list PIRQs in rotated fashion
+  // = 0  A,B,C,D
+  // = 1  B,C,D,A
+  // = 2  C,D,A,B
+  // = 3  D,A,B,C
+  //
+
+  for (BridgeIndex = 0; BridgeIndex < NumberOfBridges; BridgeIndex++) {
+    SBridgeIndex = SortedBridgeIndex[BridgeIndex];
+    //
+    // Check if device behind this bridge
+    //
+    if ((LocalBus >= Bridges[SBridgeIndex].SecondaryBus) && (LocalBus <= Bridges[SBridgeIndex].SubordinateBus)) {
+      //
+      // If BaseIndexFlag = FALSE then have found base bridge, i.e
+      // bridge in slot. Save info for use by IRQ routing table.
+      //
+      if (!BaseIndexFlag) {
+        BaseBus       = Bridges[SBridgeIndex].PciBus;
+        BaseDevice    = Bridges[SBridgeIndex].PciDevice;
+        BaseFunction  = Bridges[SBridgeIndex].PciFunction;
+        BaseIndexFlag = TRUE;
+      } else {
+        LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8)Bridges[SBridgeIndex].PciDevice)%4);
+      }
+
+      //
+      // Check if at device. If not get new PCI location & PIRQ
+      //
+      if (Bridges[SBridgeIndex].SecondaryBus == (UINT8) LocalBus) {
+        //
+        // Translate PIRQ
+        //
+        LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8) (LocalDevice)) % 4);
+        break;
+      }
+    }
+  }
+
+  //
+  // In case we fail to find the Bridge just above us, this is some potential error and we want to warn the user
+  //
+  if(BridgeIndex >= NumberOfBridges){
+    DEBUG ((EFI_D_ERROR, "Cannot Find IRQ Routing for Bus %d, Device %d, Function %d\n", *PciBus, *PciDevice, *PciFunction));
+  }
+
+  *PirqIndex    = LocalPirqIndex;
+  *PciBus       = BaseBus;
+  *PciDevice    = BaseDevice;
+  *PciFunction  = BaseFunction;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Copy the $PIR table as required.
+
+  @param  Private                Legacy  BIOS Instance data
+  @param  RoutingTable           Pointer to IRQ routing table
+  @param  RoutingTableEntries    IRQ routing table entries
+  @param  PirqTable              Pointer to $PIR table
+  @param  PirqTableSize          Length of table
+
+**/
+VOID
+CopyPirqTable (
+  IN  LEGACY_BIOS_INSTANCE                *Private,
+  IN EFI_LEGACY_IRQ_ROUTING_ENTRY         *RoutingTable,
+  IN UINTN                                RoutingTableEntries,
+  IN EFI_LEGACY_PIRQ_TABLE_HEADER         *PirqTable,
+  IN UINTN                                PirqTableSize
+  )
+{
+  EFI_IA32_REGISTER_SET Regs;
+  UINT32                Granularity;
+
+  //
+  // Copy $PIR table, if it exists.
+  //
+  if (PirqTable != NULL) {
+    Private->LegacyRegion->UnLock (
+                            Private->LegacyRegion,
+                            0xE0000,
+                            0x20000,
+                            &Granularity
+                            );
+
+    Private->InternalIrqRoutingTable  = RoutingTable;
+    Private->NumberIrqRoutingEntries  = (UINT16) (RoutingTableEntries);
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
+
+    Regs.X.AX = Legacy16GetTableAddress;
+    Regs.X.CX = (UINT16) PirqTableSize;
+    //
+    // Allocate at F segment according to PCI IRQ Routing Table Specification
+    //
+    Regs.X.BX = (UINT16) 0x1;
+    //
+    // 16-byte boundary alignment requirement according to
+    // PCI IRQ Routing Table Specification
+    //
+    Regs.X.DX = 0x10;
+    Private->LegacyBios.FarCall86 (
+      &Private->LegacyBios,
+      Private->Legacy16CallSegment,
+      Private->Legacy16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+
+    Private->Legacy16Table->IrqRoutingTablePointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);
+    if (Regs.X.AX != 0) {
+      DEBUG ((EFI_D_ERROR, "PIRQ table length insufficient - %x\n", PirqTableSize));
+    } else {
+      DEBUG ((EFI_D_INFO, "PIRQ table in legacy region - %x\n", Private->Legacy16Table->IrqRoutingTablePointer));
+      Private->Legacy16Table->IrqRoutingTableLength = (UINT32)PirqTableSize;
+      CopyMem (
+        (VOID *) (UINTN)Private->Legacy16Table->IrqRoutingTablePointer,
+        PirqTable,
+        PirqTableSize
+        );
+    }
+
+    Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
+    Private->LegacyRegion->Lock (
+                             Private->LegacyRegion,
+                             0xE0000,
+                             0x20000,
+                             &Granularity
+                             );
+  }
+
+  Private->PciInterruptLine = TRUE;
+  mHandleCount              = 0;
+}
+
+/**
+  Dump EFI_LEGACY_INSTALL_PCI_HANDLER structure information.
+
+  @param  PciHandle               The pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure
+
+**/
+VOID
+DumpPciHandle (
+  IN EFI_LEGACY_INSTALL_PCI_HANDLER  *PciHandle
+  )
+{
+  DEBUG ((EFI_D_INFO, "PciBus             - %02x\n", (UINTN)PciHandle->PciBus));
+  DEBUG ((EFI_D_INFO, "PciDeviceFun       - %02x\n", (UINTN)PciHandle->PciDeviceFun));
+  DEBUG ((EFI_D_INFO, "PciSegment         - %02x\n", (UINTN)PciHandle->PciSegment));
+  DEBUG ((EFI_D_INFO, "PciClass           - %02x\n", (UINTN)PciHandle->PciClass));
+  DEBUG ((EFI_D_INFO, "PciSubclass        - %02x\n", (UINTN)PciHandle->PciSubclass));
+  DEBUG ((EFI_D_INFO, "PciInterface       - %02x\n", (UINTN)PciHandle->PciInterface));
+
+  DEBUG ((EFI_D_INFO, "PrimaryIrq         - %02x\n", (UINTN)PciHandle->PrimaryIrq));
+  DEBUG ((EFI_D_INFO, "PrimaryReserved    - %02x\n", (UINTN)PciHandle->PrimaryReserved));
+  DEBUG ((EFI_D_INFO, "PrimaryControl     - %04x\n", (UINTN)PciHandle->PrimaryControl));
+  DEBUG ((EFI_D_INFO, "PrimaryBase        - %04x\n", (UINTN)PciHandle->PrimaryBase));
+  DEBUG ((EFI_D_INFO, "PrimaryBusMaster   - %04x\n", (UINTN)PciHandle->PrimaryBusMaster));
+
+  DEBUG ((EFI_D_INFO, "SecondaryIrq       - %02x\n", (UINTN)PciHandle->SecondaryIrq));
+  DEBUG ((EFI_D_INFO, "SecondaryReserved  - %02x\n", (UINTN)PciHandle->SecondaryReserved));
+  DEBUG ((EFI_D_INFO, "SecondaryControl   - %04x\n", (UINTN)PciHandle->SecondaryControl));
+  DEBUG ((EFI_D_INFO, "SecondaryBase      - %04x\n", (UINTN)PciHandle->SecondaryBase));
+  DEBUG ((EFI_D_INFO, "SecondaryBusMaster - %04x\n", (UINTN)PciHandle->SecondaryBusMaster));
+  return;
+}
+
+/**
+  Copy the $PIR table as required.
+
+  @param  Private                Legacy  BIOS Instance data
+  @param  PciIo                  Pointer to PCI_IO protocol
+  @param  PciIrq                 Pci IRQ number
+  @param  PciConfigHeader        Type00 Pci configuration header
+
+**/
+VOID
+InstallLegacyIrqHandler (
+  IN LEGACY_BIOS_INSTANCE       *Private,
+  IN EFI_PCI_IO_PROTOCOL        *PciIo,
+  IN UINT8                      PciIrq,
+  IN PCI_TYPE00                 *PciConfigHeader
+  )
+{
+  EFI_IA32_REGISTER_SET     Regs;
+  UINT16                    LegMask;
+  UINTN                     PciSegment;
+  UINTN                     PciBus;
+  UINTN                     PciDevice;
+  UINTN                     PciFunction;
+  EFI_LEGACY_8259_PROTOCOL  *Legacy8259;
+  UINT16                    PrimaryMaster;
+  UINT16                    SecondaryMaster;
+  UINTN                     TempData;
+  UINTN                     RegisterAddress;
+  UINT32                    Granularity;
+
+  PrimaryMaster   = 0;
+  SecondaryMaster = 0;
+  Legacy8259      = Private->Legacy8259;
+  //
+  // Disable interrupt in PIC, in case shared, to prevent an
+  // interrupt from occuring.
+  //
+  Legacy8259->GetMask (
+                Legacy8259,
+                &LegMask,
+                NULL,
+                NULL,
+                NULL
+                );
+
+  LegMask = (UINT16) (LegMask | (UINT16) (1 << PciIrq));
+
+  Legacy8259->SetMask (
+                Legacy8259,
+                &LegMask,
+                NULL,
+                NULL,
+                NULL
+                );
+
+  PciIo->GetLocation (
+          PciIo,
+          &PciSegment,
+          &PciBus,
+          &PciDevice,
+          &PciFunction
+          );
+  Private->IntThunk->PciHandler.PciBus              = (UINT8) PciBus;
+  Private->IntThunk->PciHandler.PciDeviceFun        = (UINT8) ((PciDevice << 3) + PciFunction);
+  Private->IntThunk->PciHandler.PciSegment          = (UINT8) PciSegment;
+  Private->IntThunk->PciHandler.PciClass            = PciConfigHeader->Hdr.ClassCode[2];
+  Private->IntThunk->PciHandler.PciSubclass         = PciConfigHeader->Hdr.ClassCode[1];
+  Private->IntThunk->PciHandler.PciInterface        = PciConfigHeader->Hdr.ClassCode[0];
+
+  //
+  // Use native mode base address registers in two cases:
+  // 1. Programming Interface (PI) register indicates Primary Controller is
+  // in native mode OR
+  // 2. PCI device Sub Class Code is not IDE
+  //
+  Private->IntThunk->PciHandler.PrimaryBusMaster  = (UINT16)(PciConfigHeader->Device.Bar[4] & 0xfffc);
+  if (((PciConfigHeader->Hdr.ClassCode[0] & 0x01) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) {
+    Private->IntThunk->PciHandler.PrimaryIrq      = PciIrq;
+    Private->IntThunk->PciHandler.PrimaryBase     = (UINT16) (PciConfigHeader->Device.Bar[0] & 0xfffc);
+    Private->IntThunk->PciHandler.PrimaryControl  = (UINT16) ((PciConfigHeader->Device.Bar[1] & 0xfffc) + 2);
+  } else {
+    Private->IntThunk->PciHandler.PrimaryIrq      = 14;
+    Private->IntThunk->PciHandler.PrimaryBase     = 0x1f0;
+    Private->IntThunk->PciHandler.PrimaryControl  = 0x3f6;
+  }
+  //
+  // Secondary controller data
+  //
+  if (Private->IntThunk->PciHandler.PrimaryBusMaster != 0) {
+    Private->IntThunk->PciHandler.SecondaryBusMaster  = (UINT16) ((PciConfigHeader->Device.Bar[4] & 0xfffc) + 8);
+    PrimaryMaster = (UINT16) (Private->IntThunk->PciHandler.PrimaryBusMaster + 2);
+    SecondaryMaster = (UINT16) (Private->IntThunk->PciHandler.SecondaryBusMaster + 2);
+
+    //
+    // Clear pending interrupts in Bus Master registers
+    //
+    IoWrite16 (PrimaryMaster, 0x04);
+    IoWrite16 (SecondaryMaster, 0x04);
+
+  }
+
+  //
+  // Use native mode base address registers in two cases:
+  // 1. Programming Interface (PI) register indicates Secondary Controller is
+  // in native mode OR
+  // 2. PCI device Sub Class Code is not IDE
+  //
+  if (((PciConfigHeader->Hdr.ClassCode[0] & 0x04) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) {
+    Private->IntThunk->PciHandler.SecondaryIrq      = PciIrq;
+    Private->IntThunk->PciHandler.SecondaryBase     = (UINT16) (PciConfigHeader->Device.Bar[2] & 0xfffc);
+    Private->IntThunk->PciHandler.SecondaryControl  = (UINT16) ((PciConfigHeader->Device.Bar[3] & 0xfffc) + 2);
+  } else {
+
+    Private->IntThunk->PciHandler.SecondaryIrq      = 15;
+    Private->IntThunk->PciHandler.SecondaryBase     = 0x170;
+    Private->IntThunk->PciHandler.SecondaryControl  = 0x376;
+  }
+
+  //
+  // Clear pending interrupts in IDE Command Block Status reg before we
+  // Thunk to CSM16 below.  Don't want a pending Interrupt before we
+  // install the handlers as wierd corruption would occur and hang system.
+  //
+  //
+  // Read IDE CMD blk status reg to clear out any pending interrupts.
+  // Do here for Primary and Secondary IDE channels
+  //
+  RegisterAddress = (UINT16)Private->IntThunk->PciHandler.PrimaryBase + 0x07;
+  IoRead8 (RegisterAddress);
+  RegisterAddress = (UINT16)Private->IntThunk->PciHandler.SecondaryBase + 0x07;
+  IoRead8 (RegisterAddress);
+
+  Private->IntThunk->PciHandler.PrimaryReserved   = 0;
+  Private->IntThunk->PciHandler.SecondaryReserved = 0;
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           0xE0000,
+                           0x20000,
+                           &Granularity
+                           );
+
+  Regs.X.AX = Legacy16InstallPciHandler;
+  TempData  = (UINTN) &Private->IntThunk->PciHandler;
+  Regs.X.ES = EFI_SEGMENT ((UINT32) TempData);
+  Regs.X.BX = EFI_OFFSET ((UINT32) TempData);
+
+  DumpPciHandle (&Private->IntThunk->PciHandler);
+
+  Private->LegacyBios.FarCall86 (
+    &Private->LegacyBios,
+    Private->Legacy16CallSegment,
+    Private->Legacy16CallOffset,
+    &Regs,
+    NULL,
+    0
+    );
+
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
+  Private->LegacyRegion->Lock (
+                           Private->LegacyRegion,
+                           0xE0000,
+                           0x20000,
+                           &Granularity
+                           );
+
+}
+
+
+/**
+  Program the interrupt routing register in all the PCI devices. On a PC AT system
+  this register contains the 8259 IRQ vector that matches it's PCI interrupt.
+
+  @param  Private                Legacy  BIOS Instance data
+
+  @retval EFI_SUCCESS            Succeed.
+  @retval EFI_ALREADY_STARTED    All PCI devices have been processed.
+
+**/
+EFI_STATUS
+PciProgramAllInterruptLineRegisters (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_PCI_IO_PROTOCOL               *PciIo;
+  EFI_LEGACY_8259_PROTOCOL          *Legacy8259;
+  EFI_LEGACY_INTERRUPT_PROTOCOL     *LegacyInterrupt;
+  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
+  UINT8                             InterruptPin;
+  UINTN                             Index;
+  UINTN                             HandleCount;
+  EFI_HANDLE                        *HandleBuffer;
+  UINTN                             MassStorageHandleCount;
+  EFI_HANDLE                        *MassStorageHandleBuffer;
+  UINTN                             MassStorageHandleIndex;
+  UINT8                             PciIrq;
+  UINT16                            Command;
+  UINTN                             PciSegment;
+  UINTN                             PciBus;
+  UINTN                             PciDevice;
+  UINTN                             PciFunction;
+  EFI_LEGACY_IRQ_ROUTING_ENTRY      *RoutingTable;
+  UINTN                             RoutingTableEntries;
+  UINT16                            LegMask;
+  UINT16                            LegEdgeLevel;
+  PCI_TYPE00                        PciConfigHeader;
+  EFI_LEGACY_PIRQ_TABLE_HEADER      *PirqTable;
+  UINTN                             PirqTableSize;
+  UINTN                             Flags;
+  HDD_INFO                          *HddInfo;
+  UINT64                            Supports;
+
+  //
+  // Note - This routine use to return immediately if Private->PciInterruptLine
+  //        was true. Routine changed since resets etc can cause not all
+  //        PciIo protocols to be registered the first time through.
+  // New algorithm is to do the copy $PIR table on first pass and save
+  // HandleCount on first pass. If subsequent passes LocateHandleBuffer gives
+  // a larger handle count then proceed with body of function else return
+  // EFI_ALREADY_STARTED. In addition check if PCI device InterruptLine != 0.
+  // If zero then function unprogrammed else skip function.
+  //
+  Legacy8259          = Private->Legacy8259;
+  LegacyInterrupt     = Private->LegacyInterrupt;
+  LegacyBiosPlatform  = Private->LegacyBiosPlatform;
+
+  LegacyBiosPlatform->GetRoutingTable (
+                        Private->LegacyBiosPlatform,
+                        (VOID *) &RoutingTable,
+                        &RoutingTableEntries,
+                        (VOID *) &PirqTable,
+                        &PirqTableSize,
+                        NULL,
+                        NULL
+                        );
+  CreateBridgeTable (RoutingTable, RoutingTableEntries);
+
+  if (!Private->PciInterruptLine) {
+    CopyPirqTable (
+      Private,
+      RoutingTable,
+      RoutingTableEntries,
+      PirqTable,
+      PirqTableSize
+      );
+  }
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+  if (HandleCount == mHandleCount) {
+    FreePool (HandleBuffer);
+    return EFI_ALREADY_STARTED;
+  }
+
+  if (mHandleCount == 0x00) {
+    mHandleCount = HandleCount;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    //
+    // If VGA then only do VGA to allow drives fore time to spin up
+    // otherwise assign PCI IRQs to all potential devices.
+    //
+    if ((mVgaInstallationInProgress) && (HandleBuffer[Index] != mVgaHandle)) {
+      continue;
+    } else {
+      //
+      // Force code to go through all handles next time called if video.
+      // This will catch case where HandleCount doesn't change but want
+      //  to get drive info etc.
+      //
+      mHandleCount = 0x00;
+    }
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Test whether the device can be enabled or not.
+    // If it can't be enabled, then just skip it to avoid further operation.
+    //
+    PciIo->Pci.Read (
+                 PciIo,
+                 EfiPciIoWidthUint32,
+                 0,
+                 sizeof (PciConfigHeader) / sizeof (UINT32),
+                 &PciConfigHeader
+                 );
+    Command = PciConfigHeader.Hdr.Command;
+
+    //
+    // Note PciIo->Attributes does not program the PCI command register
+    //
+    Status = PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationSupported,
+                      0,
+                      &Supports
+                      );
+    if (!EFI_ERROR (Status)) {
+      Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
+      Status = PciIo->Attributes (
+                        PciIo,
+                        EfiPciIoAttributeOperationEnable,
+                        Supports,
+                        NULL
+                        );
+    }
+    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x04, 1, &Command);
+
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    InterruptPin = PciConfigHeader.Device.InterruptPin;
+
+    if ((InterruptPin != 0) && (PciConfigHeader.Device.InterruptLine == PCI_INT_LINE_UNKNOWN)) {
+      PciIo->GetLocation (
+               PciIo,
+               &PciSegment,
+               &PciBus,
+               &PciDevice,
+               &PciFunction
+               );
+      //
+      // Translate PIRQ index back thru busses to slot bus with InterruptPin
+      // zero based
+      //
+      InterruptPin -= 1;
+
+      Status = GetBaseBus (
+                 Private,
+                 PciBus,
+                 PciDevice,
+                 RoutingTable,
+                 RoutingTableEntries
+                 );
+
+      if (Status == EFI_NOT_FOUND) {
+        TranslateBusPirq (
+          Private,
+          &PciBus,
+          &PciDevice,
+          &PciFunction,
+          &InterruptPin
+          );
+      }
+      //
+      // Translate InterruptPin(0-3) into PIRQ
+      //
+      Status = LegacyBiosPlatform->TranslatePirq (
+                                     LegacyBiosPlatform,
+                                     PciBus,
+                                     (PciDevice << 3),
+                                     PciFunction,
+                                     &InterruptPin,
+                                     &PciIrq
+                                     );
+      //
+      // TranslatePirq() should never fail or we are in trouble
+      // If it does return failure status, check your PIRQ routing table to see if some item is missing or incorrect
+      //
+      if (EFI_ERROR (Status)) {
+        DEBUG ((EFI_D_ERROR, "Translate Pirq Failed - Status = %r\n ", Status));
+        continue;
+      }
+
+      LegacyInterrupt->WritePirq (
+                         LegacyInterrupt,
+                         InterruptPin,
+                         PciIrq
+                         );
+
+      //
+      // Check if device has an OPROM associated with it.
+      // If not invoke special 16-bit function, to allow 16-bit
+      // code to install an interrupt handler.
+      //
+      Status = LegacyBiosCheckPciRom (
+                 &Private->LegacyBios,
+                 HandleBuffer[Index],
+                 NULL,
+                 NULL,
+                 &Flags
+                 );
+      if ((EFI_ERROR (Status)) && (PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE)) {
+        //
+        // Device has no OPROM associated with it and is a mass storage
+        // device. It needs to have an PCI IRQ handler installed. To
+        // correctly install the handler we need to insure device is
+        // connected. The device may just have register itself but not
+        // been connected. Re-read PCI config space after as it can
+        // change
+        //
+        //
+        // Get IDE Handle. If matches handle then skip ConnectController
+        // since ConnectController may force native mode and we don't
+        // want that for primary IDE controller
+        //
+        MassStorageHandleCount = 0;
+        MassStorageHandleBuffer = NULL;
+        LegacyBiosPlatform->GetPlatformHandle (
+                              Private->LegacyBiosPlatform,
+                              EfiGetPlatformIdeHandle,
+                              0,
+                              &MassStorageHandleBuffer,
+                              &MassStorageHandleCount,
+                              NULL
+                              );
+
+        HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0];
+
+        LegacyBiosBuildIdeData (Private, &HddInfo, 0);
+        PciIo->Pci.Read (
+                     PciIo,
+                     EfiPciIoWidthUint32,
+                     0,
+                     sizeof (PciConfigHeader) / sizeof (UINT32),
+                     &PciConfigHeader
+                     );
+
+        for (MassStorageHandleIndex = 0; MassStorageHandleIndex < MassStorageHandleCount; MassStorageHandleIndex++) {
+          if (MassStorageHandleBuffer[MassStorageHandleIndex] == HandleBuffer[Index]) {
+            //
+            // InstallLegacyIrqHandler according to Platform requirement
+            //
+            InstallLegacyIrqHandler (
+              Private,
+              PciIo,
+              PciIrq,
+              &PciConfigHeader
+              );
+            break;
+          }
+        }
+      }
+      //
+      // Write InterruptPin and enable 8259.
+      //
+      PciIo->Pci.Write (
+                   PciIo,
+                   EfiPciIoWidthUint8,
+                   0x3c,
+                   1,
+                   &PciIrq
+                   );
+      Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask = (UINT16) (Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask | (UINT16) (1 << PciIrq));
+
+      Legacy8259->GetMask (
+                    Legacy8259,
+                    &LegMask,
+                    &LegEdgeLevel,
+                    NULL,
+                    NULL
+                    );
+
+      LegMask       = (UINT16) (LegMask & (UINT16)~(1 << PciIrq));
+      LegEdgeLevel  = (UINT16) (LegEdgeLevel | (UINT16) (1 << PciIrq));
+      Legacy8259->SetMask (
+                    Legacy8259,
+                    &LegMask,
+                    &LegEdgeLevel,
+                    NULL,
+                    NULL
+                    );
+    }
+  }
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Find & verify PnP Expansion header in ROM image
+
+  @param  Private                Protocol instance pointer.
+  @param  FirstHeader            1 = Find first header, 0 = Find successive headers
+  @param  PnpPtr                 Input Rom start if FirstHeader =1, Current Header
+                                 otherwise Output Next header, if it exists
+
+  @retval EFI_SUCCESS            Next Header found at BasePnpPtr
+  @retval EFI_NOT_FOUND          No more headers
+
+**/
+EFI_STATUS
+FindNextPnpExpansionHeader (
+  IN  LEGACY_BIOS_INSTANCE             *Private,
+  IN BOOLEAN                           FirstHeader,
+  IN OUT LEGACY_PNP_EXPANSION_HEADER   **PnpPtr
+
+  )
+{
+  UINTN                       TempData;
+  LEGACY_PNP_EXPANSION_HEADER *LocalPnpPtr;
+  LocalPnpPtr = *PnpPtr;
+  if (FirstHeader == FIRST_INSTANCE) {
+    mBasePnpPtr     = LocalPnpPtr;
+    mBbsRomSegment  = (UINT16) ((UINTN) mBasePnpPtr >> 4);
+    //
+    // Offset 0x1a gives offset to PnP expansion header for the first
+    // instance, there after the structure gives the offset to the next
+    // structure
+    //
+    LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) ((UINT8 *) LocalPnpPtr + 0x1a);
+    TempData    = (*((UINT16 *) LocalPnpPtr));
+  } else {
+    TempData = (UINT16) LocalPnpPtr->NextHeader;
+  }
+
+  LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) (((UINT8 *) mBasePnpPtr + TempData));
+
+  //
+  // Search for PnP table in Shadowed ROM
+  //
+  *PnpPtr = LocalPnpPtr;
+  if (*(UINT32 *) LocalPnpPtr == SIGNATURE_32 ('$', 'P', 'n', 'P')) {
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+}
+
+
+/**
+  Update list of Bev or BCV table entries.
+
+  @param  Private                Protocol instance pointer.
+  @param  RomStart               Table of ROM start address in RAM/ROM. PciIo  _
+                                 Handle to PCI IO for this device
+  @param  PciIo                  Instance of PCI I/O Protocol
+
+  @retval EFI_SUCCESS            Always should succeed.
+
+**/
+EFI_STATUS
+UpdateBevBcvTable (
+  IN  LEGACY_BIOS_INSTANCE             *Private,
+  IN  EFI_LEGACY_EXPANSION_ROM_HEADER  *RomStart,
+  IN  EFI_PCI_IO_PROTOCOL              *PciIo
+  )
+{
+  VOID                            *RomEnd;
+  BBS_TABLE                       *BbsTable;
+  UINTN                           BbsIndex;
+  EFI_LEGACY_EXPANSION_ROM_HEADER *PciPtr;
+  LEGACY_PNP_EXPANSION_HEADER     *PnpPtr;
+  BOOLEAN                         Instance;
+  EFI_STATUS                      Status;
+  UINTN                           Segment;
+  UINTN                           Bus;
+  UINTN                           Device;
+  UINTN                           Function;
+  UINT8                           Class;
+  UINT16                          DeviceType;
+  Segment     = 0;
+  Bus         = 0;
+  Device      = 0;
+  Function    = 0;
+  Class       = 0;
+  DeviceType  = BBS_UNKNOWN;
+
+  //
+  // Skip floppy and 2*onboard IDE controller entries(Master/Slave per
+  // controller).
+  //
+  BbsIndex  = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries;
+
+  BbsTable  = (BBS_TABLE*)(UINTN) Private->IntThunk->EfiToLegacy16BootTable.BbsTable;
+  PnpPtr    = (LEGACY_PNP_EXPANSION_HEADER *) RomStart;
+  PciPtr    = (EFI_LEGACY_EXPANSION_ROM_HEADER *) RomStart;
+
+  RomEnd    = (VOID *) (PciPtr->Size512 * 512 + (UINTN) PciPtr);
+  Instance  = FIRST_INSTANCE;
+  //
+  // OPROMs like PXE may not be tied to a piece of hardware and thus
+  // don't have a PciIo associated with them
+  //
+  if (PciIo != NULL) {
+    PciIo->GetLocation (
+             PciIo,
+             &Segment,
+             &Bus,
+             &Device,
+             &Function
+             );
+    PciIo->Pci.Read (
+                 PciIo,
+                 EfiPciIoWidthUint8,
+                 0x0b,
+                 1,
+                 &Class
+                 );
+
+    if (Class == PCI_CLASS_MASS_STORAGE) {
+      DeviceType = BBS_HARDDISK;
+    } else {
+      if (Class == PCI_CLASS_NETWORK) {
+        DeviceType = BBS_EMBED_NETWORK;
+      }
+    }
+  }
+
+  while (TRUE) {
+    Status    = FindNextPnpExpansionHeader (Private, Instance, &PnpPtr);
+    Instance  = NOT_FIRST_INSTANCE;
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+    //
+    // There can be additional $PnP headers within the OPROM.
+    // Example: SCSI can have one per drive.
+    //
+    BbsTable[BbsIndex].BootPriority             = BBS_UNPRIORITIZED_ENTRY;
+    BbsTable[BbsIndex].DeviceType               = DeviceType;
+    BbsTable[BbsIndex].Bus                      = (UINT32) Bus;
+    BbsTable[BbsIndex].Device                   = (UINT32) Device;
+    BbsTable[BbsIndex].Function                 = (UINT32) Function;
+    BbsTable[BbsIndex].StatusFlags.OldPosition  = 0;
+    BbsTable[BbsIndex].StatusFlags.Reserved1    = 0;
+    BbsTable[BbsIndex].StatusFlags.Enabled      = 0;
+    BbsTable[BbsIndex].StatusFlags.Failed       = 0;
+    BbsTable[BbsIndex].StatusFlags.MediaPresent = 0;
+    BbsTable[BbsIndex].StatusFlags.Reserved2    = 0;
+    BbsTable[BbsIndex].Class                    = PnpPtr->Class;
+    BbsTable[BbsIndex].SubClass                 = PnpPtr->SubClass;
+    BbsTable[BbsIndex].DescStringOffset         = PnpPtr->ProductNamePointer;
+    BbsTable[BbsIndex].DescStringSegment        = mBbsRomSegment;
+    BbsTable[BbsIndex].MfgStringOffset          = PnpPtr->MfgPointer;
+    BbsTable[BbsIndex].MfgStringSegment         = mBbsRomSegment;
+    BbsTable[BbsIndex].BootHandlerSegment       = mBbsRomSegment;
+
+    //
+    // Have seen case where PXE base code have PnP expansion ROM
+    // header but no Bcv or Bev vectors.
+    //
+    if (PnpPtr->Bcv != 0) {
+      BbsTable[BbsIndex].BootHandlerOffset = PnpPtr->Bcv;
+      ++BbsIndex;
+    }
+
+    if (PnpPtr->Bev != 0) {
+      BbsTable[BbsIndex].BootHandlerOffset  = PnpPtr->Bev;
+      BbsTable[BbsIndex].DeviceType         = BBS_BEV_DEVICE;
+      ++BbsIndex;
+    }
+
+    if ((PnpPtr == (LEGACY_PNP_EXPANSION_HEADER *) PciPtr) || (PnpPtr > (LEGACY_PNP_EXPANSION_HEADER *) RomEnd)) {
+      break;
+    }
+  }
+
+  BbsTable[BbsIndex].BootPriority = BBS_IGNORE_ENTRY;
+  Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries = (UINT32) BbsIndex;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Shadow all the PCI legacy ROMs. Use data from the Legacy BIOS Protocol
+  to chose the order. Skip any devices that have already have legacy
+  BIOS run.
+
+  @param  Private                Protocol instance pointer.
+
+  @retval EFI_SUCCESS            Succeed.
+  @retval EFI_UNSUPPORTED        Cannot get VGA device handle.
+
+**/
+EFI_STATUS
+PciShadowRoms (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_PCI_IO_PROTOCOL               *PciIo;
+  PCI_TYPE00                        Pci;
+  UINTN                             Index;
+  UINTN                             HandleCount;
+  EFI_HANDLE                        *HandleBuffer;
+  EFI_HANDLE                        VgaHandle;
+  EFI_HANDLE                        FirstHandle;
+  VOID                              **RomStart;
+  UINTN                             Flags;
+  PCI_TYPE00                        PciConfigHeader;
+  UINT16                            *Command;
+  UINT64                            Supports;
+
+  //
+  // Make the VGA device first
+  //
+  Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformVgaHandle,
+                                          0,
+                                          &HandleBuffer,
+                                          &HandleCount,
+                                          NULL
+                                          );
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  VgaHandle = HandleBuffer[0];
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Place the VGA handle as first.
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+    if (HandleBuffer[Index] == VgaHandle) {
+      FirstHandle         = HandleBuffer[0];
+      HandleBuffer[0]     = HandleBuffer[Index];
+      HandleBuffer[Index] = FirstHandle;
+      break;
+    }
+  }
+  //
+  // Allocate memory to save Command WORD from each device. We do this
+  // to restore devices to same state as EFI after switching to legacy.
+  //
+  Command = (UINT16 *) AllocatePool (
+                         sizeof (UINT16) * (HandleCount + 1)
+                         );
+  if (NULL == Command) {
+    FreePool (HandleBuffer);
+    return EFI_OUT_OF_RESOURCES;
+  }
+  //
+  // Disconnect all EFI devices first. This covers cases where alegacy BIOS
+  // may control multiple PCI devices.
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Save command register for "connect" loop
+    //
+    PciIo->Pci.Read (
+                 PciIo,
+                 EfiPciIoWidthUint32,
+                 0,
+                 sizeof (PciConfigHeader) / sizeof (UINT32),
+                 &PciConfigHeader
+                 );
+    Command[Index] = PciConfigHeader.Hdr.Command;
+    //
+    // Skip any device that already has a legacy ROM run
+    //
+    Status = IsLegacyRom (HandleBuffer[Index]);
+    if (!EFI_ERROR (Status)) {
+      continue;
+    }
+    //
+    // Stop EFI Drivers with oprom.
+    //
+    gBS->DisconnectController (
+           HandleBuffer[Index],
+           NULL,
+           NULL
+           );
+  }
+  //
+  // For every device that has not had a legacy ROM started. Start a legacy ROM.
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Here make sure if one VGA have been shadowed,
+    // then wil not shadowed another one.
+    //
+    PciIo->Pci.Read (
+                 PciIo,
+                 EfiPciIoWidthUint32,
+                 0,
+                 sizeof (Pci) / sizeof (UINT32),
+                 &Pci
+                 );
+
+    //
+    // Only one Video OPROM can be given control in BIOS phase. If there are multiple Video devices,
+    // one will work in legacy mode (OPROM will be given control) and
+    // other Video devices will work in native mode (OS driver will handle these devices).
+    //
+    if (IS_PCI_DISPLAY (&Pci) && Index != 0) {
+      continue;
+    }
+    //
+    // Skip any device that already has a legacy ROM run
+    //
+    Status = IsLegacyRom (HandleBuffer[Index]);
+    if (!EFI_ERROR (Status)) {
+      continue;
+    }
+
+    //
+    // If legacy VBIOS Oprom has not been dispatched before, install legacy VBIOS here.
+    //
+    if (IS_PCI_DISPLAY (&Pci) && Index == 0) {
+      Status = LegacyBiosInstallVgaRom (Private);
+      //
+      // A return status of EFI_NOT_FOUND is considered valid (No EFI
+      // driver is controlling video).
+      //
+      ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_NOT_FOUND));
+      continue;
+    }
+
+    //
+    // Install legacy ROM
+    //
+    Status = LegacyBiosInstallPciRom (
+               &Private->LegacyBios,
+               HandleBuffer[Index],
+               NULL,
+               &Flags,
+               NULL,
+               NULL,
+               (VOID **) &RomStart,
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      if (!((Status == EFI_UNSUPPORTED) && (Flags == NO_ROM))) {
+        continue;
+      }
+    }
+    //
+    // Restore Command register so legacy has same devices enabled or disabled
+    // as EFI.
+    // If Flags = NO_ROM use command register as is. This covers the
+    //            following cases:
+    //              Device has no ROMs associated with it.
+    //              Device has ROM associated with it but was already
+    //              installed.
+    //          = ROM_FOUND but not VALID_LEGACY_ROM, disable it.
+    //          = ROM_FOUND and VALID_LEGACY_ROM, enable it.
+    //
+    if ((Flags & ROM_FOUND) == ROM_FOUND) {
+      if ((Flags & VALID_LEGACY_ROM) == 0) {
+        Command[Index] = 0;
+      } else {
+        //
+        // For several VGAs, only one of them can be enabled.
+        //
+        Status = PciIo->Attributes (
+                          PciIo,
+                          EfiPciIoAttributeOperationSupported,
+                          0,
+                          &Supports
+                          );
+        if (!EFI_ERROR (Status)) {
+          Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
+          Status = PciIo->Attributes (
+                            PciIo,
+                            EfiPciIoAttributeOperationEnable,
+                            Supports,
+                            NULL
+                            );
+        }
+        if (!EFI_ERROR (Status)) {
+          Command[Index] = 0x1f;
+        }
+      }
+    }
+
+    PciIo->Pci.Write (
+                 PciIo,
+                 EfiPciIoWidthUint16,
+                 0x04,
+                 1,
+                 &Command[Index]
+                 );
+  }
+
+  FreePool (Command);
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Test to see if a legacy PCI ROM exists for this device. Optionally return
+  the Legacy ROM instance for this PCI device.
+
+  @param  This                   Protocol instance pointer.
+  @param  PciHandle              The PCI PC-AT OPROM from this devices ROM BAR will
+                                 be loaded
+  @param  RomImage               Return the legacy PCI ROM for this device
+  @param  RomSize                Size of ROM Image
+  @param  Flags                  Indicates if ROM found and if PC-AT.
+
+  @retval EFI_SUCCESS            Legacy Option ROM available for this device
+  @retval EFI_UNSUPPORTED        Legacy Option ROM not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosCheckPciRom (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN  EFI_HANDLE                        PciHandle,
+  OUT VOID                              **RomImage, OPTIONAL
+  OUT UINTN                             *RomSize, OPTIONAL
+  OUT UINTN                             *Flags
+  )
+{
+  return LegacyBiosCheckPciRomEx (
+           This,
+           PciHandle,
+           RomImage,
+           RomSize,
+           NULL,
+           Flags,
+           NULL,
+           NULL
+           );
+
+}
+
+/**
+
+  Routine Description:
+    Test to see if a legacy PCI ROM exists for this device. Optionally return
+    the Legacy ROM instance for this PCI device.
+
+    @param[in] This          Protocol instance pointer.
+    @param[in] PciHandle               The PCI PC-AT OPROM from this devices ROM BAR will be loaded
+    @param[out] RomImage               Return the legacy PCI ROM for this device
+    @param[out] RomSize                Size of ROM Image
+    @param[out] RuntimeImageLength     Runtime size of ROM Image
+    @param[out] Flags                  Indicates if ROM found and if PC-AT.
+    @param[out] OpromRevision          Revision of the PCI Rom
+    @param[out] ConfigUtilityCodeHeaderPointer of Configuration Utility Code Header
+
+    @return EFI_SUCCESS            Legacy Option ROM available for this device
+    @return EFI_ALREADY_STARTED    This device is already managed by its Oprom
+    @return EFI_UNSUPPORTED        Legacy Option ROM not supported.
+
+**/
+EFI_STATUS
+LegacyBiosCheckPciRomEx (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN  EFI_HANDLE                        PciHandle,
+  OUT VOID                              **RomImage, OPTIONAL
+  OUT UINTN                             *RomSize, OPTIONAL
+  OUT UINTN                             *RuntimeImageLength, OPTIONAL
+  OUT UINTN                             *Flags, OPTIONAL
+  OUT UINT8                             *OpromRevision, OPTIONAL
+  OUT VOID                              **ConfigUtilityCodeHeader OPTIONAL
+  )
+{
+  EFI_STATUS                      Status;
+  LEGACY_BIOS_INSTANCE            *Private;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
+  UINTN                           LocalRomSize;
+  VOID                            *LocalRomImage;
+  PCI_TYPE00                      PciConfigHeader;
+  VOID                            *LocalConfigUtilityCodeHeader;
+
+  LocalConfigUtilityCodeHeader = NULL;
+  *Flags = NO_ROM;
+  Status = gBS->HandleProtocol (
+                  PciHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // See if the option ROM for PciHandle has already been executed
+  //
+  Status = IsLegacyRom (PciHandle);
+  if (!EFI_ERROR (Status)) {
+    *Flags |= (UINTN)(ROM_FOUND | VALID_LEGACY_ROM);
+    return EFI_SUCCESS;
+  }
+  //
+  // Check for PCI ROM Bar
+  //
+  LocalRomSize  = (UINTN) PciIo->RomSize;
+  LocalRomImage = PciIo->RomImage;
+  if (LocalRomSize != 0) {
+    *Flags |= ROM_FOUND;
+  }
+
+  //
+  // PCI specification states you should check VendorId and Device Id.
+  //
+  PciIo->Pci.Read (
+               PciIo,
+               EfiPciIoWidthUint32,
+               0,
+               sizeof (PciConfigHeader) / sizeof (UINT32),
+               &PciConfigHeader
+               );
+
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  Status = GetPciLegacyRom (
+             Private->Csm16PciInterfaceVersion,
+             PciConfigHeader.Hdr.VendorId,
+             PciConfigHeader.Hdr.DeviceId,
+             &LocalRomImage,
+             &LocalRomSize,
+             RuntimeImageLength,
+             OpromRevision,
+             &LocalConfigUtilityCodeHeader
+             );
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  *Flags |= VALID_LEGACY_ROM;
+
+  //
+  // See if Configuration Utility Code Header valid
+  //
+  if (LocalConfigUtilityCodeHeader != NULL) {
+    *Flags |= ROM_WITH_CONFIG;
+  }
+
+  if (ConfigUtilityCodeHeader != NULL) {
+    *ConfigUtilityCodeHeader = LocalConfigUtilityCodeHeader;
+  }
+
+  if (RomImage != NULL) {
+    *RomImage = LocalRomImage;
+  }
+
+  if (RomSize != NULL) {
+    *RomSize = LocalRomSize;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Load a legacy PC-AT OPROM on the PciHandle device. Return information
+  about how many disks were added by the OPROM and the shadow address and
+  size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C:
+
+  @retval EFI_SUCCESS   Legacy ROM loaded for this device
+  @retval EFI_NOT_FOUND No PS2 Keyboard found
+
+**/
+EFI_STATUS
+EnablePs2Keyboard (
+  VOID
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_HANDLE                          *HandleBuffer;
+  UINTN                               HandleCount;
+  EFI_ISA_IO_PROTOCOL                 *IsaIo;
+  UINTN                               Index;
+
+  //
+  // Get SimpleTextIn and find PS2 controller
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiSimpleTextInProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+  for (Index = 0; Index < HandleCount; Index++) {
+    //
+    // Open the IO Abstraction(s) needed to perform the supported test
+    //
+    Status = gBS->OpenProtocol (
+                    HandleBuffer[Index],
+                    &gEfiIsaIoProtocolGuid,
+                    (VOID **) &IsaIo,
+                    NULL,
+                    HandleBuffer[Index],
+                    EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+                    );
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // Use the ISA I/O Protocol to see if Controller is the Keyboard
+      // controller
+      //
+      if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) {
+        Status = EFI_UNSUPPORTED;
+      }
+
+      gBS->CloseProtocol (
+             HandleBuffer[Index],
+             &gEfiIsaIoProtocolGuid,
+             NULL,
+             HandleBuffer[Index]
+             );
+    }
+
+    if (!EFI_ERROR (Status)) {
+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE);
+    }
+  }
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Load a legacy PC-AT OpROM for VGA controller.
+
+  @param  Private                Driver private data.
+
+  @retval EFI_SUCCESS            Legacy ROM successfully installed for this device.
+  @retval EFI_DEVICE_ERROR       No VGA device handle found, or native EFI video
+                                 driver cannot be successfully disconnected, or VGA
+                                 thunk driver cannot be successfully connected.
+
+**/
+EFI_STATUS
+LegacyBiosInstallVgaRom (
+  IN  LEGACY_BIOS_INSTANCE            *Private
+  )
+{
+  EFI_STATUS                           Status;
+  EFI_HANDLE                           VgaHandle;
+  UINTN                                HandleCount;
+  EFI_HANDLE                           *HandleBuffer;
+  EFI_HANDLE                           *ConnectHandleBuffer;
+  EFI_PCI_IO_PROTOCOL                  *PciIo;
+  PCI_TYPE00                           PciConfigHeader;
+  UINT64                               Supports;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfoBuffer;
+  UINTN                                EntryCount;
+  UINTN                                Index;
+  VOID                                 *Interface;
+
+  //
+  // EfiLegacyBiosGuild attached to a device implies that there is a legacy
+  // BIOS associated with that device.
+  //
+  // There are 3 cases to consider.
+  //   Case 1: No EFI driver is controlling the video.
+  //     Action: Return EFI_SUCCESS from DisconnectController, search
+  //             video thunk driver, and connect it.
+  //   Case 2: EFI driver is controlling the video and EfiLegacyBiosGuid is
+  //           not on the image handle.
+  //     Action: Disconnect EFI driver.
+  //             ConnectController for video thunk
+  //   Case 3: EFI driver is controlling the video and EfiLegacyBiosGuid is
+  //           on the image handle.
+  //     Action: Do nothing and set Private->VgaInstalled = TRUE.
+  //             Then this routine is not called any more.
+  //
+  //
+  // Get the VGA device.
+  //
+  Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformVgaHandle,
+                                          0,
+                                          &HandleBuffer,
+                                          &HandleCount,
+                                          NULL
+                                          );
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  VgaHandle = HandleBuffer[0];
+
+  //
+  // Check whether video thunk driver already starts.
+  //
+  Status = gBS->OpenProtocolInformation (
+                  VgaHandle,
+                  &gEfiPciIoProtocolGuid,
+                  &OpenInfoBuffer,
+                  &EntryCount
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (Index = 0; Index < EntryCount; Index++) {
+    if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {
+      Status = gBS->HandleProtocol (
+                      OpenInfoBuffer[Index].AgentHandle,
+                      &gEfiLegacyBiosGuid,
+                      (VOID **) &Interface
+                      );
+      if (!EFI_ERROR (Status)) {
+        //
+        // This should be video thunk driver which is managing video device
+        // So it need not start again
+        //
+        DEBUG ((EFI_D_INFO, "Video thunk driver already start! Return!\n"));
+        Private->VgaInstalled = TRUE;
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  //
+  // Kick off the native EFI driver
+  //
+  Status = gBS->DisconnectController (
+                  VgaHandle,
+                  NULL,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    if (Status != EFI_NOT_FOUND) {
+      return EFI_DEVICE_ERROR;
+    } else {
+      return Status;
+    }
+  }
+  //
+  // Find all the Thunk Driver
+  //
+  HandleBuffer = NULL;
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiLegacyBiosGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  ASSERT_EFI_ERROR (Status);
+  ConnectHandleBuffer = (EFI_HANDLE *) AllocatePool (sizeof (EFI_HANDLE) * (HandleCount + 1));
+  ASSERT (ConnectHandleBuffer != NULL);
+
+  CopyMem (
+    ConnectHandleBuffer,
+    HandleBuffer,
+    sizeof (EFI_HANDLE) * HandleCount
+    );
+  ConnectHandleBuffer[HandleCount] = NULL;
+
+  FreePool (HandleBuffer);
+
+  //
+  // Enable the device and make sure VGA cycles are being forwarded to this VGA device
+  //
+  Status = gBS->HandleProtocol (
+                  VgaHandle,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo
+                  );
+  ASSERT_EFI_ERROR (Status);
+  PciIo->Pci.Read (
+               PciIo,
+               EfiPciIoWidthUint32,
+               0,
+               sizeof (PciConfigHeader) / sizeof (UINT32),
+               &PciConfigHeader
+               );
+
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationSupported,
+                    0,
+                    &Supports
+                    );
+  if (!EFI_ERROR (Status)) {
+    Supports &= (UINT64)(EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | \
+                         EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16);
+    Status = PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationEnable,
+                      Supports,
+                      NULL
+                      );
+  }
+
+  if (Status == EFI_SUCCESS) {
+    Private->VgaInstalled = TRUE;
+
+    //
+    // Attach the VGA thunk driver.
+    // Assume the video is installed. This prevents potential of infinite recursion.
+    //
+    Status = gBS->ConnectController (
+                    VgaHandle,
+                    ConnectHandleBuffer,
+                    NULL,
+                    TRUE
+                    );
+  }
+
+  FreePool (ConnectHandleBuffer);
+
+  if (EFI_ERROR (Status)) {
+
+    Private->VgaInstalled = FALSE;
+
+    //
+    // Reconnect the EFI VGA driver.
+    //
+    gBS->ConnectController (VgaHandle, NULL, NULL, TRUE);
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Load a legacy PC-AT OpROM.
+
+  @param  This                              Protocol instance pointer.
+  @param  Private                          Driver's private data.
+  @param  PciHandle                      The EFI handle for the PCI device. It could be
+                                                    NULL if the  OpROM image is not associated with
+                                                    any device.
+  @param  OpromRevision              The revision of PCI PC-AT ROM image.
+  @param  RomImage                    Pointer to PCI PC-AT ROM image header. It must not
+                                                    be NULL.
+  @param  ImageSize                     Size of the PCI PC-AT ROM image.
+  @param  RuntimeImageLength      On input is the max runtime image length indicated by the PCIR structure
+                                                    On output is the actual runtime image length
+  @param  DiskStart                       Disk number of first device hooked by the ROM. If
+                                                    DiskStart is the same as DiskEnd no disked were
+                                                    hooked.
+  @param  DiskEnd                         Disk number of the last device hooked by the ROM.
+  @param  RomShadowAddress       Shadow address of PC-AT ROM
+
+  @retval EFI_SUCCESS            Legacy ROM loaded for this device
+  @retval EFI_OUT_OF_RESOURCES   No more space for this ROM
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosInstallRom (
+  IN EFI_LEGACY_BIOS_PROTOCOL           *This,
+  IN LEGACY_BIOS_INSTANCE               *Private,
+  IN EFI_HANDLE                         PciHandle,
+  IN UINT8                              OpromRevision,
+  IN VOID                               *RomImage,
+  IN UINTN                              ImageSize,
+  IN OUT UINTN                          *RuntimeImageLength,
+  OUT UINT8                             *DiskStart, OPTIONAL
+  OUT UINT8                             *DiskEnd, OPTIONAL
+  OUT VOID                              **RomShadowAddress OPTIONAL
+  )
+{
+  EFI_STATUS            Status;
+  EFI_STATUS            PciEnableStatus;
+  EFI_PCI_IO_PROTOCOL   *PciIo;
+  UINT8                 LocalDiskStart;
+  UINT8                 LocalDiskEnd;
+  UINTN                 Segment;
+  UINTN                 Bus;
+  UINTN                 Device;
+  UINTN                 Function;
+  EFI_IA32_REGISTER_SET Regs;
+  UINT8                 VideoMode;
+  UINT8                 OldVideoMode;
+  EFI_TIME              BootTime;
+  UINT32                *BdaPtr;
+  UINT32                LocalTime;
+  UINT32                StartBbsIndex;
+  UINT32                EndBbsIndex;
+  UINT32                MaxRomAddr;
+  UINTN                 TempData;
+  UINTN                 InitAddress;
+  UINTN                 RuntimeAddress;
+  EFI_PHYSICAL_ADDRESS  PhysicalAddress;
+  UINT32                Granularity;
+
+  PciIo           = NULL;
+  LocalDiskStart  = 0;
+  LocalDiskEnd    = 0;
+  Segment         = 0;
+  Bus             = 0;
+  Device          = 0;
+  Function        = 0;
+  VideoMode       = 0;
+  OldVideoMode    = 0;
+  PhysicalAddress = 0;
+  MaxRomAddr      = PcdGet32 (PcdEndOpromShadowAddress);
+
+  if ((Private->Legacy16Table->TableLength >= OFFSET_OF(EFI_COMPATIBILITY16_TABLE, HiPermanentMemoryAddress)) &&
+      (Private->Legacy16Table->UmaAddress != 0) &&
+      (Private->Legacy16Table->UmaSize != 0) &&
+      (MaxRomAddr > (Private->Legacy16Table->UmaAddress))) {
+    MaxRomAddr = Private->Legacy16Table->UmaAddress;
+  }
+
+
+  PciProgramAllInterruptLineRegisters (Private);
+
+  if ((OpromRevision >= 3) && (Private->Csm16PciInterfaceVersion >= 0x0300)) {
+    //
+    // CSM16 3.0 meets PCI 3.0 OpROM
+    //   first test if there is enough space for its INIT code
+    //
+    PhysicalAddress = CONVENTIONAL_MEMORY_TOP;
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiBootServicesCode,
+                    EFI_SIZE_TO_PAGES (ImageSize),
+                    &PhysicalAddress
+                    );
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__));
+      //
+      // Report Status Code to indicate that there is no enough space for OpROM
+      //
+      REPORT_STATUS_CODE (
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,
+        (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE)
+        );
+      return EFI_OUT_OF_RESOURCES;
+    }
+    InitAddress = (UINTN) PhysicalAddress;
+    //
+    //   then test if there is enough space for its RT code
+    //
+    RuntimeAddress = Private->OptionRom;
+    if (RuntimeAddress + *RuntimeImageLength > MaxRomAddr) {
+      DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__));
+      gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize));
+      //
+      // Report Status Code to indicate that there is no enough space for OpROM
+      //
+      REPORT_STATUS_CODE (
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,
+        (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE)
+        );
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    // CSM16 3.0 meets PCI 2.x OpROM
+    // CSM16 2.x meets PCI 2.x/3.0 OpROM
+    //   test if there is enough space for its INIT code
+    //
+    InitAddress    = PCI_START_ADDRESS (Private->OptionRom);
+    if (InitAddress + ImageSize > MaxRomAddr) {
+      DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__));
+      //
+      // Report Status Code to indicate that there is no enough space for OpROM
+      //
+      REPORT_STATUS_CODE (
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,
+        (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE)
+        );
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    RuntimeAddress = InitAddress;
+  }
+
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           0xE0000,
+                           0x20000,
+                           &Granularity
+                           );
+
+  Private->LegacyRegion->UnLock (
+                           Private->LegacyRegion,
+                           (UINT32) RuntimeAddress,
+                           (UINT32) ImageSize,
+                           &Granularity
+                           );
+
+  DEBUG ((EFI_D_INFO, " Shadowing OpROM init/runtime/isize = %x/%x/%x\n", InitAddress, RuntimeAddress, ImageSize));
+
+  CopyMem ((VOID *) InitAddress, RomImage, ImageSize);
+
+  //
+  // Read the highest disk number "installed: and assume a new disk will
+  // show up on the first drive past the current value.
+  // There are several considerations here:
+  // 1. Non-BBS compliant drives will change 40:75 but 16-bit CSM will undo
+  //    the change until boot selection time frame.
+  // 2. BBS compliants drives will not change 40:75 until boot time.
+  // 3. Onboard IDE controllers will change 40:75
+  //
+  ACCESS_PAGE0_CODE (
+    LocalDiskStart = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80);
+    if ((Private->Disk4075 + 0x80) < LocalDiskStart) {
+      //
+      // Update table since onboard IDE drives found
+      //
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment        = 0xff;
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus            = 0xff;
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice         = 0xff;
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction       = 0xff;
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber  = (UINT8) (Private->Disk4075 + 0x80);
+      Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber    = LocalDiskStart;
+      Private->LegacyEfiHddTableIndex ++;
+      Private->Disk4075 = (UINT8) (LocalDiskStart & 0x7f);
+      Private->DiskEnd  = LocalDiskStart;
+    }
+
+    if (PciHandle != mVgaHandle) {
+
+      EnablePs2Keyboard ();
+
+      //
+      // Store current mode settings since PrepareToScanRom may change mode.
+      //
+      VideoMode = *(UINT8 *) ((UINTN) (0x400 + BDA_VIDEO_MODE));
+    }
+  );
+
+  //
+  // Notify the platform that we are about to scan the ROM
+  //
+  Status = Private->LegacyBiosPlatform->PlatformHooks (
+                                          Private->LegacyBiosPlatform,
+                                          EfiPlatformHookPrepareToScanRom,
+                                          0,
+                                          PciHandle,
+                                          &InitAddress,
+                                          NULL,
+                                          NULL
+                                          );
+
+  //
+  // If Status returned is EFI_UNSUPPORTED then abort due to platform
+  // policy.
+  //
+  if (Status == EFI_UNSUPPORTED) {
+    goto Done;
+  }
+
+  //
+  // Report corresponding status code
+  //
+  REPORT_STATUS_CODE (
+    EFI_PROGRESS_CODE,
+    (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_CSM_LEGACY_ROM_INIT)
+    );
+
+  //
+  // Generate number of ticks since midnight for BDA. Some OPROMs require
+  // this. Place result in 40:6C-6F
+  //
+  gRT->GetTime (&BootTime, NULL);
+  LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second;
+
+  //
+  // Multiply result by 18.2 for number of ticks since midnight.
+  // Use 182/10 to avoid floating point math.
+  //
+  ACCESS_PAGE0_CODE (
+    LocalTime = (LocalTime * 182) / 10;
+    BdaPtr    = (UINT32 *) ((UINTN) 0x46C);
+    *BdaPtr   = LocalTime;
+  );
+
+  //
+  // Pass in handoff data
+  //
+  PciEnableStatus = EFI_UNSUPPORTED;
+  ZeroMem (&Regs, sizeof (Regs));
+  if (PciHandle != NULL) {
+
+    Status = gBS->HandleProtocol (
+                    PciHandle,
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Enable command register.
+    //
+    PciEnableStatus = PciIo->Attributes (
+                               PciIo,
+                               EfiPciIoAttributeOperationEnable,
+                               EFI_PCI_DEVICE_ENABLE,
+                               NULL
+                               );
+
+    PciIo->GetLocation (
+             PciIo,
+             &Segment,
+             &Bus,
+             &Device,
+             &Function
+             );
+    DEBUG ((EFI_D_INFO, "Shadowing OpROM on the PCI device %x/%x/%x\n", Bus, Device, Function));
+  }
+
+  mIgnoreBbsUpdateFlag  = FALSE;
+  Regs.X.AX             = Legacy16DispatchOprom;
+
+  //
+  // Generate DispatchOpRomTable data
+  //
+  Private->IntThunk->DispatchOpromTable.PnPInstallationCheckSegment = Private->Legacy16Table->PnPInstallationCheckSegment;
+  Private->IntThunk->DispatchOpromTable.PnPInstallationCheckOffset  = Private->Legacy16Table->PnPInstallationCheckOffset;
+  Private->IntThunk->DispatchOpromTable.OpromSegment                = (UINT16) (InitAddress >> 4);
+  Private->IntThunk->DispatchOpromTable.PciBus                      = (UINT8) Bus;
+  Private->IntThunk->DispatchOpromTable.PciDeviceFunction           = (UINT8) ((Device << 3) | Function);
+  Private->IntThunk->DispatchOpromTable.NumberBbsEntries            = (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries;
+  Private->IntThunk->DispatchOpromTable.BbsTablePointer             = (UINT32) (UINTN) Private->BbsTablePtr;
+  Private->IntThunk->DispatchOpromTable.RuntimeSegment              = (UINT16)((OpromRevision < 3) ? 0xffff : (RuntimeAddress >> 4));
+  TempData = (UINTN) &Private->IntThunk->DispatchOpromTable;
+  Regs.X.ES = EFI_SEGMENT ((UINT32) TempData);
+  Regs.X.BX = EFI_OFFSET ((UINT32) TempData);
+  //
+  // Skip dispatching ROM for those PCI devices that can not be enabled by PciIo->Attributes
+  // Otherwise, it may cause the system to hang in some cases
+  //
+  if (!EFI_ERROR (PciEnableStatus)) {
+    DEBUG ((EFI_D_INFO, " Legacy16DispatchOprom - %02x/%02x/%02x\n", Bus, Device, Function));
+    Private->LegacyBios.FarCall86 (
+      &Private->LegacyBios,
+      Private->Legacy16CallSegment,
+      Private->Legacy16CallOffset,
+      &Regs,
+      NULL,
+      0
+      );
+  } else {
+    Regs.X.BX = 0;
+  }
+
+  if (Private->IntThunk->DispatchOpromTable.NumberBbsEntries != (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries) {
+    Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries  = (UINT8) Private->IntThunk->DispatchOpromTable.NumberBbsEntries;
+    mIgnoreBbsUpdateFlag = TRUE;
+  }
+  //
+  // Check if non-BBS compliant drives found
+  //
+  if (Regs.X.BX != 0) {
+    LocalDiskEnd  = (UINT8) (LocalDiskStart + Regs.H.BL);
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment        = (UINT8) Segment;
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus            = (UINT8) Bus;
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice         = (UINT8) Device;
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction       = (UINT8) Function;
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber  = Private->DiskEnd;
+    Private->DiskEnd = LocalDiskEnd;
+    Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd;
+    Private->LegacyEfiHddTableIndex += 1;
+  }
+  //
+  // Skip video mode set, if installing VGA
+  //
+  if (PciHandle != mVgaHandle) {
+    //
+    // Set mode settings since PrepareToScanRom may change mode
+    //
+    ACCESS_PAGE0_CODE ({
+      OldVideoMode = *(UINT8 *) ((UINTN) (0x400 + BDA_VIDEO_MODE));
+    });
+
+    if (VideoMode != OldVideoMode) {
+      //
+      // The active video mode is changed, restore it to original mode.
+      //
+      Regs.H.AH = 0x00;
+      Regs.H.AL = VideoMode;
+      Private->LegacyBios.Int86 (&Private->LegacyBios, 0x10, &Regs);
+    }
+  }
+  //
+  // Regs.X.AX from the adapter initializion is ignored since some adapters
+  // do not follow the standard of setting AX = 0 on success.
+  //
+  //
+  // The ROM could have updated it's size so we need to read again.
+  //
+  if (((EFI_LEGACY_EXPANSION_ROM_HEADER *) RuntimeAddress)->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
+    //
+    // Now we check the signature (0xaa55) to judge whether the run-time code is truly generated by INIT function.
+    // If signature is not valid, that means the INIT function didn't copy the run-time code to RuntimeAddress.
+    //
+    *RuntimeImageLength = 0;
+  } else {
+    *RuntimeImageLength = ((EFI_LEGACY_EXPANSION_ROM_HEADER *) RuntimeAddress)->Size512 * 512;
+  }
+
+  DEBUG ((EFI_D_INFO, " fsize = %x\n", *RuntimeImageLength));
+
+  //
+  // If OpROM runs in 2.0 mode
+  //
+  if (PhysicalAddress == 0) {
+    if (*RuntimeImageLength < ImageSize) {
+      //
+      // Make area from end of shadowed rom to end of original rom all ffs
+      //
+      gBS->SetMem ((VOID *) (InitAddress + *RuntimeImageLength), ImageSize - *RuntimeImageLength, 0xff);
+    }
+  }
+
+  ACCESS_PAGE0_CODE (
+    LocalDiskEnd = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80);
+  );
+
+  //
+  // Allow platform to perform any required actions after the
+  // OPROM has been initialized.
+  //
+  Status = Private->LegacyBiosPlatform->PlatformHooks (
+                                          Private->LegacyBiosPlatform,
+                                          EfiPlatformHookAfterRomInit,
+                                          0,
+                                          PciHandle,
+                                          &RuntimeAddress,
+                                          NULL,
+                                          NULL
+                                          );
+  if (PciHandle != NULL) {
+    //
+    // If no PCI Handle then no header or Bevs.
+    //
+    if ((*RuntimeImageLength != 0) && (!mIgnoreBbsUpdateFlag)) {
+      StartBbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries;
+      TempData      = RuntimeAddress;
+      UpdateBevBcvTable (
+        Private,
+        (EFI_LEGACY_EXPANSION_ROM_HEADER *) TempData,
+        PciIo
+        );
+      EndBbsIndex   = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries;
+      LocalDiskEnd  = (UINT8) (LocalDiskStart + (UINT8) (EndBbsIndex - StartBbsIndex));
+      if (LocalDiskEnd != LocalDiskStart) {
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment        = (UINT8) Segment;
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus            = (UINT8) Bus;
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice         = (UINT8) Device;
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction       = (UINT8) Function;
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber  = Private->DiskEnd;
+        Private->DiskEnd = LocalDiskEnd;
+        Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd;
+        Private->LegacyEfiHddTableIndex += 1;
+      }
+    }
+    //
+    // Mark PCI device as having a legacy BIOS ROM loaded.
+    //
+    RomShadow (
+      PciHandle,
+      (UINT32) RuntimeAddress,
+      (UINT32) *RuntimeImageLength,
+      LocalDiskStart,
+      LocalDiskEnd
+      );
+  }
+
+  //
+  // Stuff caller's OPTIONAL return parameters.
+  //
+  if (RomShadowAddress != NULL) {
+    *RomShadowAddress = (VOID *) RuntimeAddress;
+  }
+
+  if (DiskStart != NULL) {
+    *DiskStart = LocalDiskStart;
+  }
+
+  if (DiskEnd != NULL) {
+    *DiskEnd = LocalDiskEnd;
+  }
+
+  Private->OptionRom = (UINT32) (RuntimeAddress + *RuntimeImageLength);
+
+  Status = EFI_SUCCESS;
+
+Done:
+  if (PhysicalAddress != 0) {
+    //
+    // Free pages when OpROM is 3.0
+    //
+    gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize));
+  }
+
+  //
+  // Insure all shadowed  areas are locked
+  //
+  Private->LegacyRegion->Lock (
+                           Private->LegacyRegion,
+                           0xC0000,
+                           0x40000,
+                           &Granularity
+                           );
+
+  return Status;
+}
+
+/**
+  Let IOMMU grant DMA access for the PCI device.
+
+  @param  PciHandle             The EFI handle for the PCI device.
+  @param  HostAddress           The system memory address to map to the PCI controller.
+  @param  NumberOfBytes         The number of bytes to map.
+
+  @retval EFI_SUCCESS  The DMA access is granted.
+**/
+EFI_STATUS
+IoMmuGrantAccess (
+  IN  EFI_HANDLE                        PciHandle,
+  IN  EFI_PHYSICAL_ADDRESS              HostAddress,
+  IN  UINTN                             NumberOfBytes
+  )
+{
+  EFI_PHYSICAL_ADDRESS            DeviceAddress;
+  VOID                            *Mapping;
+  EFI_STATUS                      Status;
+
+  if (PciHandle == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_SUCCESS;
+  if (mIoMmu == NULL) {
+    gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmu);
+  }
+  if (mIoMmu != NULL) {
+    Status = mIoMmu->Map (
+                       mIoMmu,
+                       EdkiiIoMmuOperationBusMasterCommonBuffer,
+                       (VOID *)(UINTN)HostAddress,
+                       &NumberOfBytes,
+                       &DeviceAddress,
+                       &Mapping
+                       );
+    if (EFI_ERROR(Status)) {
+      DEBUG ((DEBUG_ERROR, "LegacyPci - IoMmuMap - %r\n", Status));
+    } else {
+      ASSERT (DeviceAddress == HostAddress);
+      Status = mIoMmu->SetAttribute (
+                         mIoMmu,
+                         PciHandle,
+                         Mapping,
+                         EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
+                         );
+      if (EFI_ERROR(Status)) {
+        DEBUG ((DEBUG_ERROR, "LegacyPci - IoMmuSetAttribute - %r\n", Status));
+      }
+    }
+  }
+  return Status;
+}
+
+/**
+  Load a legacy PC-AT OPROM on the PciHandle device. Return information
+  about how many disks were added by the OPROM and the shadow address and
+  size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C:
+
+  @param  This                   Protocol instance pointer.
+  @param  PciHandle              The PCI PC-AT OPROM from this devices ROM BAR will
+                                 be loaded. This value is NULL if RomImage is
+                                 non-NULL. This is the normal case.
+  @param  RomImage               A PCI PC-AT ROM image. This argument is non-NULL
+                                 if there is no hardware associated with the ROM
+                                 and thus no PciHandle, otherwise is must be NULL.
+                                 Example is PXE base code.
+  @param  Flags                  Indicates if ROM found and if PC-AT.
+  @param  DiskStart              Disk number of first device hooked by the ROM. If
+                                 DiskStart is the same as DiskEnd no disked were
+                                 hooked.
+  @param  DiskEnd                Disk number of the last device hooked by the ROM.
+  @param  RomShadowAddress       Shadow address of PC-AT ROM
+  @param  RomShadowedSize        Size of RomShadowAddress in bytes
+
+  @retval EFI_SUCCESS            Legacy ROM loaded for this device
+  @retval EFI_INVALID_PARAMETER  PciHandle not found
+  @retval EFI_UNSUPPORTED        There is no PCI ROM in the ROM BAR or no onboard
+                                 ROM
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBiosInstallPciRom (
+  IN EFI_LEGACY_BIOS_PROTOCOL           * This,
+  IN  EFI_HANDLE                        PciHandle,
+  IN  VOID                              **RomImage,
+  OUT UINTN                             *Flags,
+  OUT UINT8                             *DiskStart, OPTIONAL
+  OUT UINT8                             *DiskEnd, OPTIONAL
+  OUT VOID                              **RomShadowAddress, OPTIONAL
+  OUT UINT32                            *RomShadowedSize OPTIONAL
+  )
+{
+  EFI_STATUS                      Status;
+  LEGACY_BIOS_INSTANCE            *Private;
+  VOID                            *LocalRomImage;
+  UINTN                           ImageSize;
+  UINTN                           RuntimeImageLength;
+  EFI_PCI_IO_PROTOCOL             *PciIo;
+  PCI_TYPE01                      PciConfigHeader;
+  UINTN                           HandleCount;
+  EFI_HANDLE                      *HandleBuffer;
+  UINTN                           PciSegment;
+  UINTN                           PciBus;
+  UINTN                           PciDevice;
+  UINTN                           PciFunction;
+  UINTN                           LastBus;
+  UINTN                           Index;
+  UINT8                           OpromRevision;
+  UINT32                          Granularity;
+  PCI_3_0_DATA_STRUCTURE          *Pcir;
+
+  OpromRevision = 0;
+
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+  if (Private->Legacy16Table->LastPciBus == 0) {
+    //
+    // Get last bus number if not already found
+    //
+    Status = gBS->LocateHandleBuffer (
+                    ByProtocol,
+                    &gEfiPciIoProtocolGuid,
+                    NULL,
+                    &HandleCount,
+                    &HandleBuffer
+                    );
+
+    LastBus = 0;
+    for (Index = 0; Index < HandleCount; Index++) {
+      Status = gBS->HandleProtocol (
+                      HandleBuffer[Index],
+                      &gEfiPciIoProtocolGuid,
+                      (VOID **) &PciIo
+                      );
+      if (EFI_ERROR (Status)) {
+        continue;
+      }
+
+      Status = PciIo->GetLocation (
+                        PciIo,
+                        &PciSegment,
+                        &PciBus,
+                        &PciDevice,
+                        &PciFunction
+                        );
+      if (PciBus > LastBus) {
+        LastBus = PciBus;
+      }
+    }
+
+    Private->LegacyRegion->UnLock (
+                             Private->LegacyRegion,
+                             0xE0000,
+                             0x20000,
+                             &Granularity
+                             );
+    Private->Legacy16Table->LastPciBus = (UINT8) LastBus;
+    Private->LegacyRegion->Lock (
+                             Private->LegacyRegion,
+                             0xE0000,
+                             0x20000,
+                             &Granularity
+                             );
+  }
+
+  *Flags = 0;
+  if ((PciHandle != NULL) && (RomImage == NULL)) {
+    //
+    // If PciHandle has OpRom to Execute
+    // and OpRom are all associated with Hardware
+    //
+    Status = gBS->HandleProtocol (
+                    PciHandle,
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **) &PciIo
+                    );
+
+    if (!EFI_ERROR (Status)) {
+      PciIo->Pci.Read (
+                   PciIo,
+                   EfiPciIoWidthUint32,
+                   0,
+                   sizeof (PciConfigHeader) / sizeof (UINT32),
+                   &PciConfigHeader
+                   );
+
+      //
+      // if video installed & OPROM is video return
+      //
+      if (
+          (
+           ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_OLD) &&
+            (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_OLD_VGA))
+           ||
+           ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_DISPLAY) &&
+            (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_DISPLAY_VGA))
+          )
+          &&
+          (!Private->VgaInstalled)
+         ) {
+        mVgaInstallationInProgress = TRUE;
+
+        //
+        //      return EFI_UNSUPPORTED;
+        //
+      }
+    }
+    //
+    // To run any legacy image, the VGA needs to be installed first.
+    // if installing the video, then don't need the thunk as already installed.
+    //
+    Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                            Private->LegacyBiosPlatform,
+                                            EfiGetPlatformVgaHandle,
+                                            0,
+                                            &HandleBuffer,
+                                            &HandleCount,
+                                            NULL
+                                            );
+
+    if (!EFI_ERROR (Status)) {
+      mVgaHandle = HandleBuffer[0];
+      if ((!Private->VgaInstalled) && (PciHandle != mVgaHandle)) {
+        //
+        // A return status of EFI_NOT_FOUND is considered valid (No EFI
+        // driver is controlling video.
+        //
+        mVgaInstallationInProgress  = TRUE;
+        Status                      = LegacyBiosInstallVgaRom (Private);
+        if (EFI_ERROR (Status)) {
+          if (Status != EFI_NOT_FOUND) {
+            mVgaInstallationInProgress = FALSE;
+            return Status;
+          }
+        } else {
+          mVgaInstallationInProgress = FALSE;
+        }
+      }
+    }
+    //
+    // See if the option ROM for PciHandle has already been executed
+    //
+    Status = IsLegacyRom (PciHandle);
+
+    if (!EFI_ERROR (Status)) {
+      mVgaInstallationInProgress = FALSE;
+      GetShadowedRomParameters (
+        PciHandle,
+        DiskStart,
+        DiskEnd,
+        RomShadowAddress,
+        (UINTN *) RomShadowedSize
+        );
+      return EFI_SUCCESS;
+    }
+
+    Status = LegacyBiosCheckPciRomEx (
+               &Private->LegacyBios,
+               PciHandle,
+               &LocalRomImage,
+               &ImageSize,
+               &RuntimeImageLength,
+               Flags,
+               &OpromRevision,
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      //
+      // There is no PCI ROM in the ROM BAR or no onboard ROM
+      //
+      mVgaInstallationInProgress = FALSE;
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    if ((RomImage == NULL) || (*RomImage == NULL)) {
+      //
+      // If PciHandle is NULL, and no OpRom is to be associated
+      //
+      mVgaInstallationInProgress = FALSE;
+      return EFI_UNSUPPORTED;
+    }
+
+    Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                            Private->LegacyBiosPlatform,
+                                            EfiGetPlatformVgaHandle,
+                                            0,
+                                            &HandleBuffer,
+                                            &HandleCount,
+                                            NULL
+                                            );
+    if ((!EFI_ERROR (Status)) && (!Private->VgaInstalled)) {
+      //
+      // A return status of EFI_NOT_FOUND is considered valid (No EFI
+      // driver is controlling video.
+      //
+      mVgaInstallationInProgress  = TRUE;
+      Status                      = LegacyBiosInstallVgaRom (Private);
+      if (EFI_ERROR (Status)) {
+        if (Status != EFI_NOT_FOUND) {
+          mVgaInstallationInProgress = FALSE;
+          return Status;
+        }
+      } else {
+        mVgaInstallationInProgress = FALSE;
+      }
+    }
+
+    LocalRomImage = *RomImage;
+    if (((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE ||
+        ((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset == 0 ||
+        (((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset & 3 ) != 0) {
+      mVgaInstallationInProgress = FALSE;
+      return EFI_UNSUPPORTED;
+    }
+
+    Pcir = (PCI_3_0_DATA_STRUCTURE *)
+           ((UINT8 *) LocalRomImage + ((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset);
+
+    if ((Pcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) || (Pcir->CodeType != PCI_CODE_TYPE_PCAT_IMAGE)) {
+      mVgaInstallationInProgress = FALSE;
+      return EFI_UNSUPPORTED;
+    }
+
+    ImageSize = Pcir->ImageLength * 512;
+    if (Pcir->Length >= 0x1C) {
+      OpromRevision = Pcir->Revision;
+    } else {
+      OpromRevision = 0;
+    }
+    if (Pcir->Revision < 3) {
+      RuntimeImageLength = 0;
+    } else {
+      RuntimeImageLength = Pcir->MaxRuntimeImageLength * 512;
+    }
+  }
+
+  //
+  // Grant access for below 1M
+  // BDA/EBDA/LowPMM and scratch memory for OPROM.
+  //
+  IoMmuGrantAccess (PciHandle, 0, SIZE_1MB);
+  //
+  // Grant access for HiPmm
+  //
+  IoMmuGrantAccess (
+    PciHandle,
+    Private->IntThunk->EfiToLegacy16InitTable.HiPmmMemory,
+    Private->IntThunk->EfiToLegacy16InitTable.HiPmmMemorySizeInBytes
+    );
+
+  //
+  // Shadow and initialize the OpROM.
+  //
+  ASSERT (Private->TraceIndex < 0x200);
+  Private->Trace[Private->TraceIndex] = LEGACY_PCI_TRACE_000;
+  Private->TraceIndex ++;
+  Private->TraceIndex = (UINT16) (Private->TraceIndex % 0x200);
+  Status = LegacyBiosInstallRom (
+             This,
+             Private,
+             PciHandle,
+             OpromRevision,
+             LocalRomImage,
+             ImageSize,
+             &RuntimeImageLength,
+             DiskStart,
+             DiskEnd,
+             RomShadowAddress
+             );
+  if (RomShadowedSize != NULL) {
+    *RomShadowedSize = (UINT32) RuntimeImageLength;
+  }
+
+  mVgaInstallationInProgress = FALSE;
+  return Status;
+}
+
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacySio.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacySio.c
new file mode 100644
index 0000000000..17720a74f7
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacySio.c
@@ -0,0 +1,477 @@
+/** @file
+  Collect Sio information from Native EFI Drivers.
+  Sio is floppy, parallel, serial, ... hardware
+
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+/**
+  Collect EFI Info about legacy devices through Super IO interface.
+
+  @param  SioPtr       Pointer to SIO data.
+
+  @retval EFI_SUCCESS   When SIO data is got successfully.
+  @retval EFI_NOT_FOUND When ISA IO interface is absent.
+
+**/
+EFI_STATUS
+LegacyBiosBuildSioDataFromSio (
+  IN DEVICE_PRODUCER_DATA_HEADER             *SioPtr
+  )
+{
+  EFI_STATUS                                 Status;
+  DEVICE_PRODUCER_SERIAL                     *SioSerial;
+  DEVICE_PRODUCER_PARALLEL                   *SioParallel;
+  DEVICE_PRODUCER_FLOPPY                     *SioFloppy;
+  UINTN                                      HandleCount;
+  EFI_HANDLE                                 *HandleBuffer;
+  UINTN                                      Index;
+  UINTN                                      ChildIndex;
+  EFI_SIO_PROTOCOL                           *Sio;
+  ACPI_RESOURCE_HEADER_PTR                   Resources;
+  EFI_ACPI_IO_PORT_DESCRIPTOR                *IoResource;
+  EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIoResource;
+  EFI_ACPI_DMA_DESCRIPTOR                    *DmaResource;
+  EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR             *IrqResource;
+  UINT16                                     Address;
+  UINT8                                      Dma;
+  UINT8                                      Irq;
+  UINTN                                      EntryCount;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY        *OpenInfoBuffer;
+  EFI_BLOCK_IO_PROTOCOL                      *BlockIo;
+  EFI_SERIAL_IO_PROTOCOL                     *SerialIo;
+  EFI_DEVICE_PATH_PROTOCOL                   *DevicePath;
+  ACPI_HID_DEVICE_PATH                       *Acpi;
+
+  //
+  // Get the list of ISA controllers in the system
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiSioProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+  //
+  // Collect legacy information from each of the ISA controllers in the system
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSioProtocolGuid, (VOID **) &Sio);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Address = MAX_UINT16;
+    Dma     = MAX_UINT8;
+    Irq     = MAX_UINT8;
+    Status = Sio->GetResources (Sio, &Resources);
+    if (!EFI_ERROR (Status)) {
+      //
+      // Get the base address information from ACPI resource descriptor.
+      //
+      while (Resources.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) {
+        switch (Resources.SmallHeader->Byte) {
+        case ACPI_IO_PORT_DESCRIPTOR:
+          IoResource = (EFI_ACPI_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
+          Address = IoResource->BaseAddressMin;
+          break;
+
+        case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR:
+          FixedIoResource = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) Resources.SmallHeader;
+          Address = FixedIoResource->BaseAddress;
+          break;
+
+        case ACPI_DMA_DESCRIPTOR:
+          DmaResource = (EFI_ACPI_DMA_DESCRIPTOR *) Resources.SmallHeader;
+          Dma = (UINT8) LowBitSet32 (DmaResource->ChannelMask);
+          break;
+
+        case ACPI_IRQ_DESCRIPTOR:
+        case ACPI_IRQ_NOFLAG_DESCRIPTOR:
+          IrqResource = (EFI_ACPI_IRQ_NOFLAG_DESCRIPTOR *) Resources.SmallHeader;
+          Irq = (UINT8) LowBitSet32 (IrqResource->Mask);
+          break;
+
+        default:
+          break;
+        }
+
+        if (Resources.SmallHeader->Bits.Type == 0) {
+          Resources.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) Resources.SmallHeader
+                                                                  + Resources.SmallHeader->Bits.Length
+                                                                  + sizeof (*Resources.SmallHeader));
+        } else {
+          Resources.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) Resources.LargeHeader
+                                                                  + Resources.LargeHeader->Length
+                                                                  + sizeof (*Resources.LargeHeader));
+        }
+      }
+    }
+
+    DEBUG ((EFI_D_INFO, "LegacySio: Address/Dma/Irq = %x/%d/%d\n", Address, Dma, Irq));
+
+    DevicePath = DevicePathFromHandle (HandleBuffer[Index]);
+    if (DevicePath == NULL) {
+      continue;
+    }
+
+    Acpi = NULL;
+    while (!IsDevicePathEnd (DevicePath)) {
+      Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath;
+      DevicePath = NextDevicePathNode (DevicePath);
+    }
+
+    if ((Acpi == NULL) || (DevicePathType (Acpi) != ACPI_DEVICE_PATH) ||
+        ((DevicePathSubType (Acpi) != ACPI_DP) && (DevicePathSubType (Acpi) != ACPI_EXTENDED_DP))
+        ) {
+      continue;
+    }
+
+    //
+    // See if this is an ISA serial port
+    //
+    // Ignore DMA resource since it is always returned NULL
+    //
+    if (Acpi->HID == EISA_PNP_ID (0x500) || Acpi->HID == EISA_PNP_ID (0x501)) {
+
+      if (Acpi->UID < 4 && Address != MAX_UINT16 && Irq != MAX_UINT8) {
+        //
+        // Get the handle of the child device that has opened the Super I/O Protocol
+        //
+        Status = gBS->OpenProtocolInformation (
+                        HandleBuffer[Index],
+                        &gEfiSioProtocolGuid,
+                        &OpenInfoBuffer,
+                        &EntryCount
+                        );
+        if (EFI_ERROR (Status)) {
+          continue;
+        }
+        for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) {
+          if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+            Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo);
+            if (!EFI_ERROR (Status)) {
+              SioSerial           = &SioPtr->Serial[Acpi->UID];
+              SioSerial->Address  = Address;
+              SioSerial->Irq      = Irq;
+              SioSerial->Mode     = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF;
+              break;
+            }
+          }
+        }
+
+        FreePool (OpenInfoBuffer);
+      }
+    }
+    //
+    // See if this is an ISA parallel port
+    //
+    // Ignore DMA resource since it is always returned NULL, port
+    // only used in output mode.
+    //
+    if (Acpi->HID == EISA_PNP_ID (0x400) || Acpi->HID == EISA_PNP_ID (0x401)) {
+      if (Acpi->UID < 3 && Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) {
+        SioParallel           = &SioPtr->Parallel[Acpi->UID];
+        SioParallel->Address  = Address;
+        SioParallel->Irq      = Irq;
+        SioParallel->Dma      = Dma;
+        SioParallel->Mode     = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY;
+      }
+    }
+    //
+    // See if this is an ISA floppy controller
+    //
+    if (Acpi->HID == EISA_PNP_ID (0x604)) {
+      if (Address != MAX_UINT16 && Irq != MAX_UINT8 && Dma != MAX_UINT8) {
+        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+        if (!EFI_ERROR (Status)) {
+          SioFloppy           = &SioPtr->Floppy;
+          SioFloppy->Address  = Address;
+          SioFloppy->Irq      = Irq;
+          SioFloppy->Dma      = Dma;
+          SioFloppy->NumberOfFloppy++;
+        }
+      }
+    }
+    //
+    // See if this is a mouse
+    // Always set mouse found so USB hot plug will work
+    //
+    // Ignore lower byte of HID. Pnp0fxx is any type of mouse.
+    //
+    //    Hid = ResourceList->Device.HID & 0xff00ffff;
+    //    PnpId = EISA_PNP_ID(0x0f00);
+    //    if (Hid == PnpId) {
+    //      if (ResourceList->Device.UID == 1) {
+    //        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer);
+    //      if (!EFI_ERROR (Status)) {
+    //
+    SioPtr->MousePresent = 0x01;
+    //
+    //        }
+    //      }
+    //    }
+    //
+  }
+
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+
+}
+
+/**
+  Collect EFI Info about legacy devices through ISA IO interface.
+
+  @param  SioPtr       Pointer to SIO data.
+
+  @retval EFI_SUCCESS   When SIO data is got successfully.
+  @retval EFI_NOT_FOUND When ISA IO interface is absent.
+
+**/
+EFI_STATUS
+LegacyBiosBuildSioDataFromIsaIo (
+  IN DEVICE_PRODUCER_DATA_HEADER      *SioPtr
+  )
+{
+  EFI_STATUS                          Status;
+  DEVICE_PRODUCER_SERIAL              *SioSerial;
+  DEVICE_PRODUCER_PARALLEL            *SioParallel;
+  DEVICE_PRODUCER_FLOPPY              *SioFloppy;
+  UINTN                               HandleCount;
+  EFI_HANDLE                          *HandleBuffer;
+  UINTN                               Index;
+  UINTN                               ResourceIndex;
+  UINTN                               ChildIndex;
+  EFI_ISA_IO_PROTOCOL                 *IsaIo;
+  EFI_ISA_ACPI_RESOURCE_LIST          *ResourceList;
+  EFI_ISA_ACPI_RESOURCE               *IoResource;
+  EFI_ISA_ACPI_RESOURCE               *DmaResource;
+  EFI_ISA_ACPI_RESOURCE               *InterruptResource;
+  UINTN                               EntryCount;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
+  EFI_BLOCK_IO_PROTOCOL               *BlockIo;
+  EFI_SERIAL_IO_PROTOCOL              *SerialIo;
+
+  //
+  // Get the list of ISA controllers in the system
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiIsaIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_FOUND;
+  }
+  //
+  // Collect legacy information from each of the ISA controllers in the system
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+
+    Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    ResourceList = IsaIo->ResourceList;
+
+    if (ResourceList == NULL) {
+      continue;
+    }
+    //
+    // Collect the resource types neededto fill in the SIO data structure
+    //
+    IoResource        = NULL;
+    DmaResource       = NULL;
+    InterruptResource = NULL;
+    for (ResourceIndex = 0;
+         ResourceList->ResourceItem[ResourceIndex].Type != EfiIsaAcpiResourceEndOfList;
+         ResourceIndex++
+        ) {
+      switch (ResourceList->ResourceItem[ResourceIndex].Type) {
+      case EfiIsaAcpiResourceIo:
+        IoResource = &ResourceList->ResourceItem[ResourceIndex];
+        break;
+
+      case EfiIsaAcpiResourceMemory:
+        break;
+
+      case EfiIsaAcpiResourceDma:
+        DmaResource = &ResourceList->ResourceItem[ResourceIndex];
+        break;
+
+      case EfiIsaAcpiResourceInterrupt:
+        InterruptResource = &ResourceList->ResourceItem[ResourceIndex];
+        break;
+
+      default:
+        break;
+      }
+    }
+    //
+    // See if this is an ISA serial port
+    //
+    // Ignore DMA resource since it is always returned NULL
+    //
+    if (ResourceList->Device.HID == EISA_PNP_ID (0x500) || ResourceList->Device.HID == EISA_PNP_ID (0x501)) {
+
+      if (ResourceList->Device.UID <= 3 &&
+          IoResource != NULL &&
+          InterruptResource != NULL
+          ) {
+        //
+        // Get the handle of the child device that has opened the ISA I/O Protocol
+        //
+        Status = gBS->OpenProtocolInformation (
+                        HandleBuffer[Index],
+                        &gEfiIsaIoProtocolGuid,
+                        &OpenInfoBuffer,
+                        &EntryCount
+                        );
+        if (EFI_ERROR (Status)) {
+          continue;
+        }
+        //
+        // We want resource for legacy even if no 32-bit driver installed
+        //
+        for (ChildIndex = 0; ChildIndex < EntryCount; ChildIndex++) {
+          if ((OpenInfoBuffer[ChildIndex].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+            Status = gBS->HandleProtocol (OpenInfoBuffer[ChildIndex].ControllerHandle, &gEfiSerialIoProtocolGuid, (VOID **) &SerialIo);
+            if (!EFI_ERROR (Status)) {
+              SioSerial           = &SioPtr->Serial[ResourceList->Device.UID];
+              SioSerial->Address  = (UINT16) IoResource->StartRange;
+              SioSerial->Irq      = (UINT8) InterruptResource->StartRange;
+              SioSerial->Mode     = DEVICE_SERIAL_MODE_NORMAL | DEVICE_SERIAL_MODE_DUPLEX_HALF;
+              break;
+            }
+          }
+        }
+
+        FreePool (OpenInfoBuffer);
+      }
+    }
+    //
+    // See if this is an ISA parallel port
+    //
+    // Ignore DMA resource since it is always returned NULL, port
+    // only used in output mode.
+    //
+    if (ResourceList->Device.HID == EISA_PNP_ID (0x400) || ResourceList->Device.HID == EISA_PNP_ID (0x401)) {
+      if (ResourceList->Device.UID <= 2 &&
+          IoResource != NULL &&
+          InterruptResource != NULL &&
+          DmaResource != NULL
+          ) {
+        SioParallel           = &SioPtr->Parallel[ResourceList->Device.UID];
+        SioParallel->Address  = (UINT16) IoResource->StartRange;
+        SioParallel->Irq      = (UINT8) InterruptResource->StartRange;
+        SioParallel->Dma      = (UINT8) DmaResource->StartRange;
+        SioParallel->Mode     = DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY;
+      }
+    }
+    //
+    // See if this is an ISA floppy controller
+    //
+    if (ResourceList->Device.HID == EISA_PNP_ID (0x604)) {
+      if (IoResource != NULL && InterruptResource != NULL && DmaResource != NULL) {
+        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+        if (!EFI_ERROR (Status)) {
+          SioFloppy           = &SioPtr->Floppy;
+          SioFloppy->Address  = (UINT16) IoResource->StartRange;
+          SioFloppy->Irq      = (UINT8) InterruptResource->StartRange;
+          SioFloppy->Dma      = (UINT8) DmaResource->StartRange;
+          SioFloppy->NumberOfFloppy++;
+        }
+      }
+    }
+    //
+    // See if this is a mouse
+    // Always set mouse found so USB hot plug will work
+    //
+    // Ignore lower byte of HID. Pnp0fxx is any type of mouse.
+    //
+    //    Hid = ResourceList->Device.HID & 0xff00ffff;
+    //    PnpId = EISA_PNP_ID(0x0f00);
+    //    if (Hid == PnpId) {
+    //      if (ResourceList->Device.UID == 1) {
+    //        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimplePointerProtocolGuid, &SimplePointer);
+    //      if (!EFI_ERROR (Status)) {
+    //
+    SioPtr->MousePresent = 0x01;
+    //
+    //        }
+    //      }
+    //    }
+    //
+  }
+
+  FreePool (HandleBuffer);
+  return EFI_SUCCESS;
+}
+
+/**
+  Collect EFI Info about legacy devices.
+
+  @param  Private      Legacy BIOS Instance data
+
+  @retval EFI_SUCCESS  It should always work.
+
+**/
+EFI_STATUS
+LegacyBiosBuildSioData (
+  IN  LEGACY_BIOS_INSTANCE      *Private
+  )
+{
+  EFI_STATUS                          Status;
+  DEVICE_PRODUCER_DATA_HEADER         *SioPtr;
+  EFI_HANDLE                          IsaBusController;
+  UINTN                               HandleCount;
+  EFI_HANDLE                          *HandleBuffer;
+
+  //
+  // Get the pointer to the SIO data structure
+  //
+  SioPtr = &Private->IntThunk->EfiToLegacy16BootTable.SioData;
+
+  //
+  // Zero the data in the SIO data structure
+  //
+  gBS->SetMem (SioPtr, sizeof (DEVICE_PRODUCER_DATA_HEADER), 0);
+
+  //
+  // Find the ISA Bus Controller used for legacy
+  //
+  Status = Private->LegacyBiosPlatform->GetPlatformHandle (
+                                          Private->LegacyBiosPlatform,
+                                          EfiGetPlatformIsaBusHandle,
+                                          0,
+                                          &HandleBuffer,
+                                          &HandleCount,
+                                          NULL
+                                          );
+  IsaBusController = HandleBuffer[0];
+  if (!EFI_ERROR (Status)) {
+    //
+    // Force ISA Bus Controller to produce all ISA devices
+    //
+    gBS->ConnectController (IsaBusController, NULL, NULL, TRUE);
+  }
+
+  Status = LegacyBiosBuildSioDataFromIsaIo (SioPtr);
+  if (EFI_ERROR (Status)) {
+    LegacyBiosBuildSioDataFromSio (SioPtr);
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/Thunk.c b/OvmfPkg/Csm/LegacyBiosDxe/Thunk.c
new file mode 100644
index 0000000000..a4985ede77
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/Thunk.c
@@ -0,0 +1,419 @@
+/** @file
+  Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+
+THUNK_CONTEXT      mThunkContext;
+
+/**
+  Sets the counter value for Timer #0 in a legacy 8254 timer.
+
+  @param  Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.
+
+**/
+VOID
+SetPitCount (
+  IN UINT16  Count
+  )
+{
+  IoWrite8 (TIMER_CONTROL_PORT, TIMER0_CONTROL_WORD);
+  IoWrite8 (TIMER0_COUNT_PORT, (UINT8) (Count & 0xFF));
+  IoWrite8 (TIMER0_COUNT_PORT, (UINT8) ((Count>>8) & 0xFF));
+}
+
+/**
+  Thunk to 16-bit real mode and execute a software interrupt with a vector
+  of BiosInt. Regs will contain the 16-bit register context on entry and
+  exit.
+
+  @param  This    Protocol instance pointer.
+  @param  BiosInt Processor interrupt vector to invoke
+  @param  Regs    Register contexted passed into (and returned) from thunk to
+                  16-bit mode
+
+  @retval FALSE   Thunk completed, and there were no BIOS errors in the target code.
+                  See Regs for status.
+  @retval TRUE    There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+LegacyBiosInt86 (
+  IN  EFI_LEGACY_BIOS_PROTOCOL      *This,
+  IN  UINT8                         BiosInt,
+  IN  EFI_IA32_REGISTER_SET         *Regs
+  )
+{
+  UINT16                Segment;
+  UINT16                Offset;
+
+  Regs->X.Flags.Reserved1 = 1;
+  Regs->X.Flags.Reserved2 = 0;
+  Regs->X.Flags.Reserved3 = 0;
+  Regs->X.Flags.Reserved4 = 0;
+  Regs->X.Flags.IOPL      = 3;
+  Regs->X.Flags.NT        = 0;
+  Regs->X.Flags.IF        = 0;
+  Regs->X.Flags.TF        = 0;
+  Regs->X.Flags.CF        = 0;
+  //
+  // The base address of legacy interrupt vector table is 0.
+  // We use this base address to get the legacy interrupt handler.
+  //
+  ACCESS_PAGE0_CODE (
+    Segment               = (UINT16)(((UINT32 *)0)[BiosInt] >> 16);
+    Offset                = (UINT16)((UINT32 *)0)[BiosInt];
+  );
+
+  return InternalLegacyBiosFarCall (
+           This,
+           Segment,
+           Offset,
+           Regs,
+           &Regs->X.Flags,
+           sizeof (Regs->X.Flags)
+           );
+}
+
+/**
+  Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
+  16-bit register context on entry and exit. Arguments can be passed on
+  the Stack argument
+
+  @param  This                   Protocol instance pointer.
+  @param  Segment                Segemnt of 16-bit mode call
+  @param  Offset                 Offset of 16-bit mdoe call
+  @param  Regs                   Register contexted passed into (and returned) from
+                                 thunk to  16-bit mode
+  @param  Stack                  Caller allocated stack used to pass arguments
+  @param  StackSize              Size of Stack in bytes
+
+  @retval FALSE                  Thunk completed, and there were no BIOS errors in
+                                 the target code. See Regs for status.
+  @retval TRUE                   There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+LegacyBiosFarCall86 (
+  IN  EFI_LEGACY_BIOS_PROTOCOL        *This,
+  IN  UINT16                          Segment,
+  IN  UINT16                          Offset,
+  IN  EFI_IA32_REGISTER_SET           *Regs,
+  IN  VOID                            *Stack,
+  IN  UINTN                           StackSize
+  )
+{
+  Regs->X.Flags.Reserved1 = 1;
+  Regs->X.Flags.Reserved2 = 0;
+  Regs->X.Flags.Reserved3 = 0;
+  Regs->X.Flags.Reserved4 = 0;
+  Regs->X.Flags.IOPL      = 3;
+  Regs->X.Flags.NT        = 0;
+  Regs->X.Flags.IF        = 1;
+  Regs->X.Flags.TF        = 0;
+  Regs->X.Flags.CF        = 0;
+
+  return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize);
+}
+
+/**
+  Provide NULL interrupt handler which is used to check
+  if there is more than one HW interrupt registers with the CPU AP.
+
+  @param  InterruptType - The type of interrupt that occured
+  @param  SystemContext - A pointer to the system context when the interrupt occured
+
+**/
+VOID
+EFIAPI
+LegacyBiosNullInterruptHandler (
+  IN EFI_EXCEPTION_TYPE   InterruptType,
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+}
+
+/**
+  Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
+  16-bit register context on entry and exit. Arguments can be passed on
+  the Stack argument
+
+  @param  This       Protocol instance pointer.
+  @param  Segment    Segemnt of 16-bit mode call
+  @param  Offset     Offset of 16-bit mdoe call
+  @param  Regs       Register contexted passed into (and returned) from thunk to
+                     16-bit mode
+  @param  Stack      Caller allocated stack used to pass arguments
+  @param  StackSize  Size of Stack in bytes
+
+  @retval FALSE      Thunk completed, and there were no BIOS errors in the target code.
+                     See Regs for status.
+  @retval TRUE       There was a BIOS erro in the target code.
+
+**/
+BOOLEAN
+EFIAPI
+InternalLegacyBiosFarCall (
+  IN  EFI_LEGACY_BIOS_PROTOCOL        *This,
+  IN  UINT16                          Segment,
+  IN  UINT16                          Offset,
+  IN  EFI_IA32_REGISTER_SET           *Regs,
+  IN  VOID                            *Stack,
+  IN  UINTN                           StackSize
+  )
+{
+  UINTN                 Status;
+  LEGACY_BIOS_INSTANCE  *Private;
+  UINT16                *Stack16;
+  EFI_TPL               OriginalTpl;
+  IA32_REGISTER_SET     ThunkRegSet;
+  BOOLEAN               InterruptState;
+  UINT64                TimerPeriod;
+
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
+
+  ZeroMem (&ThunkRegSet, sizeof (ThunkRegSet));
+  ThunkRegSet.X.DI   = Regs->X.DI;
+  ThunkRegSet.X.SI   = Regs->X.SI;
+  ThunkRegSet.X.BP   = Regs->X.BP;
+  ThunkRegSet.X.BX   = Regs->X.BX;
+  ThunkRegSet.X.DX   = Regs->X.DX;
+  //
+  // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is
+  // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.
+  //
+  ThunkRegSet.E.ECX   = Regs->E.ECX;
+  ThunkRegSet.X.AX   = Regs->X.AX;
+  ThunkRegSet.E.DS   = Regs->X.DS;
+  ThunkRegSet.E.ES   = Regs->X.ES;
+
+  CopyMem (&(ThunkRegSet.E.EFLAGS.UintN), &(Regs->X.Flags), sizeof (Regs->X.Flags));
+
+  //
+  // Clear the error flag; thunk code may set it. Stack16 should be the high address
+  // Make Statk16 address the low 16 bit must be not zero.
+  //
+  Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16));
+
+  //
+  // Save current rate of DXE Timer
+  //
+  Private->Timer->GetTimerPeriod (Private->Timer, &TimerPeriod);
+
+  //
+  // Disable DXE Timer while executing in real mode
+  //
+  Private->Timer->SetTimerPeriod (Private->Timer, 0);
+
+  //
+  // Save and disable interrupt of debug timer
+  //
+  InterruptState = SaveAndSetDebugTimerInterrupt (FALSE);
+
+  //
+  // The call to Legacy16 is a critical section to EFI
+  //
+  OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  //
+  // Check to see if there is more than one HW interrupt registers with the CPU AP.
+  // If there is, then ASSERT() since that is not compatible with the CSM because
+  // interupts other than the Timer interrupt that was disabled above can not be
+  // handled properly from real mode.
+  //
+  DEBUG_CODE (
+    UINTN  Vector;
+    UINTN  Count;
+
+    for (Vector = 0x20, Count = 0; Vector < 0x100; Vector++) {
+      Status = Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, LegacyBiosNullInterruptHandler);
+      if (Status == EFI_ALREADY_STARTED) {
+        Count++;
+      }
+      if (Status == EFI_SUCCESS) {
+        Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, NULL);
+      }
+    }
+    if (Count >= 2) {
+      DEBUG ((EFI_D_ERROR, "ERROR: More than one HW interrupt active with CSM enabled\n"));
+    }
+    ASSERT (Count < 2);
+  );
+
+  //
+  // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer
+  // period is less than the CSM required rate of 54.9254, then force the 8254
+  // PIT counter to 0, which is the CSM required rate of 54.9254 ms
+  //
+  if (Private->TimerUses8254 && TimerPeriod < 549254) {
+    SetPitCount (0);
+  }
+
+  if (Stack != NULL && StackSize != 0) {
+    //
+    // Copy Stack to low memory stack
+    //
+    Stack16 -= StackSize / sizeof (UINT16);
+    CopyMem (Stack16, Stack, StackSize);
+  }
+
+  ThunkRegSet.E.SS   = (UINT16) (((UINTN) Stack16 >> 16) << 12);
+  ThunkRegSet.E.ESP  = (UINT16) (UINTN) Stack16;
+  ThunkRegSet.E.CS   = Segment;
+  ThunkRegSet.E.Eip  = Offset;
+
+  mThunkContext.RealModeState      = &ThunkRegSet;
+
+  //
+  // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
+  //
+  Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  AsmThunk16 (&mThunkContext);
+
+  if (Stack != NULL && StackSize != 0) {
+    //
+    // Copy low memory stack to Stack
+    //
+    CopyMem (Stack, Stack16, StackSize);
+  }
+
+  //
+  // Restore protected mode interrupt state
+  //
+  Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  mThunkContext.RealModeState = NULL;
+
+  //
+  // Enable and restore rate of DXE Timer
+  //
+  Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod);
+
+  //
+  // End critical section
+  //
+  gBS->RestoreTPL (OriginalTpl);
+
+  //
+  // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.
+  // Get the current EBDA base address, and compared with pre-allocate minimum
+  // EBDA base address, if the current EBDA base address is smaller, it indicates
+  // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.
+  //
+  DEBUG_CODE (
+    {
+      UINTN                 EbdaBaseAddress;
+      UINTN                 ReservedEbdaBaseAddress;
+
+      ACCESS_PAGE0_CODE (
+        EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4;
+        ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP
+                                  - PcdGet32 (PcdEbdaReservedMemorySize);
+        ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress);
+      );
+    }
+  );
+
+  //
+  // Restore interrupt of debug timer
+  //
+  SaveAndSetDebugTimerInterrupt (InterruptState);
+
+  Regs->E.EDI      = ThunkRegSet.E.EDI;
+  Regs->E.ESI      = ThunkRegSet.E.ESI;
+  Regs->E.EBP      = ThunkRegSet.E.EBP;
+  Regs->E.EBX      = ThunkRegSet.E.EBX;
+  Regs->E.EDX      = ThunkRegSet.E.EDX;
+  Regs->E.ECX      = ThunkRegSet.E.ECX;
+  Regs->E.EAX      = ThunkRegSet.E.EAX;
+  Regs->X.SS       = ThunkRegSet.E.SS;
+  Regs->X.CS       = ThunkRegSet.E.CS;
+  Regs->X.DS       = ThunkRegSet.E.DS;
+  Regs->X.ES       = ThunkRegSet.E.ES;
+
+  CopyMem (&(Regs->X.Flags), &(ThunkRegSet.E.EFLAGS.UintN), sizeof (Regs->X.Flags));
+
+  return (BOOLEAN) (Regs->X.Flags.CF == 1);
+}
+
+/**
+  Allocate memory < 1 MB and copy the thunker code into low memory. Se up
+  all the descriptors.
+
+  @param  Private                Private context for Legacy BIOS
+
+  @retval EFI_SUCCESS            Should only pass.
+
+**/
+EFI_STATUS
+LegacyBiosInitializeThunk (
+  IN  LEGACY_BIOS_INSTANCE    *Private
+  )
+{
+  EFI_STATUS              Status;
+  EFI_PHYSICAL_ADDRESS    MemoryAddress;
+  UINT8                   TimerVector;
+
+  MemoryAddress   = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->IntThunk;
+
+  mThunkContext.RealModeBuffer     = (VOID *) (UINTN) (MemoryAddress + ((sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 1) * EFI_PAGE_SIZE);
+  mThunkContext.RealModeBufferSize = EFI_PAGE_SIZE;
+  mThunkContext.ThunkAttributes    = THUNK_ATTRIBUTE_BIG_REAL_MODE | THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15;
+
+  AsmPrepareThunk16 (&mThunkContext);
+
+  //
+  // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver
+  //
+  TimerVector = 0;
+  Status = Private->Legacy8259->GetVector (Private->Legacy8259, Efi8259Irq0, &TimerVector);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Check to see if the Timer AP has hooked the IRQ0 from the 8254 PIT
+  //
+  Status = Private->Cpu->RegisterInterruptHandler (
+                           Private->Cpu,
+                           TimerVector,
+                           LegacyBiosNullInterruptHandler
+                           );
+  if (Status == EFI_SUCCESS) {
+    //
+    // If the Timer AP has not enabled the 8254 timer IRQ, then force the 8254 PIT
+    // counter to 0, which is the CSM required rate of 54.9254 ms
+    //
+    Private->Cpu->RegisterInterruptHandler (
+                    Private->Cpu,
+                    TimerVector,
+                    NULL
+                    );
+    SetPitCount (0);
+
+    //
+    // Save status that the Timer AP is not using the 8254 PIT
+    //
+    Private->TimerUses8254 = FALSE;
+  } else if (Status == EFI_ALREADY_STARTED) {
+    //
+    // Save status that the Timer AP is using the 8254 PIT
+    //
+    Private->TimerUses8254 = TRUE;
+  } else {
+    //
+    // Unexpected status from CPU AP RegisterInterruptHandler()
+    //
+    ASSERT (FALSE);
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.c b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.c
new file mode 100644
index 0000000000..af7d80eb5f
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUi.c
@@ -0,0 +1,1505 @@
+/** @file
+  Legacy Boot Maintainence UI implementation.
+
+Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2018 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "LegacyBootMaintUi.h"
+
+LEGACY_BOOT_OPTION_CALLBACK_DATA  *mLegacyBootOptionPrivate = NULL;
+EFI_GUID  mLegacyBootOptionGuid     = LEGACY_BOOT_OPTION_FORMSET_GUID;
+CHAR16    mLegacyBootStorageName[]  = L"LegacyBootData";
+BBS_TYPE  mBbsType[] = {BBS_FLOPPY, BBS_HARDDISK, BBS_CDROM, BBS_EMBED_NETWORK, BBS_BEV_DEVICE, BBS_UNKNOWN};
+BOOLEAN   mFirstEnterLegacyForm = FALSE;
+
+
+///
+/// Legacy FD Info from LegacyBios.GetBbsInfo()
+///
+LEGACY_MENU_OPTION      LegacyFDMenu = {
+  LEGACY_MENU_OPTION_SIGNATURE,
+  {NULL},
+  0
+};
+
+///
+/// Legacy HD Info from LegacyBios.GetBbsInfo()
+///
+LEGACY_MENU_OPTION      LegacyHDMenu = {
+  LEGACY_MENU_OPTION_SIGNATURE,
+  {NULL},
+  0
+};
+
+///
+/// Legacy CD Info from LegacyBios.GetBbsInfo()
+///
+LEGACY_MENU_OPTION      LegacyCDMenu = {
+  LEGACY_MENU_OPTION_SIGNATURE,
+  {NULL},
+  0
+};
+
+///
+/// Legacy NET Info from LegacyBios.GetBbsInfo()
+///
+LEGACY_MENU_OPTION      LegacyNETMenu = {
+  LEGACY_MENU_OPTION_SIGNATURE,
+  {NULL},
+  0
+};
+
+///
+/// Legacy NET Info from LegacyBios.GetBbsInfo()
+///
+LEGACY_MENU_OPTION      LegacyBEVMenu = {
+  LEGACY_MENU_OPTION_SIGNATURE,
+  {NULL},
+  0
+};
+
+
+VOID                *mLegacyStartOpCodeHandle = NULL;
+VOID                *mLegacyEndOpCodeHandle = NULL;
+EFI_IFR_GUID_LABEL  *mLegacyStartLabel = NULL;
+EFI_IFR_GUID_LABEL  *mLegacyEndLabel = NULL;
+
+
+HII_VENDOR_DEVICE_PATH  mLegacyBootOptionHiiVendorDevicePath = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_VENDOR_DP,
+      {
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+      }
+    },
+    { 0x6bc75598, 0x89b4, 0x483d, { 0x91, 0x60, 0x7f, 0x46, 0x9a, 0x96, 0x35, 0x31 } }
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      (UINT8) (END_DEVICE_PATH_LENGTH),
+      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+    }
+  }
+};
+
+/**
+
+  Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo().
+
+**/
+VOID
+GetLegacyOptions (
+  VOID
+  );
+
+
+/**
+
+  Base on the L"LegacyDevOrder" variable to build the current order data.
+
+**/
+VOID
+GetLegacyOptionsOrder (
+  VOID
+  );
+
+/**
+  Re-order the Boot Option according to the DevOrder.
+
+  The routine re-orders the Boot Option in BootOption array according to
+  the order specified by DevOrder.
+
+  @param DevOrder           Pointer to buffer containing the BBS Index,
+                            high 8-bit value 0xFF indicating a disabled boot option
+  @param DevOrderCount      Count of the BBS Index
+  @param EnBootOption       Callee allocated buffer containing the enabled Boot Option Numbers
+  @param EnBootOptionCount  Count of the enabled Boot Option Numbers
+  @param DisBootOption      Callee allocated buffer containing the disabled Boot Option Numbers
+  @param DisBootOptionCount Count of the disabled Boot Option Numbers
+
+  @return EFI_SUCCESS       The function completed successfully.
+  @retval other             Contain some error, details see  the status return by gRT->SetVariable.
+**/
+EFI_STATUS
+OrderLegacyBootOption4SameType (
+  UINT16                   *DevOrder,
+  UINTN                    DevOrderCount,
+  UINT16                   **EnBootOption,
+  UINTN                    *EnBootOptionCount,
+  UINT16                   **DisBootOption,
+  UINTN                    *DisBootOptionCount
+  )
+{
+  EFI_STATUS               Status;
+  UINT16                   *NewBootOption;
+  UINT16                   *BootOrder;
+  UINTN                    BootOrderSize;
+  UINTN                    Index;
+  UINTN                    StartPosition;
+
+  EFI_BOOT_MANAGER_LOAD_OPTION    BootOption;
+
+  CHAR16                           OptionName[sizeof ("Boot####")];
+  UINT16                   *BbsIndexArray;
+  UINT16                   *DeviceTypeArray;
+
+  GetEfiGlobalVariable2 (L"BootOrder", (VOID **) &BootOrder, &BootOrderSize);
+  ASSERT (BootOrder != NULL);
+
+  BbsIndexArray       = AllocatePool (BootOrderSize);
+  DeviceTypeArray     = AllocatePool (BootOrderSize);
+  *EnBootOption       = AllocatePool (BootOrderSize);
+  *DisBootOption      = AllocatePool (BootOrderSize);
+  *DisBootOptionCount = 0;
+  *EnBootOptionCount  = 0;
+  Index               = 0;
+  Status              = EFI_SUCCESS;
+
+  ASSERT (BbsIndexArray != NULL);
+  ASSERT (DeviceTypeArray != NULL);
+  ASSERT (*EnBootOption != NULL);
+  ASSERT (*DisBootOption != NULL);
+
+  for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
+
+    UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]);
+    Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);
+    ASSERT_EFI_ERROR (Status);
+
+    if ((DevicePathType (BootOption.FilePath) == BBS_DEVICE_PATH) &&
+        (DevicePathSubType (BootOption.FilePath) == BBS_BBS_DP)) {
+      //
+      // Legacy Boot Option
+      //
+      ASSERT (BootOption.OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA));
+
+      DeviceTypeArray[Index] = ((BBS_BBS_DEVICE_PATH *) BootOption.FilePath)->DeviceType;
+      BbsIndexArray  [Index] = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption.OptionalData)->BbsIndex;
+    } else {
+      DeviceTypeArray[Index] = BBS_TYPE_UNKNOWN;
+      BbsIndexArray  [Index] = 0xFFFF;
+    }
+    EfiBootManagerFreeLoadOption (&BootOption);
+  }
+
+  //
+  // Record the corresponding Boot Option Numbers according to the DevOrder
+  // Record the EnBootOption and DisBootOption according to the DevOrder
+  //
+  StartPosition = BootOrderSize / sizeof (UINT16);
+  NewBootOption = AllocatePool (DevOrderCount * sizeof (UINT16));
+  ASSERT (NewBootOption != NULL);
+  while (DevOrderCount-- != 0) {
+    for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
+      if (BbsIndexArray[Index] == (DevOrder[DevOrderCount] & 0xFF)) {
+        StartPosition = MIN (StartPosition, Index);
+        NewBootOption[DevOrderCount] = BootOrder[Index];
+
+        if ((DevOrder[DevOrderCount] & 0xFF00) == 0xFF00) {
+          (*DisBootOption)[*DisBootOptionCount] = BootOrder[Index];
+          (*DisBootOptionCount)++;
+        } else {
+          (*EnBootOption)[*EnBootOptionCount] = BootOrder[Index];
+          (*EnBootOptionCount)++;
+        }
+        break;
+      }
+    }
+  }
+
+  //
+  // Overwrite the old BootOption
+  //
+  CopyMem (&BootOrder[StartPosition], NewBootOption, (*DisBootOptionCount + *EnBootOptionCount) * sizeof (UINT16));
+  Status = gRT->SetVariable (
+                  L"BootOrder",
+                  &gEfiGlobalVariableGuid,
+                  VAR_FLAG,
+                  BootOrderSize,
+                  BootOrder
+                  );
+
+  FreePool (NewBootOption);
+  FreePool (DeviceTypeArray);
+  FreePool (BbsIndexArray);
+
+  return Status;
+}
+
+/**
+  Update the legacy BBS boot option. L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable
+  is udpated with the new Legacy Boot order. The EFI Variable of "Boot####" and gEfiGlobalVariableGuid
+  is also updated.
+
+  @param NVMapData   The data for egacy BBS boot.
+
+  @return EFI_SUCCESS           The function completed successfully.
+  @retval EFI_NOT_FOUND         If L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable can not be found.
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate memory resource
+  @retval other                 Contain some error, details see  the status return by gRT->SetVariable.
+**/
+EFI_STATUS
+UpdateBBSOption (
+  IN LEGACY_BOOT_NV_DATA            *NVMapData
+  )
+{
+  UINTN                       Index;
+  UINTN                       Index2;
+  UINTN                       CurrentType;
+  VOID                        *BootOptionVar;
+  CHAR16                      VarName[100];
+  UINTN                       OptionSize;
+  EFI_STATUS                  Status;
+  UINT32                      *Attribute;
+  LEGACY_MENU_OPTION          *OptionMenu;
+  UINT16                      *LegacyDev;
+  UINT16                      *InitialLegacyDev;
+  UINT8                       *VarData;
+  UINTN                       VarSize;
+  LEGACY_DEV_ORDER_ENTRY      *DevOrder;
+  UINT8                       *OriginalPtr;
+  UINT8                       *DisMap;
+  UINTN                       Pos;
+  UINTN                       Bit;
+  UINT16                      *NewOrder;
+  UINT16                      Tmp;
+  UINT16                      *EnBootOption;
+  UINTN                       EnBootOptionCount;
+  UINT16                      *DisBootOption;
+  UINTN                       DisBootOptionCount;
+  UINTN                       BufferSize;
+
+
+  DisMap              = NULL;
+  NewOrder            = NULL;
+  CurrentType         = 0;
+  EnBootOption        = NULL;
+  DisBootOption       = NULL;
+
+
+  DisMap  = mLegacyBootOptionPrivate->MaintainMapData->DisableMap;
+  Status  = EFI_SUCCESS;
+
+  //
+  // Update the Variable "LegacyDevOrder"
+  //
+  GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize);
+  if (VarData == NULL) {
+    return EFI_NOT_FOUND;
+  }
+  OriginalPtr = VarData;
+
+  while (mBbsType[CurrentType] != BBS_UNKNOWN) {
+    switch (mBbsType[CurrentType]) {
+    case BBS_FLOPPY:
+      OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyFDMenu;
+      LegacyDev             = NVMapData->LegacyFD;
+      InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD;
+      BufferSize            = sizeof (NVMapData->LegacyFD);
+      break;
+
+    case BBS_HARDDISK:
+      OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyHDMenu;
+      LegacyDev             = NVMapData->LegacyHD;
+      InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD;
+
+      BufferSize            = sizeof (NVMapData->LegacyHD);
+      break;
+
+    case BBS_CDROM:
+      OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyCDMenu;
+      LegacyDev             = NVMapData->LegacyCD;
+      InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD;
+      BufferSize            = sizeof (NVMapData->LegacyCD);
+      break;
+
+    case BBS_EMBED_NETWORK:
+      OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyNETMenu;
+      LegacyDev             = NVMapData->LegacyNET;
+      InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET;
+      BufferSize            = sizeof (NVMapData->LegacyNET);
+      break;
+
+    default:
+      ASSERT (mBbsType[CurrentType] == BBS_BEV_DEVICE);
+      OptionMenu            = (LEGACY_MENU_OPTION *) &LegacyBEVMenu;
+      LegacyDev             = NVMapData->LegacyBEV;
+      InitialLegacyDev     = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV;
+      BufferSize            = sizeof (NVMapData->LegacyBEV);
+      break;
+    }
+
+    //
+    // Check whether has value changed.
+    //
+    if (CompareMem (LegacyDev, InitialLegacyDev, BufferSize) == 0) {
+      CurrentType++;
+      continue;
+    }
+
+    DevOrder    = (LEGACY_DEV_ORDER_ENTRY *) OriginalPtr;
+    while (VarData < OriginalPtr + VarSize) {
+      if (DevOrder->BbsType == mBbsType[CurrentType]) {
+        break;
+      }
+
+      VarData += sizeof (BBS_TYPE) + DevOrder->Length;
+      DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData;
+    }
+
+    if (VarData >= OriginalPtr + VarSize) {
+      FreePool (OriginalPtr);
+      return EFI_NOT_FOUND;
+    }
+
+    NewOrder = AllocateZeroPool (DevOrder->Length - sizeof (DevOrder->Length));
+    if (NewOrder == NULL) {
+      FreePool (OriginalPtr);
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
+      if (0xFF == LegacyDev[Index]) {
+        break;
+      }
+
+      NewOrder[Index] = LegacyDev[Index];
+    }
+
+    //
+    // Only the enable/disable state of each boot device with same device type can be changed,
+    // so we can count on the index information in DevOrder.
+    // DisMap bit array is the only reliable source to check a device's en/dis state,
+    // so we use DisMap to set en/dis state of each item in NewOrder array
+    //
+    for (Index2 = 0; Index2 < OptionMenu->MenuNumber; Index2++) {
+      Tmp = (UINT16) (DevOrder->Data[Index2] & 0xFF);
+      Pos = Tmp / 8;
+      Bit = 7 - (Tmp % 8);
+      if ((DisMap[Pos] & (1 << Bit)) != 0) {
+        NewOrder[Index] = (UINT16) (0xFF00 | Tmp);
+        Index++;
+      }
+    }
+
+    CopyMem (
+      DevOrder->Data,
+      NewOrder,
+      DevOrder->Length - sizeof (DevOrder->Length)
+      );
+    FreePool (NewOrder);
+
+    //
+    // Update BootOrder and Boot####.Attribute
+    //
+    // 1. Re-order the Option Number in BootOrder according to Legacy Dev Order
+    //
+    ASSERT (OptionMenu->MenuNumber == DevOrder->Length / sizeof (UINT16) - 1);
+
+    Status = OrderLegacyBootOption4SameType (
+      DevOrder->Data,
+      DevOrder->Length / sizeof (UINT16) - 1,
+      &EnBootOption,
+      &EnBootOptionCount,
+      &DisBootOption,
+      &DisBootOptionCount
+      );
+     if (EFI_ERROR(Status)) {
+       goto Fail;
+     }
+
+    //
+    // 2. Deactivate the DisBootOption and activate the EnBootOption
+    //
+    for (Index = 0; Index < DisBootOptionCount; Index++) {
+      UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", DisBootOption[Index]);
+      GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize);
+      if (BootOptionVar != NULL) {
+        Attribute   = (UINT32 *) BootOptionVar;
+        *Attribute &= ~LOAD_OPTION_ACTIVE;
+
+        Status = gRT->SetVariable (
+                        VarName,
+                        &gEfiGlobalVariableGuid,
+                        VAR_FLAG,
+                        OptionSize,
+                        BootOptionVar
+                        );
+
+        FreePool (BootOptionVar);
+      }
+    }
+
+    for (Index = 0; Index < EnBootOptionCount; Index++) {
+      UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", EnBootOption[Index]);
+      GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize);
+      if (BootOptionVar != NULL) {
+        Attribute   = (UINT32 *) BootOptionVar;
+        *Attribute |= LOAD_OPTION_ACTIVE;
+
+        Status = gRT->SetVariable (
+                        VarName,
+                        &gEfiGlobalVariableGuid,
+                        VAR_FLAG,
+                        OptionSize,
+                        BootOptionVar
+                        );
+
+        FreePool (BootOptionVar);
+      }
+    }
+
+
+    FreePool (EnBootOption);
+    FreePool (DisBootOption);
+
+    CurrentType++;
+  }
+
+  Status = gRT->SetVariable (
+                  VAR_LEGACY_DEV_ORDER,
+                  &gEfiLegacyDevOrderVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  VarSize,
+                  OriginalPtr
+                  );
+
+Fail:
+  if (EnBootOption != NULL) {
+    FreePool (EnBootOption);
+  }
+
+  if (DisBootOption != NULL) {
+    FreePool (DisBootOption);
+  }
+
+  FreePool (OriginalPtr);
+  return Status;
+}
+
+/**
+  This function allows a caller to extract the current configuration for one
+  or more named elements from the target driver.
+
+
+  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Request         A null-terminated Unicode string in <ConfigRequest> format.
+  @param Progress        On return, points to a character in the Request string.
+                         Points to the string's null terminator if request was successful.
+                         Points to the most recent '&' before the first failing name/value
+                         pair (or the beginning of the string if the failure is in the
+                         first name/value pair) if the request was not successful.
+  @param Results         A null-terminated Unicode string in <ConfigAltResp> format which
+                         has all values filled in for the names in the Request string.
+                         String to be allocated by the called function.
+
+  @retval  EFI_SUCCESS            The Results is filled with the requested values.
+  @retval  EFI_OUT_OF_RESOURCES   Not enough memory to store the results.
+  @retval  EFI_INVALID_PARAMETER  Request is illegal syntax, or unknown name.
+  @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionExtractConfig (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  CONST EFI_STRING                       Request,
+  OUT EFI_STRING                             *Progress,
+  OUT EFI_STRING                             *Results
+  )
+{
+  if (Progress == NULL || Results == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  *Progress = Request;
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This function processes the results of changes in configuration.
+
+
+  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Configuration   A null-terminated Unicode string in <ConfigResp> format.
+  @param Progress        A pointer to a string filled in with the offset of the most
+                         recent '&' before the first failing name/value pair (or the
+                         beginning of the string if the failure is in the first
+                         name/value pair) or the terminating NULL if all was successful.
+
+  @retval  EFI_SUCCESS            The Results is processed successfully.
+  @retval  EFI_INVALID_PARAMETER  Configuration is NULL.
+  @retval  EFI_NOT_FOUND          Routing data doesn't match any storage in this driver.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionRouteConfig (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  CONST EFI_STRING                       Configuration,
+  OUT       EFI_STRING                       *Progress
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;
+  LEGACY_BOOT_NV_DATA             *CurrentNVMapData;
+  UINTN                           BufferSize;
+
+
+  if (Configuration == NULL || Progress == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Progress = Configuration;
+
+  //
+  // Check routing data in <ConfigHdr>.
+  // Note: there is no name for Name/Value storage, only GUID will be checked
+  //
+  if (!HiiIsConfigHdrMatch (Configuration, &mLegacyBootOptionGuid, mLegacyBootStorageName)) {
+    return EFI_NOT_FOUND;
+  }
+
+  Status = gBS->LocateProtocol (
+                  &gEfiHiiConfigRoutingProtocolGuid,
+                  NULL,
+                  (VOID **) &ConfigRouting
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
+  //
+  CurrentNVMapData = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData;
+  Status = ConfigRouting->ConfigToBlock (
+                            ConfigRouting,
+                            Configuration,
+                            (UINT8 *) CurrentNVMapData,
+                            &BufferSize,
+                            Progress
+                            );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = UpdateBBSOption (CurrentNVMapData);
+
+  return Status;
+}
+
+/**
+  Refresh the global UpdateData structure.
+
+**/
+VOID
+RefreshLegacyUpdateData (
+  VOID
+  )
+{
+  //
+  // Free current updated date
+  //
+  if (mLegacyStartOpCodeHandle != NULL) {
+    HiiFreeOpCodeHandle (mLegacyStartOpCodeHandle);
+  }
+  if (mLegacyEndOpCodeHandle != NULL) {
+    HiiFreeOpCodeHandle (mLegacyEndOpCodeHandle);
+  }
+
+  //
+  // Create new OpCode Handle
+  //
+  mLegacyStartOpCodeHandle = HiiAllocateOpCodeHandle ();
+  mLegacyEndOpCodeHandle = HiiAllocateOpCodeHandle ();
+
+  //
+  // Create Hii Extend Label OpCode as the start opcode
+  //
+  mLegacyStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+                                         mLegacyStartOpCodeHandle,
+                                         &gEfiIfrTianoGuid,
+                                         NULL,
+                                         sizeof (EFI_IFR_GUID_LABEL)
+                                         );
+  mLegacyStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+
+  mLegacyStartLabel->Number = FORM_BOOT_LEGACY_DEVICE_ID;
+
+  //
+  // Create Hii Extend Label OpCode as the start opcode
+  //
+  mLegacyEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+                                         mLegacyEndOpCodeHandle,
+                                         &gEfiIfrTianoGuid,
+                                         NULL,
+                                         sizeof (EFI_IFR_GUID_LABEL)
+                                         );
+  mLegacyEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+
+  mLegacyEndLabel->Number = FORM_BOOT_LEGACY_LABEL_END;
+
+}
+
+/**
+  Get the Menu Entry from the list in Menu Entry List.
+
+  If MenuNumber is great or equal to the number of Menu
+  Entry in the list, then ASSERT.
+
+  @param MenuOption      The Menu Entry List to read the menu entry.
+  @param MenuNumber      The index of Menu Entry.
+
+  @return The Menu Entry.
+
+**/
+LEGACY_MENU_ENTRY *
+GetMenuEntry (
+  LEGACY_MENU_OPTION      *MenuOption,
+  UINTN                   MenuNumber
+  )
+{
+  LEGACY_MENU_ENTRY   *NewMenuEntry;
+  UINTN               Index;
+  LIST_ENTRY          *List;
+
+  ASSERT (MenuNumber < MenuOption->MenuNumber);
+
+  List = MenuOption->Head.ForwardLink;
+  for (Index = 0; Index < MenuNumber; Index++) {
+    List = List->ForwardLink;
+  }
+
+  NewMenuEntry = CR (List, LEGACY_MENU_ENTRY, Link, LEGACY_MENU_ENTRY_SIGNATURE);
+
+  return NewMenuEntry;
+}
+
+/**
+  Create string tokens for a menu from its help strings and display strings
+
+  @param HiiHandle          Hii Handle of the package to be updated.
+  @param MenuOption         The Menu whose string tokens need to be created
+
+**/
+VOID
+CreateLegacyMenuStringToken (
+  IN EFI_HII_HANDLE                   HiiHandle,
+  IN LEGACY_MENU_OPTION               *MenuOption
+  )
+{
+  LEGACY_MENU_ENTRY *NewMenuEntry;
+  UINTN             Index;
+
+  for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
+    NewMenuEntry = GetMenuEntry (MenuOption, Index);
+
+    NewMenuEntry->DisplayStringToken = HiiSetString (
+                                         HiiHandle,
+                                         0,
+                                         NewMenuEntry->DisplayString,
+                                         NULL
+                                         );
+
+    if (NULL == NewMenuEntry->HelpString) {
+      NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken;
+    } else {
+      NewMenuEntry->HelpStringToken = HiiSetString (
+                                        HiiHandle,
+                                        0,
+                                        NewMenuEntry->HelpString,
+                                        NULL
+                                        );
+    }
+  }
+}
+
+/**
+  Create a dynamic page so that Legacy Device boot order
+  can be set for specified device type.
+
+  @param UpdatePageId    The form ID. It also spefies the legacy device type.
+
+
+**/
+VOID
+UpdateLegacyDeviceOrderPage (
+  IN UINT16                           UpdatePageId
+  )
+{
+  LEGACY_MENU_OPTION          *OptionMenu;
+  LEGACY_MENU_ENTRY           *NewMenuEntry;
+  EFI_STRING_ID               StrRef;
+  EFI_STRING_ID               StrRefHelp;
+  UINT16                      *Default;
+  UINT16                      Index;
+  UINT16                      Key;
+  CHAR16                      String[100];
+  CHAR16                      *TypeStr;
+  CHAR16                      *TypeStrHelp;
+  CHAR16                      *FormTitle;
+  VOID                        *OptionsOpCodeHandle;
+  VOID                        *DefaultOpCodeHandle;
+
+  Key         = 0;
+  StrRef      = 0;
+  StrRefHelp  = 0;
+  OptionMenu  = NULL;
+  TypeStr     = NULL;
+  TypeStrHelp = NULL;
+  Default     = NULL;
+
+  RefreshLegacyUpdateData();
+
+  //
+  // Create oneof option list
+  //
+  switch (UpdatePageId) {
+  case FORM_FLOPPY_BOOT_ID:
+    OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyFDMenu;
+    Key         = (UINT16) LEGACY_FD_QUESTION_ID;
+    TypeStr     = STR_FLOPPY;
+    TypeStrHelp = STR_FLOPPY_HELP;
+    FormTitle   = STR_FLOPPY_TITLE;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyFD;
+    break;
+
+  case FORM_HARDDISK_BOOT_ID:
+    OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyHDMenu;
+    Key         = (UINT16) LEGACY_HD_QUESTION_ID;
+    TypeStr     = STR_HARDDISK;
+    TypeStrHelp = STR_HARDDISK_HELP;
+    FormTitle   = STR_HARDDISK_TITLE;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyHD;
+    break;
+
+  case FORM_CDROM_BOOT_ID:
+    OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyCDMenu;
+    Key         = (UINT16) LEGACY_CD_QUESTION_ID;
+    TypeStr     = STR_CDROM;
+    TypeStrHelp = STR_CDROM_HELP;
+    FormTitle   = STR_CDROM_TITLE;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyCD;
+    break;
+
+  case FORM_NET_BOOT_ID:
+    OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyNETMenu;
+    Key         = (UINT16) LEGACY_NET_QUESTION_ID;
+    TypeStr     = STR_NET;
+    TypeStrHelp = STR_NET_HELP;
+    FormTitle   = STR_NET_TITLE;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyNET;
+    break;
+
+  case FORM_BEV_BOOT_ID:
+    OptionMenu  = (LEGACY_MENU_OPTION *) &LegacyBEVMenu;
+    Key         = (UINT16) LEGACY_BEV_QUESTION_ID;
+    TypeStr     = STR_BEV;
+    TypeStrHelp = STR_BEV_HELP;
+    FormTitle   = STR_BEV_TITLE;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyBEV;
+    break;
+
+  default:
+    DEBUG ((EFI_D_ERROR, "Invalid command ID for updating page!\n"));
+    return;
+  }
+
+  HiiSetString (mLegacyBootOptionPrivate->HiiHandle, STRING_TOKEN(STR_ORDER_CHANGE_PROMPT), FormTitle, NULL);
+
+  CreateLegacyMenuStringToken (mLegacyBootOptionPrivate->HiiHandle, OptionMenu);
+
+  OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
+  ASSERT (OptionsOpCodeHandle != NULL);
+
+
+  for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
+    NewMenuEntry = GetMenuEntry (OptionMenu, Index);
+    //
+    // Create OneOf for each legacy device
+    //
+    HiiCreateOneOfOptionOpCode (
+      OptionsOpCodeHandle,
+      NewMenuEntry->DisplayStringToken,
+      0,
+      EFI_IFR_TYPE_NUM_SIZE_16,
+      ((LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext)->BbsIndex
+      );
+  }
+
+  //
+  // Create OneOf for item "Disabled"
+  //
+  HiiCreateOneOfOptionOpCode (
+    OptionsOpCodeHandle,
+    STRING_TOKEN (STR_DISABLE_LEGACY_DEVICE),
+    0,
+    EFI_IFR_TYPE_NUM_SIZE_16,
+    0xFF
+    );
+
+  //
+  // Create oneof tag here for FD/HD/CD #1 #2
+  //
+  for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
+    DefaultOpCodeHandle = HiiAllocateOpCodeHandle ();
+    ASSERT (DefaultOpCodeHandle != NULL);
+
+    HiiCreateDefaultOpCode (
+      DefaultOpCodeHandle,
+      EFI_HII_DEFAULT_CLASS_STANDARD,
+      EFI_IFR_TYPE_NUM_SIZE_16,
+      *Default++
+      );
+
+    //
+    // Create the string for oneof tag
+    //
+    UnicodeSPrint (String, sizeof (String), TypeStr, Index);
+    StrRef = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL);
+
+    UnicodeSPrint (String, sizeof (String), TypeStrHelp, Index);
+    StrRefHelp = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL);
+
+    HiiCreateOneOfOpCode (
+      mLegacyStartOpCodeHandle,
+      (EFI_QUESTION_ID) (Key + Index),
+      VARSTORE_ID_LEGACY_BOOT,
+      (UINT16) (Key + Index * 2 - CONFIG_OPTION_OFFSET),
+      StrRef,
+      StrRefHelp,
+      EFI_IFR_FLAG_CALLBACK,
+      EFI_IFR_NUMERIC_SIZE_2,
+      OptionsOpCodeHandle,
+      DefaultOpCodeHandle //NULL //
+      );
+
+    HiiFreeOpCodeHandle (DefaultOpCodeHandle);
+  }
+
+  HiiUpdateForm (
+    mLegacyBootOptionPrivate->HiiHandle,
+    &mLegacyBootOptionGuid,
+    LEGACY_ORDER_CHANGE_FORM_ID,
+    mLegacyStartOpCodeHandle,
+    mLegacyEndOpCodeHandle
+    );
+
+  HiiFreeOpCodeHandle (OptionsOpCodeHandle);
+}
+
+
+/**
+  Adjust question value when one question value has been changed.
+
+  @param QuestionId    The question id for the value changed question.
+  @param Value         The value for the changed question.
+
+**/
+VOID
+AdjustOptionValue (
+  IN  UINT16                                 QuestionId,
+  IN  EFI_IFR_TYPE_VALUE                     *Value
+  )
+{
+  UINTN                       Number;
+  UINT16                      *Default;
+  LEGACY_BOOT_NV_DATA         *CurrentNVMap;
+  UINT16                      *CurrentVal;
+  UINTN                       Index;
+  UINTN                       Index2;
+  UINTN                       Index3;
+  UINTN                       NewValuePos;
+  UINTN                       OldValue;
+  UINTN                       NewValue;
+  UINT8                       *DisMap;
+  UINTN                       Pos;
+  UINTN                       Bit;
+
+  Number = 0;
+  CurrentVal = 0;
+  Default = NULL;
+  NewValue = 0;
+  NewValuePos = 0;
+  OldValue = 0;
+
+  //
+  // Update Select FD/HD/CD/NET/BEV Order Form
+  //
+  ASSERT ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER));
+
+  CurrentNVMap = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData;
+  HiiGetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap);
+  DisMap  = mLegacyBootOptionPrivate->MaintainMapData->DisableMap;
+
+  if (QuestionId >= LEGACY_FD_QUESTION_ID && QuestionId < LEGACY_FD_QUESTION_ID + MAX_MENU_NUMBER) {
+    Number      = (UINT16) LegacyFDMenu.MenuNumber;
+    CurrentVal  = CurrentNVMap->LegacyFD;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyFD;
+  } else if (QuestionId >= LEGACY_HD_QUESTION_ID && QuestionId < LEGACY_HD_QUESTION_ID + MAX_MENU_NUMBER) {
+    Number      = (UINT16) LegacyHDMenu.MenuNumber;
+    CurrentVal  = CurrentNVMap->LegacyHD;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyHD;
+  } else if (QuestionId >= LEGACY_CD_QUESTION_ID && QuestionId < LEGACY_CD_QUESTION_ID + MAX_MENU_NUMBER) {
+    Number      = (UINT16) LegacyCDMenu.MenuNumber;
+    CurrentVal  = CurrentNVMap->LegacyCD;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyCD;
+  } else if (QuestionId >= LEGACY_NET_QUESTION_ID && QuestionId < LEGACY_NET_QUESTION_ID + MAX_MENU_NUMBER) {
+    Number      = (UINT16) LegacyNETMenu.MenuNumber;
+    CurrentVal  = CurrentNVMap->LegacyNET;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyNET;
+  } else if (QuestionId >= LEGACY_BEV_QUESTION_ID && QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER) {
+    Number      = (UINT16) LegacyBEVMenu.MenuNumber;
+    CurrentVal  = CurrentNVMap->LegacyBEV;
+    Default     = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyBEV;
+  }
+
+  //
+  //  First, find the different position
+  //  if there is change, it should be only one
+  //
+  for (Index = 0; Index < Number; Index++) {
+    if (CurrentVal[Index] != Default[Index]) {
+      OldValue  = Default[Index];
+      NewValue  = CurrentVal[Index];
+      break;
+    }
+  }
+
+  if (Index != Number) {
+    //
+    // there is change, now process
+    //
+    if (0xFF == NewValue) {
+      //
+      // This item will be disable
+      // Just move the items behind this forward to overlap it
+      //
+      Pos = OldValue / 8;
+      Bit = 7 - (OldValue % 8);
+      DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
+      for (Index2 = Index; Index2 < Number - 1; Index2++) {
+        CurrentVal[Index2] = CurrentVal[Index2 + 1];
+      }
+
+      CurrentVal[Index2] = 0xFF;
+    } else {
+      for (Index2 = 0; Index2 < Number; Index2++) {
+        if (Index2 == Index) {
+          continue;
+        }
+
+        if (Default[Index2] == NewValue) {
+          //
+          // If NewValue is in OldLegacyDev array
+          // remember its old position
+          //
+          NewValuePos = Index2;
+          break;
+        }
+      }
+
+      if (Index2 != Number) {
+        //
+        // We will change current item to an existing item
+        // (It's hard to describe here, please read code, it's like a cycle-moving)
+        //
+        for (Index2 = NewValuePos; Index2 != Index;) {
+          if (NewValuePos < Index) {
+            CurrentVal[Index2] = Default[Index2 + 1];
+            Index2++;
+          } else {
+            CurrentVal[Index2] = Default[Index2 - 1];
+            Index2--;
+          }
+        }
+      } else {
+        //
+        // If NewValue is not in OldlegacyDev array, we are changing to a disabled item
+        // so we should modify DisMap to reflect the change
+        //
+        Pos = NewValue / 8;
+        Bit = 7 - (NewValue % 8);
+        DisMap[Pos] = (UINT8) (DisMap[Pos] & (~ (UINT8) (1 << Bit)));
+        if (0xFF != OldValue) {
+          //
+          // Because NewValue is a item that was disabled before
+          // so after changing the OldValue should be disabled
+          // actually we are doing a swap of enable-disable states of two items
+          //
+          Pos = OldValue / 8;
+          Bit = 7 - (OldValue % 8);
+          DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
+        }
+      }
+    }
+    //
+    // To prevent DISABLE appears in the middle of the list
+    // we should perform a re-ordering
+    //
+    Index3 = Index;
+    Index = 0;
+    while (Index < Number) {
+      if (0xFF != CurrentVal[Index]) {
+        Index++;
+        continue;
+      }
+
+      Index2 = Index;
+      Index2++;
+      while (Index2 < Number) {
+        if (0xFF != CurrentVal[Index2]) {
+          break;
+        }
+
+        Index2++;
+      }
+
+      if (Index2 < Number) {
+        CurrentVal[Index]   = CurrentVal[Index2];
+        CurrentVal[Index2]  = 0xFF;
+      }
+
+      Index++;
+    }
+
+    //
+    // Return correct question value.
+    //
+    Value->u16 = CurrentVal[Index3];
+    CopyMem (Default, CurrentVal, sizeof (UINT16) * Number);
+  }
+
+  //
+  // Pass changed uncommitted data back to Form Browser
+  //
+  HiiSetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap, NULL);
+}
+
+/**
+  This call back function is registered with Boot Manager formset.
+  When user selects a boot option, this call back function will
+  be triggered. The boot option is saved for later processing.
+
+
+  @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+  @param Action          Specifies the type of action taken by the browser.
+  @param QuestionId      A unique value which is sent to the original exporting driver
+                         so that it can identify the type of data to expect.
+  @param Type            The type of value for the question.
+  @param Value           A pointer to the data being sent to the original exporting driver.
+  @param ActionRequest   On return, points to the action requested by the callback function.
+
+  @retval  EFI_SUCCESS           The callback successfully handled the action.
+  @retval  EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootOptionCallback (
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
+  IN  EFI_BROWSER_ACTION                     Action,
+  IN  EFI_QUESTION_ID                        QuestionId,
+  IN  UINT8                                  Type,
+  IN  EFI_IFR_TYPE_VALUE                     *Value,
+  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
+  )
+{
+  if (Action != EFI_BROWSER_ACTION_CHANGED && Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_FORM_OPEN) {
+    //
+    // Do nothing for other UEFI Action. Only do call back when data is changed or the form is open.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  if ((Value == NULL) || (ActionRequest == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Action == EFI_BROWSER_ACTION_FORM_OPEN) {
+    if (QuestionId == FORM_FLOPPY_BOOT_ID) {
+      if (!mFirstEnterLegacyForm) {
+        //
+        // The leagcyBootMaintUiLib depends on the LegacyBootManagerLib to realize its functionality.
+        // We need to do the leagcy boot options related actions after the LegacyBootManagerLib has been initialized.
+        // Opening the legacy menus is the appropriate time that the LegacyBootManagerLib has already been initialized.
+        //
+        mFirstEnterLegacyForm = TRUE;
+        GetLegacyOptions ();
+        GetLegacyOptionsOrder ();
+      }
+    }
+  }
+
+  if (Action == EFI_BROWSER_ACTION_CHANGING) {
+    switch (QuestionId) {
+    case FORM_FLOPPY_BOOT_ID:
+    case FORM_HARDDISK_BOOT_ID:
+    case FORM_CDROM_BOOT_ID:
+    case FORM_NET_BOOT_ID:
+    case FORM_BEV_BOOT_ID:
+      UpdateLegacyDeviceOrderPage (QuestionId);
+      break;
+
+    default:
+      break;
+    }
+  } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
+    if ((Value == NULL) || (ActionRequest == NULL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER)) {
+      AdjustOptionValue(QuestionId, Value);
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Create a menu entry by given menu type.
+
+  @param MenuType        The Menu type to be created.
+
+  @retval NULL           If failed to create the menu.
+  @return the new menu entry.
+
+**/
+LEGACY_MENU_ENTRY *
+CreateMenuEntry (
+  VOID
+  )
+{
+  LEGACY_MENU_ENTRY *MenuEntry;
+
+  //
+  // Create new menu entry
+  //
+  MenuEntry = AllocateZeroPool (sizeof (LEGACY_MENU_ENTRY));
+  if (MenuEntry == NULL) {
+    return NULL;
+  }
+
+  MenuEntry->VariableContext = AllocateZeroPool (sizeof (LEGACY_DEVICE_CONTEXT));
+  if (MenuEntry->VariableContext == NULL) {
+    FreePool (MenuEntry);
+    return NULL;
+  }
+
+  MenuEntry->Signature        = LEGACY_MENU_ENTRY_SIGNATURE;
+  return MenuEntry;
+}
+
+/**
+
+  Base on the L"LegacyDevOrder" variable to build the current order data.
+
+**/
+VOID
+GetLegacyOptionsOrder (
+  VOID
+  )
+{
+  UINTN                       VarSize;
+  UINT8                       *VarData;
+  UINT8                       *VarTmp;
+  LEGACY_DEV_ORDER_ENTRY      *DevOrder;
+  UINT16                      *LegacyDev;
+  UINTN                       Index;
+  LEGACY_MENU_OPTION          *OptionMenu;
+  UINT16                      VarDevOrder;
+  UINTN                       Pos;
+  UINTN                       Bit;
+  UINT8                       *DisMap;
+  UINTN                       TotalLength;
+
+  LegacyDev = NULL;
+  OptionMenu = NULL;
+
+  DisMap = ZeroMem (mLegacyBootOptionPrivate->MaintainMapData->DisableMap, sizeof (mLegacyBootOptionPrivate->MaintainMapData->DisableMap));
+
+  //
+  // Get Device Order from variable
+  //
+  GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize);
+  VarTmp = VarData;
+  if (NULL != VarData) {
+    DevOrder    = (LEGACY_DEV_ORDER_ENTRY *) VarData;
+    while (VarData < VarTmp + VarSize) {
+      switch (DevOrder->BbsType) {
+      case BBS_FLOPPY:
+        LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD;
+        OptionMenu = &LegacyFDMenu;
+        break;
+
+      case BBS_HARDDISK:
+        LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD;
+        OptionMenu = &LegacyHDMenu;
+        break;
+
+      case BBS_CDROM:
+        LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD;
+        OptionMenu = &LegacyCDMenu;
+        break;
+
+      case BBS_EMBED_NETWORK:
+        LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET;
+        OptionMenu = &LegacyNETMenu;
+        break;
+
+      case BBS_BEV_DEVICE:
+        LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV;
+        OptionMenu = &LegacyBEVMenu;
+        break;
+
+      case BBS_UNKNOWN:
+      default:
+        ASSERT (FALSE);
+        DEBUG ((DEBUG_ERROR, "Unsupported device type found!\n"));
+        break;
+      }
+
+      //
+      // Create oneof tag here for FD/HD/CD #1 #2
+      //
+      for (Index = 0; Index < OptionMenu->MenuNumber; Index++) {
+        TotalLength = sizeof (BBS_TYPE) + sizeof (UINT16) + Index * sizeof (UINT16);
+        VarDevOrder = *(UINT16 *) ((UINT8 *) DevOrder + TotalLength);
+
+        if (0xFF00 == (VarDevOrder & 0xFF00)) {
+          LegacyDev[Index]  = 0xFF;
+          Pos               = (VarDevOrder & 0xFF) / 8;
+          Bit               = 7 - ((VarDevOrder & 0xFF) % 8);
+          DisMap[Pos]       = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit));
+        } else {
+          LegacyDev[Index] = VarDevOrder & 0xFF;
+        }
+      }
+
+      VarData ++;
+      VarData += *(UINT16 *) VarData;
+      DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData;
+    }
+  }
+
+  CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA));
+  CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA));
+}
+
+/**
+
+  Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo().
+
+**/
+VOID
+GetLegacyOptions (
+  VOID
+  )
+{
+  LEGACY_MENU_ENTRY             *NewMenuEntry;
+  LEGACY_DEVICE_CONTEXT         *NewLegacyDevContext;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
+  UINTN                         BootOptionCount;
+  UINT16                        Index;
+  UINTN                         FDNum;
+  UINTN                         HDNum;
+  UINTN                         CDNum;
+  UINTN                         NETNum;
+  UINTN                         BEVNum;
+
+  //
+  // Initialize Bbs Table Context from BBS info data
+  //
+  InitializeListHead (&LegacyFDMenu.Head);
+  InitializeListHead (&LegacyHDMenu.Head);
+  InitializeListHead (&LegacyCDMenu.Head);
+  InitializeListHead (&LegacyNETMenu.Head);
+  InitializeListHead (&LegacyBEVMenu.Head);
+
+  FDNum   = 0;
+  HDNum   = 0;
+  CDNum   = 0;
+  NETNum  = 0;
+  BEVNum  = 0;
+
+  EfiBootManagerConnectAll ();
+
+  //
+  // for better user experience
+  // 1. User changes HD configuration (e.g.: unplug HDD), here we have a chance to remove the HDD boot option
+  // 2. User enables/disables UEFI PXE, here we have a chance to add/remove EFI Network boot option
+  //
+  EfiBootManagerRefreshAllBootOption ();
+
+  BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) ||
+        (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP)
+       ) {
+      continue;
+    }
+    ASSERT (BootOption[Index].OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA));
+    NewMenuEntry = CreateMenuEntry ();
+    ASSERT (NewMenuEntry != NULL);
+
+    NewLegacyDevContext              = (LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext;
+    NewLegacyDevContext->BbsIndex    = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex;
+    NewLegacyDevContext->Description = AllocateCopyPool (StrSize (BootOption[Index].Description), BootOption[Index].Description);
+    ASSERT (NewLegacyDevContext->Description != NULL);
+
+    NewMenuEntry->DisplayString = NewLegacyDevContext->Description;
+    NewMenuEntry->HelpString    = NULL;
+
+    switch (((BBS_BBS_DEVICE_PATH *) BootOption[Index].FilePath)->DeviceType) {
+    case BBS_TYPE_FLOPPY:
+      InsertTailList (&LegacyFDMenu.Head, &NewMenuEntry->Link);
+      FDNum++;
+      break;
+
+    case BBS_TYPE_HARDDRIVE:
+      InsertTailList (&LegacyHDMenu.Head, &NewMenuEntry->Link);
+      HDNum++;
+      break;
+
+    case BBS_TYPE_CDROM:
+      InsertTailList (&LegacyCDMenu.Head, &NewMenuEntry->Link);
+      CDNum++;
+      break;
+
+    case BBS_TYPE_EMBEDDED_NETWORK:
+      InsertTailList (&LegacyNETMenu.Head, &NewMenuEntry->Link);
+      NETNum++;
+      break;
+
+    case BBS_TYPE_BEV:
+      InsertTailList (&LegacyBEVMenu.Head, &NewMenuEntry->Link);
+      BEVNum++;
+      break;
+    }
+  }
+
+  EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
+
+  LegacyFDMenu.MenuNumber   = FDNum;
+  LegacyHDMenu.MenuNumber   = HDNum;
+  LegacyCDMenu.MenuNumber   = CDNum;
+  LegacyNETMenu.MenuNumber  = NETNum;
+  LegacyBEVMenu.MenuNumber  = BEVNum;
+}
+
+
+/**
+
+  Install Boot Manager Menu driver.
+
+  @param ImageHandle     The image handle.
+  @param SystemTable     The system table.
+
+  @retval  EFI_SUCEESS  Install Boot manager menu success.
+  @retval  Other        Return error status.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootMaintUiLibConstructor (
+  IN EFI_HANDLE                            ImageHandle,
+  IN EFI_SYSTEM_TABLE                      *SystemTable
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_LEGACY_BIOS_PROTOCOL          *LegacyBios;
+  LEGACY_BOOT_OPTION_CALLBACK_DATA  *LegacyBootOptionData;
+
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (!EFI_ERROR (Status)) {
+    //
+    // Create LegacyBootOptionData structures for Driver Callback
+    //
+    LegacyBootOptionData = AllocateZeroPool (sizeof (LEGACY_BOOT_OPTION_CALLBACK_DATA));
+    ASSERT (LegacyBootOptionData != NULL);
+
+    LegacyBootOptionData->MaintainMapData = AllocateZeroPool (sizeof (LEGACY_BOOT_MAINTAIN_DATA));
+    ASSERT (LegacyBootOptionData->MaintainMapData != NULL);
+
+    LegacyBootOptionData->ConfigAccess.ExtractConfig = LegacyBootOptionExtractConfig;
+    LegacyBootOptionData->ConfigAccess.RouteConfig   = LegacyBootOptionRouteConfig;
+    LegacyBootOptionData->ConfigAccess.Callback      = LegacyBootOptionCallback;
+
+    //
+    // Install Device Path Protocol and Config Access protocol to driver handle
+    //
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &LegacyBootOptionData->DriverHandle,
+                    &gEfiDevicePathProtocolGuid,
+                    &mLegacyBootOptionHiiVendorDevicePath,
+                    &gEfiHiiConfigAccessProtocolGuid,
+                    &LegacyBootOptionData->ConfigAccess,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Publish our HII data
+    //
+    LegacyBootOptionData->HiiHandle = HiiAddPackages (
+                                      &mLegacyBootOptionGuid,
+                                      LegacyBootOptionData->DriverHandle,
+                                      LegacyBootMaintUiVfrBin,
+                                      LegacyBootMaintUiLibStrings,
+                                      NULL
+                                      );
+    ASSERT (LegacyBootOptionData->HiiHandle != NULL);
+
+    mLegacyBootOptionPrivate = LegacyBootOptionData;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Destructor of Customized Display Library Instance.
+
+  @param  ImageHandle   The firmware allocated handle for the EFI image.
+  @param  SystemTable   A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS   The destructor completed successfully.
+  @retval Other value   The destructor did not complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootMaintUiLibDestructor (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+
+  if (mLegacyBootOptionPrivate != NULL && mLegacyBootOptionPrivate->DriverHandle != NULL) {
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    mLegacyBootOptionPrivate->DriverHandle,
+                    &gEfiDevicePathProtocolGuid,
+                    &mLegacyBootOptionHiiVendorDevicePath,
+                    &gEfiHiiConfigAccessProtocolGuid,
+                    &mLegacyBootOptionPrivate->ConfigAccess,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    HiiRemovePackages (mLegacyBootOptionPrivate->HiiHandle);
+
+    FreePool (mLegacyBootOptionPrivate->MaintainMapData);
+    FreePool (mLegacyBootOptionPrivate);
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBm.c b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBm.c
new file mode 100644
index 0000000000..6138a32ad7
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBm.c
@@ -0,0 +1,1530 @@
+/** @file
+  This function deal with the legacy boot option, it create, delete
+  and manage the legacy boot option, all legacy boot option is getting from
+  the legacy BBS table.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalLegacyBm.h"
+
+#define  LEGACY_BM_BOOT_DESCRIPTION_LENGTH  32
+
+/**
+  Initialize legacy boot manager library by call EfiBootManagerRegisterLegacyBootSupport
+  function to export two function pointer.
+
+  @param ImageHandle     The image handle.
+  @param SystemTable     The system table.
+
+  @retval EFI_SUCCESS    The legacy boot manager library is initialized correctly.
+  @return Other value if failed to initialize the legacy boot manager library.
+**/
+EFI_STATUS
+EFIAPI
+LegacyBootManagerLibConstructor (
+  IN EFI_HANDLE                            ImageHandle,
+  IN EFI_SYSTEM_TABLE                      *SystemTable
+)
+{
+  EfiBootManagerRegisterLegacyBootSupport (
+    LegacyBmRefreshAllBootOption,
+    LegacyBmBoot
+    );
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the device type from the input legacy device path.
+
+  @param DevicePath     The legacy device path.
+
+  @retval               The legacy device type.
+**/
+UINT16
+LegacyBmDeviceType (
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath
+  )
+{
+  ASSERT ((DevicePathType (DevicePath) == BBS_DEVICE_PATH) &&
+          (DevicePathSubType (DevicePath) == BBS_BBS_DP));
+  return ((BBS_BBS_DEVICE_PATH *) DevicePath)->DeviceType;
+}
+
+/**
+  Validate the BbsEntry base on the Boot Priority info in the BbsEntry.
+
+  @param BbsEntry       The input bbs entry info.
+
+  @retval TRUE          The BbsEntry is valid.
+  @retval FALSE         The BbsEntry is invalid.
+**/
+BOOLEAN
+LegacyBmValidBbsEntry (
+  IN BBS_TABLE   *BbsEntry
+  )
+{
+  switch (BbsEntry->BootPriority) {
+    case BBS_IGNORE_ENTRY:
+    case BBS_DO_NOT_BOOT_FROM:
+    case BBS_LOWEST_PRIORITY:
+      return FALSE;
+    default:
+      return TRUE;
+  }
+}
+
+/**
+  Build Legacy Device Name String according.
+
+  @param CurBBSEntry     BBS Table.
+  @param Index           Index.
+  @param BufSize         The buffer size.
+  @param BootString      The output string.
+
+**/
+VOID
+LegacyBmBuildLegacyDevNameString (
+  IN  BBS_TABLE                 *CurBBSEntry,
+  IN  UINTN                     Index,
+  IN  UINTN                     BufSize,
+  OUT CHAR16                    *BootString
+  )
+{
+  CHAR16  *Fmt;
+  CHAR16  *Type;
+  CHAR8   *StringDesc;
+  CHAR8   StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
+  CHAR16  StringBufferU[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
+
+  switch (Index) {
+  //
+  // Primary Master
+  //
+  case 1:
+    Fmt = L"Primary Master %s";
+    break;
+
+ //
+ // Primary Slave
+ //
+  case 2:
+    Fmt = L"Primary Slave %s";
+    break;
+
+  //
+  // Secondary Master
+  //
+  case 3:
+    Fmt = L"Secondary Master %s";
+    break;
+
+  //
+  // Secondary Slave
+  //
+  case 4:
+    Fmt = L"Secondary Slave %s";
+    break;
+
+  default:
+    Fmt = L"%s";
+    break;
+  }
+
+  switch (CurBBSEntry->DeviceType) {
+  case BBS_FLOPPY:
+    Type = L"Floppy";
+    break;
+
+  case BBS_HARDDISK:
+    Type = L"Harddisk";
+    break;
+
+  case BBS_CDROM:
+    Type = L"CDROM";
+    break;
+
+  case BBS_PCMCIA:
+    Type = L"PCMCIAe";
+    break;
+
+  case BBS_USB:
+    Type = L"USB";
+    break;
+
+  case BBS_EMBED_NETWORK:
+    Type = L"Network";
+    break;
+
+  case BBS_BEV_DEVICE:
+    Type = L"BEVe";
+    break;
+
+  case BBS_UNKNOWN:
+  default:
+    Type = L"Unknown";
+    break;
+  }
+  //
+  // If current BBS entry has its description then use it.
+  //
+  StringDesc = (CHAR8 *) (((UINTN) CurBBSEntry->DescStringSegment << 4) + CurBBSEntry->DescStringOffset);
+  if (NULL != StringDesc) {
+    //
+    // Only get fisrt 32 characters, this is suggested by BBS spec
+    //
+    CopyMem (StringBufferA, StringDesc, LEGACY_BM_BOOT_DESCRIPTION_LENGTH);
+    StringBufferA[LEGACY_BM_BOOT_DESCRIPTION_LENGTH] = 0;
+    AsciiStrToUnicodeStrS (StringBufferA, StringBufferU, ARRAY_SIZE (StringBufferU));
+    Fmt   = L"%s";
+    Type  = StringBufferU;
+  }
+
+  //
+  // BbsTable 16 entries are for onboard IDE.
+  // Set description string for SATA harddisks, Harddisk 0 ~ Harddisk 11
+  //
+  if (Index >= 5 && Index <= 16 && (CurBBSEntry->DeviceType == BBS_HARDDISK || CurBBSEntry->DeviceType == BBS_CDROM)) {
+    Fmt = L"%s %d";
+    UnicodeSPrint (BootString, BufSize, Fmt, Type, Index - 5);
+  } else {
+    UnicodeSPrint (BootString, BufSize, Fmt, Type);
+  }
+}
+
+/**
+  Get the Bbs index for the input boot option.
+
+  @param BootOption     The input boot option info.
+  @param BbsTable       The input Bbs table.
+  @param BbsCount       The input total bbs entry number.
+  @param BbsIndexUsed   The array shows how many BBS table indexs have been used.
+
+  @retval The index for the input boot option.
+**/
+UINT16
+LegacyBmFuzzyMatch (
+  EFI_BOOT_MANAGER_LOAD_OPTION   *BootOption,
+  BBS_TABLE                      *BbsTable,
+  UINT16                         BbsCount,
+  BOOLEAN                        *BbsIndexUsed
+  )
+{
+  UINT16                         Index;
+  LEGACY_BM_BOOT_OPTION_BBS_DATA *BbsData;
+  CHAR16                         Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
+
+  BbsData = (LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData;
+
+  //
+  // Directly check the BBS index stored in BootOption
+  //
+  if ((BbsData->BbsIndex < BbsCount) &&
+      (LegacyBmDeviceType (BootOption->FilePath) == BbsTable[BbsData->BbsIndex].DeviceType)) {
+    LegacyBmBuildLegacyDevNameString (
+      &BbsTable[BbsData->BbsIndex],
+      BbsData->BbsIndex,
+      sizeof (Description),
+      Description
+      );
+    if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[BbsData->BbsIndex]) {
+      //
+      // If devices with the same description string are connected,
+      // the BbsIndex of the first device is returned for the other device also.
+      // So, check if the BbsIndex is already being used, before assigning the BbsIndex.
+      //
+      BbsIndexUsed[BbsData->BbsIndex] = TRUE;
+      return BbsData->BbsIndex;
+    }
+  }
+
+  //
+  // BBS table could be changed (entry removed/moved)
+  // find the correct BBS index
+  //
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&BbsTable[Index]) ||
+        (BbsTable[Index].DeviceType != LegacyBmDeviceType (BootOption->FilePath))) {
+      continue;
+    }
+
+    LegacyBmBuildLegacyDevNameString (
+      &BbsTable[Index],
+      Index,
+      sizeof (Description),
+      Description
+      );
+    if ((StrCmp (Description, BootOption->Description) == 0) && !BbsIndexUsed[Index]) {
+      //
+      // If devices with the same description string are connected,
+      // the BbsIndex of the first device is assigned for the other device also.
+      // So, check if the BbsIndex is already being used, before assigning the corrected BbsIndex.
+      //
+      break;
+    }
+  }
+
+  //
+  // Add the corrected BbsIndex in the UsedBbsIndex Buffer
+  //
+  if (Index != BbsCount) {
+    BbsIndexUsed[Index] = TRUE;
+  }
+
+  return Index;
+}
+
+/**
+
+  Update legacy device order base on the input info.
+
+  @param   LegacyDevOrder     Legacy device order data buffer.
+  @param   LegacyDevOrderSize Legacy device order data buffer size.
+  @param   DeviceType         Device type which need to check.
+  @param   OldBbsIndex        Old Bds Index.
+  @param   NewBbsIndex        New Bds Index, if it is -1,means remove this option.
+
+**/
+VOID
+LegacyBmUpdateBbsIndex (
+  LEGACY_DEV_ORDER_ENTRY   *LegacyDevOrder,
+  UINTN                    *LegacyDevOrderSize,
+  UINT16                   DeviceType,
+  UINT16                   OldBbsIndex,
+  UINT16                   NewBbsIndex // Delete entry if -1
+  )
+{
+  LEGACY_DEV_ORDER_ENTRY   *Entry;
+  UINTN                    Index;
+
+  ASSERT (((LegacyDevOrder == NULL) && (*LegacyDevOrderSize == 0)) ||
+          ((LegacyDevOrder != NULL) && (*LegacyDevOrderSize != 0))
+         );
+
+  for (Entry = LegacyDevOrder;
+       Entry < (LEGACY_DEV_ORDER_ENTRY *) ((UINT8 *) LegacyDevOrder + *LegacyDevOrderSize);
+       Entry = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) Entry + sizeof (BBS_TYPE) + Entry->Length)
+       ) {
+    if (Entry->BbsType == DeviceType) {
+      for (Index = 0; Index < Entry->Length / sizeof (UINT16) - 1; Index++) {
+        if (Entry->Data[Index] == OldBbsIndex) {
+          if (NewBbsIndex == (UINT16) -1) {
+            //
+            // Delete the old entry
+            //
+            CopyMem (
+              &Entry->Data[Index],
+              &Entry->Data[Index + 1],
+              (UINT8 *) LegacyDevOrder + *LegacyDevOrderSize - (UINT8 *) &Entry->Data[Index + 1]
+              );
+            Entry->Length       -= sizeof (UINT16);
+            *LegacyDevOrderSize -= sizeof(UINT16);
+          } else {
+            Entry->Data[Index]   = NewBbsIndex;
+          }
+          break;
+        }
+      }
+      break;
+    }
+  }
+}
+
+/**
+  Delete all the legacy boot options.
+
+  @retval EFI_SUCCESS            All legacy boot options are deleted.
+**/
+EFI_STATUS
+LegacyBmDeleteAllBootOptions (
+  VOID
+  )
+{
+  EFI_STATUS                    Status;
+  UINTN                         Index;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
+  UINTN                         BootOptionCount;
+
+  BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    if ((DevicePathType (BootOption[Index].FilePath) == BBS_DEVICE_PATH) &&
+        (DevicePathSubType (BootOption[Index].FilePath) == BBS_BBS_DP)) {
+      Status = EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType);
+      //
+      // Deleting variable with current variable implementation shouldn't fail.
+      //
+      ASSERT_EFI_ERROR (Status);
+    }
+  }
+
+  Status = gRT->SetVariable (
+                  VAR_LEGACY_DEV_ORDER,
+                  &gEfiLegacyDevOrderVariableGuid,
+                  0,
+                  0,
+                  NULL
+                  );
+  //
+  // Deleting variable with current variable implementation shouldn't fail.
+  //
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Delete all the invalid legacy boot options.
+
+  @retval EFI_SUCCESS             All invalide legacy boot options are deleted.
+  @retval EFI_OUT_OF_RESOURCES    Fail to allocate necessary memory.
+  @retval EFI_NOT_FOUND           Fail to retrive variable of boot order.
+**/
+EFI_STATUS
+LegacyBmDeleteAllInvalidBootOptions (
+  VOID
+  )
+{
+  EFI_STATUS                    Status;
+  UINT16                        HddCount;
+  UINT16                        BbsCount;
+  HDD_INFO                      *HddInfo;
+  BBS_TABLE                     *BbsTable;
+  UINT16                        BbsIndex;
+  EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
+  UINTN                         Index;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption;
+  UINTN                         BootOptionCount;
+  LEGACY_DEV_ORDER_ENTRY        *LegacyDevOrder;
+  UINTN                         LegacyDevOrderSize;
+  BOOLEAN                       *BbsIndexUsed;
+
+  HddCount      = 0;
+  BbsCount      = 0;
+  HddInfo       = NULL;
+  BbsTable      = NULL;
+
+  Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = LegacyBios->GetBbsInfo (
+                         LegacyBios,
+                         &HddCount,
+                         &HddInfo,
+                         &BbsCount,
+                         &BbsTable
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &LegacyDevOrder, &LegacyDevOrderSize);
+
+  BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+
+  BbsIndexUsed = AllocateZeroPool (BbsCount * sizeof (BOOLEAN));
+  ASSERT (BbsIndexUsed != NULL);
+
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    //
+    // Skip non legacy boot option
+    //
+    if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) ||
+        (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP)) {
+      continue;
+    }
+
+    BbsIndex = LegacyBmFuzzyMatch (&BootOption[Index], BbsTable, BbsCount, BbsIndexUsed);
+    if (BbsIndex == BbsCount) {
+      DEBUG ((EFI_D_INFO, "[LegacyBds] Delete Boot Option Boot%04x: %s\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description));
+      //
+      // Delete entry from LegacyDevOrder
+      //
+      LegacyBmUpdateBbsIndex (
+        LegacyDevOrder,
+        &LegacyDevOrderSize,
+        LegacyBmDeviceType (BootOption[Index].FilePath),
+        ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex,
+        (UINT16) -1
+        );
+      EfiBootManagerDeleteLoadOptionVariable (BootOption[Index].OptionNumber, BootOption[Index].OptionType);
+    } else {
+      if (((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex != BbsIndex) {
+        DEBUG ((EFI_D_INFO, "[LegacyBds] Update Boot Option Boot%04x: %s Bbs0x%04x->Bbs0x%04x\n", (UINTN) BootOption[Index].OptionNumber, BootOption[Index].Description,
+                (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex, (UINTN) BbsIndex));
+        //
+        // Update the BBS index in LegacyDevOrder
+        //
+        LegacyBmUpdateBbsIndex (
+          LegacyDevOrder,
+          &LegacyDevOrderSize,
+          LegacyBmDeviceType (BootOption[Index].FilePath),
+          ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex,
+          BbsIndex
+          );
+
+        //
+        // Update the OptionalData in the Boot#### variable
+        //
+        ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex = BbsIndex;
+        EfiBootManagerLoadOptionToVariable (&BootOption[Index]);
+      }
+    }
+  }
+  EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
+
+  if (LegacyDevOrder != NULL) {
+    Status = gRT->SetVariable (
+                    VAR_LEGACY_DEV_ORDER,
+                    &gEfiLegacyDevOrderVariableGuid,
+                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                    LegacyDevOrderSize,
+                    LegacyDevOrder
+                    );
+    //
+    // Shrink variable with current variable implementation shouldn't fail.
+    //
+    ASSERT_EFI_ERROR (Status);
+
+    FreePool (LegacyDevOrder);
+  }
+  FreePool(BbsIndexUsed);
+  return Status;
+}
+
+/**
+  Create legacy boot option.
+
+  @param BootOption        Ponter to the boot option which will be crated.
+  @param BbsEntry          The input bbs entry info.
+  @param BbsIndex          The BBS index.
+
+  @retval EFI_SUCCESS            Create legacy boot option successfully.
+  @retval EFI_INVALID_PARAMETER  Invalid input parameter.
+
+**/
+EFI_STATUS
+LegacyBmCreateLegacyBootOption (
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption,
+  IN BBS_TABLE                         *BbsEntry,
+  IN UINT16                            BbsIndex
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_DEVICE_PATH_PROTOCOL     *DevicePath;
+  CHAR16                       Description[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
+  CHAR8                        HelpString[LEGACY_BM_BOOT_DESCRIPTION_LENGTH + 1];
+  UINTN                        StringLen;
+  LEGACY_BM_BOOT_OPTION_BBS_DATA  *OptionalData;
+  BBS_BBS_DEVICE_PATH          *BbsNode;
+
+  if ((BootOption == NULL) || (BbsEntry == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  LegacyBmBuildLegacyDevNameString (BbsEntry, BbsIndex, sizeof (Description), Description);
+
+  //
+  // Create the BBS device path with description string
+  //
+  UnicodeStrToAsciiStrS (Description, HelpString, sizeof (HelpString));
+  StringLen = AsciiStrLen (HelpString);
+  DevicePath = AllocatePool (sizeof (BBS_BBS_DEVICE_PATH) + StringLen + END_DEVICE_PATH_LENGTH);
+  ASSERT (DevicePath != NULL);
+
+  BbsNode = (BBS_BBS_DEVICE_PATH *) DevicePath;
+  SetDevicePathNodeLength (BbsNode, sizeof (BBS_BBS_DEVICE_PATH) + StringLen);
+  BbsNode->Header.Type    = BBS_DEVICE_PATH;
+  BbsNode->Header.SubType = BBS_BBS_DP;
+  BbsNode->DeviceType     = BbsEntry->DeviceType;
+  CopyMem (&BbsNode->StatusFlag, &BbsEntry->StatusFlags, sizeof (BBS_STATUS_FLAGS));
+  CopyMem (BbsNode->String, HelpString, StringLen + 1);
+
+  SetDevicePathEndNode (NextDevicePathNode (BbsNode));
+
+  //
+  // Create the OptionalData
+  //
+  OptionalData = AllocatePool (sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA));
+  ASSERT (OptionalData != NULL);
+  OptionalData->BbsIndex = BbsIndex;
+
+  //
+  // Create the BootOption
+  //
+  Status = EfiBootManagerInitializeLoadOption (
+             BootOption,
+             LoadOptionNumberUnassigned,
+             LoadOptionTypeBoot,
+             LOAD_OPTION_ACTIVE,
+             Description,
+             DevicePath,
+             (UINT8 *) OptionalData,
+             sizeof (LEGACY_BM_BOOT_OPTION_BBS_DATA)
+             );
+  FreePool (DevicePath);
+  FreePool (OptionalData);
+
+  return Status;
+}
+
+/**
+  Fill the device order buffer.
+
+  @param BbsTable        The BBS table.
+  @param BbsType         The BBS Type.
+  @param BbsCount        The BBS Count.
+  @param Buf             device order buffer.
+
+  @return The device order buffer.
+
+**/
+UINT16 *
+LegacyBmFillDevOrderBuf (
+  IN BBS_TABLE                    *BbsTable,
+  IN BBS_TYPE                     BbsType,
+  IN UINTN                        BbsCount,
+  OUT UINT16                      *Buf
+  )
+{
+  UINTN Index;
+
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
+      continue;
+    }
+
+    if (BbsTable[Index].DeviceType != BbsType) {
+      continue;
+    }
+
+    *Buf = (UINT16) (Index & 0xFF);
+    Buf++;
+  }
+
+  return Buf;
+}
+
+/**
+  Create the device order buffer.
+
+  @param BbsTable        The BBS table.
+  @param BbsCount        The BBS Count.
+
+  @retval EFI_SUCCES             The buffer is created and the EFI variable named
+                                 VAR_LEGACY_DEV_ORDER and EfiLegacyDevOrderGuid is
+                                 set correctly.
+  @retval EFI_OUT_OF_RESOURCES   Memmory or storage is not enough.
+  @retval EFI_DEVICE_ERROR       Fail to add the device order into EFI variable fail
+                                 because of hardware error.
+**/
+EFI_STATUS
+LegacyBmCreateDevOrder (
+  IN BBS_TABLE                  *BbsTable,
+  IN UINT16                     BbsCount
+  )
+{
+  UINTN                       Index;
+  UINTN                       FDCount;
+  UINTN                       HDCount;
+  UINTN                       CDCount;
+  UINTN                       NETCount;
+  UINTN                       BEVCount;
+  UINTN                       TotalSize;
+  UINTN                       HeaderSize;
+  LEGACY_DEV_ORDER_ENTRY      *DevOrder;
+  LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
+  EFI_STATUS                  Status;
+
+  FDCount     = 0;
+  HDCount     = 0;
+  CDCount     = 0;
+  NETCount    = 0;
+  BEVCount    = 0;
+  TotalSize   = 0;
+  HeaderSize  = sizeof (BBS_TYPE) + sizeof (UINT16);
+  DevOrder    = NULL;
+  Status      = EFI_SUCCESS;
+
+  //
+  // Count all boot devices
+  //
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
+      continue;
+    }
+
+    switch (BbsTable[Index].DeviceType) {
+    case BBS_FLOPPY:
+      FDCount++;
+      break;
+
+    case BBS_HARDDISK:
+      HDCount++;
+      break;
+
+    case BBS_CDROM:
+      CDCount++;
+      break;
+
+    case BBS_EMBED_NETWORK:
+      NETCount++;
+      break;
+
+    case BBS_BEV_DEVICE:
+      BEVCount++;
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  TotalSize += (HeaderSize + sizeof (UINT16) * FDCount);
+  TotalSize += (HeaderSize + sizeof (UINT16) * HDCount);
+  TotalSize += (HeaderSize + sizeof (UINT16) * CDCount);
+  TotalSize += (HeaderSize + sizeof (UINT16) * NETCount);
+  TotalSize += (HeaderSize + sizeof (UINT16) * BEVCount);
+
+  //
+  // Create buffer to hold all boot device order
+  //
+  DevOrder = AllocateZeroPool (TotalSize);
+  if (NULL == DevOrder) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  DevOrderPtr          = DevOrder;
+
+  DevOrderPtr->BbsType = BBS_FLOPPY;
+  DevOrderPtr->Length  = (UINT16) (sizeof (DevOrderPtr->Length) + FDCount * sizeof (UINT16));
+  DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_FLOPPY, BbsCount, DevOrderPtr->Data);
+
+  DevOrderPtr->BbsType = BBS_HARDDISK;
+  DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
+  DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_HARDDISK, BbsCount, DevOrderPtr->Data);
+
+  DevOrderPtr->BbsType = BBS_CDROM;
+  DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
+  DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_CDROM, BbsCount, DevOrderPtr->Data);
+
+  DevOrderPtr->BbsType = BBS_EMBED_NETWORK;
+  DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
+  DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_EMBED_NETWORK, BbsCount, DevOrderPtr->Data);
+
+  DevOrderPtr->BbsType = BBS_BEV_DEVICE;
+  DevOrderPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
+  DevOrderPtr          = (LEGACY_DEV_ORDER_ENTRY *) LegacyBmFillDevOrderBuf (BbsTable, BBS_BEV_DEVICE, BbsCount, DevOrderPtr->Data);
+
+  ASSERT (TotalSize == ((UINTN) DevOrderPtr - (UINTN) DevOrder));
+
+  //
+  // Save device order for legacy boot device to variable.
+  //
+  Status = gRT->SetVariable (
+                  VAR_LEGACY_DEV_ORDER,
+                  &gEfiLegacyDevOrderVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  TotalSize,
+                  DevOrder
+                  );
+  FreePool (DevOrder);
+
+  return Status;
+}
+
+/**
+  Add the legacy boot devices from BBS table into
+  the legacy device boot order.
+
+  @retval EFI_SUCCESS           The boot devices are added successfully.
+  @retval EFI_NOT_FOUND         The legacy boot devices are not found.
+  @retval EFI_OUT_OF_RESOURCES  Memmory or storage is not enough.
+  @retval EFI_DEVICE_ERROR      Fail to add the legacy device boot order into EFI variable
+                                because of hardware error.
+**/
+EFI_STATUS
+LegacyBmUpdateDevOrder (
+  VOID
+  )
+{
+  LEGACY_DEV_ORDER_ENTRY      *DevOrder;
+  LEGACY_DEV_ORDER_ENTRY      *NewDevOrder;
+  LEGACY_DEV_ORDER_ENTRY      *Ptr;
+  LEGACY_DEV_ORDER_ENTRY      *NewPtr;
+  EFI_LEGACY_BIOS_PROTOCOL    *LegacyBios;
+  EFI_STATUS                  Status;
+  UINT16                      HddCount;
+  UINT16                      BbsCount;
+  HDD_INFO                    *LocalHddInfo;
+  BBS_TABLE                   *LocalBbsTable;
+  UINTN                       Index;
+  UINTN                       Index2;
+  UINTN                       *Idx;
+  UINTN                       FDCount;
+  UINTN                       HDCount;
+  UINTN                       CDCount;
+  UINTN                       NETCount;
+  UINTN                       BEVCount;
+  UINTN                       TotalSize;
+  UINTN                       HeaderSize;
+  UINT16                      *NewFDPtr;
+  UINT16                      *NewHDPtr;
+  UINT16                      *NewCDPtr;
+  UINT16                      *NewNETPtr;
+  UINT16                      *NewBEVPtr;
+  UINT16                      *NewDevPtr;
+  UINTN                       FDIndex;
+  UINTN                       HDIndex;
+  UINTN                       CDIndex;
+  UINTN                       NETIndex;
+  UINTN                       BEVIndex;
+
+  Idx           = NULL;
+  FDCount       = 0;
+  HDCount       = 0;
+  CDCount       = 0;
+  NETCount      = 0;
+  BEVCount      = 0;
+  TotalSize     = 0;
+  HeaderSize    = sizeof (BBS_TYPE) + sizeof (UINT16);
+  FDIndex       = 0;
+  HDIndex       = 0;
+  CDIndex       = 0;
+  NETIndex      = 0;
+  BEVIndex      = 0;
+  NewDevPtr     = NULL;
+
+  Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = LegacyBios->GetBbsInfo (
+                         LegacyBios,
+                         &HddCount,
+                         &LocalHddInfo,
+                         &BbsCount,
+                         &LocalBbsTable
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, NULL);
+  if (NULL == DevOrder) {
+    return LegacyBmCreateDevOrder (LocalBbsTable, BbsCount);
+  }
+  //
+  // First we figure out how many boot devices with same device type respectively
+  //
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
+      continue;
+    }
+
+    switch (LocalBbsTable[Index].DeviceType) {
+    case BBS_FLOPPY:
+      FDCount++;
+      break;
+
+    case BBS_HARDDISK:
+      HDCount++;
+      break;
+
+    case BBS_CDROM:
+      CDCount++;
+      break;
+
+    case BBS_EMBED_NETWORK:
+      NETCount++;
+      break;
+
+    case BBS_BEV_DEVICE:
+      BEVCount++;
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  TotalSize += (HeaderSize + FDCount * sizeof (UINT16));
+  TotalSize += (HeaderSize + HDCount * sizeof (UINT16));
+  TotalSize += (HeaderSize + CDCount * sizeof (UINT16));
+  TotalSize += (HeaderSize + NETCount * sizeof (UINT16));
+  TotalSize += (HeaderSize + BEVCount * sizeof (UINT16));
+
+  NewDevOrder = AllocateZeroPool (TotalSize);
+  if (NULL == NewDevOrder) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // copy FD
+  //
+  Ptr             = DevOrder;
+  NewPtr          = NewDevOrder;
+  NewPtr->BbsType = Ptr->BbsType;
+  NewPtr->Length  = (UINT16) (sizeof (UINT16) + FDCount * sizeof (UINT16));
+  for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
+        LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_FLOPPY
+        ) {
+      continue;
+    }
+
+    NewPtr->Data[FDIndex] = Ptr->Data[Index];
+    FDIndex++;
+  }
+  NewFDPtr = NewPtr->Data;
+
+  //
+  // copy HD
+  //
+  Ptr             = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
+  NewPtr          = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
+  NewPtr->BbsType = Ptr->BbsType;
+  NewPtr->Length  = (UINT16) (sizeof (UINT16) + HDCount * sizeof (UINT16));
+  for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
+        LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_HARDDISK
+        ) {
+      continue;
+    }
+
+    NewPtr->Data[HDIndex] = Ptr->Data[Index];
+    HDIndex++;
+  }
+  NewHDPtr = NewPtr->Data;
+
+  //
+  // copy CD
+  //
+  Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
+  NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
+  NewPtr->BbsType = Ptr->BbsType;
+  NewPtr->Length  = (UINT16) (sizeof (UINT16) + CDCount * sizeof (UINT16));
+  for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
+        LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_CDROM
+        ) {
+      continue;
+    }
+
+    NewPtr->Data[CDIndex] = Ptr->Data[Index];
+    CDIndex++;
+  }
+  NewCDPtr = NewPtr->Data;
+
+  //
+  // copy NET
+  //
+  Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
+  NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
+  NewPtr->BbsType = Ptr->BbsType;
+  NewPtr->Length  = (UINT16) (sizeof (UINT16) + NETCount * sizeof (UINT16));
+  for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
+        LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_EMBED_NETWORK
+        ) {
+      continue;
+    }
+
+    NewPtr->Data[NETIndex] = Ptr->Data[Index];
+    NETIndex++;
+  }
+  NewNETPtr = NewPtr->Data;
+
+  //
+  // copy BEV
+  //
+  Ptr    = (LEGACY_DEV_ORDER_ENTRY *) (&Ptr->Data[Ptr->Length / sizeof (UINT16) - 1]);
+  NewPtr = (LEGACY_DEV_ORDER_ENTRY *) (&NewPtr->Data[NewPtr->Length / sizeof (UINT16) -1]);
+  NewPtr->BbsType = Ptr->BbsType;
+  NewPtr->Length  = (UINT16) (sizeof (UINT16) + BEVCount * sizeof (UINT16));
+  for (Index = 0; Index < Ptr->Length / sizeof (UINT16) - 1; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Ptr->Data[Index] & 0xFF]) ||
+        LocalBbsTable[Ptr->Data[Index] & 0xFF].DeviceType != BBS_BEV_DEVICE
+        ) {
+      continue;
+    }
+
+    NewPtr->Data[BEVIndex] = Ptr->Data[Index];
+    BEVIndex++;
+  }
+  NewBEVPtr = NewPtr->Data;
+
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
+      continue;
+    }
+
+    switch (LocalBbsTable[Index].DeviceType) {
+    case BBS_FLOPPY:
+      Idx       = &FDIndex;
+      NewDevPtr = NewFDPtr;
+      break;
+
+    case BBS_HARDDISK:
+      Idx       = &HDIndex;
+      NewDevPtr = NewHDPtr;
+      break;
+
+    case BBS_CDROM:
+      Idx       = &CDIndex;
+      NewDevPtr = NewCDPtr;
+      break;
+
+    case BBS_EMBED_NETWORK:
+      Idx       = &NETIndex;
+      NewDevPtr = NewNETPtr;
+      break;
+
+    case BBS_BEV_DEVICE:
+      Idx       = &BEVIndex;
+      NewDevPtr = NewBEVPtr;
+      break;
+
+    default:
+      Idx = NULL;
+      break;
+    }
+    //
+    // at this point we have copied those valid indexes to new buffer
+    // and we should check if there is any new appeared boot device
+    //
+    if (Idx != NULL) {
+      for (Index2 = 0; Index2 < *Idx; Index2++) {
+        if ((NewDevPtr[Index2] & 0xFF) == (UINT16) Index) {
+          break;
+        }
+      }
+
+      if (Index2 == *Idx) {
+        //
+        // Index2 == *Idx means we didn't find Index
+        // so Index is a new appeared device's index in BBS table
+        // insert it before disabled indexes.
+        //
+        for (Index2 = 0; Index2 < *Idx; Index2++) {
+          if ((NewDevPtr[Index2] & 0xFF00) == 0xFF00) {
+            break;
+          }
+        }
+        CopyMem (&NewDevPtr[Index2 + 1], &NewDevPtr[Index2], (*Idx - Index2) * sizeof (UINT16));
+        NewDevPtr[Index2] = (UINT16) (Index & 0xFF);
+        (*Idx)++;
+      }
+    }
+  }
+
+  FreePool (DevOrder);
+
+  Status = gRT->SetVariable (
+                  VAR_LEGACY_DEV_ORDER,
+                  &gEfiLegacyDevOrderVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  TotalSize,
+                  NewDevOrder
+                  );
+  FreePool (NewDevOrder);
+
+  return Status;
+}
+
+/**
+  Set Boot Priority for specified device type.
+
+  @param DeviceType      The device type.
+  @param BbsIndex        The BBS index to set the highest priority. Ignore when -1.
+  @param LocalBbsTable   The BBS table.
+  @param Priority        The prority table.
+
+  @retval EFI_SUCCESS           The function completes successfully.
+  @retval EFI_NOT_FOUND         Failed to find device.
+  @retval EFI_OUT_OF_RESOURCES  Failed to get the efi variable of device order.
+
+**/
+EFI_STATUS
+LegacyBmSetPriorityForSameTypeDev (
+  IN UINT16                                              DeviceType,
+  IN UINTN                                               BbsIndex,
+  IN OUT BBS_TABLE                                       *LocalBbsTable,
+  IN OUT UINT16                                          *Priority
+  )
+{
+  LEGACY_DEV_ORDER_ENTRY      *DevOrder;
+  LEGACY_DEV_ORDER_ENTRY      *DevOrderPtr;
+  UINTN                       DevOrderSize;
+  UINTN                       Index;
+
+  GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &DevOrder, &DevOrderSize);
+  if (NULL == DevOrder) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DevOrderPtr = DevOrder;
+  while ((UINT8 *) DevOrderPtr < (UINT8 *) DevOrder + DevOrderSize) {
+    if (DevOrderPtr->BbsType == DeviceType) {
+      break;
+    }
+
+    DevOrderPtr = (LEGACY_DEV_ORDER_ENTRY *) ((UINTN) DevOrderPtr + sizeof (BBS_TYPE) + DevOrderPtr->Length);
+  }
+
+  if ((UINT8 *) DevOrderPtr >= (UINT8 *) DevOrder + DevOrderSize) {
+    FreePool (DevOrder);
+    return EFI_NOT_FOUND;
+  }
+
+  if (BbsIndex != (UINTN) -1) {
+    //
+    // In case the BBS entry isn't valid because devices were plugged or removed.
+    //
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[BbsIndex]) || (LocalBbsTable[BbsIndex].DeviceType != DeviceType)) {
+      FreePool (DevOrder);
+      return EFI_NOT_FOUND;
+    }
+    LocalBbsTable[BbsIndex].BootPriority = *Priority;
+    (*Priority)++;
+  }
+  //
+  // If the high byte of the DevIndex is 0xFF, it indicates that this device has been disabled.
+  //
+  for (Index = 0; Index < DevOrderPtr->Length / sizeof (UINT16) - 1; Index++) {
+    if ((DevOrderPtr->Data[Index] & 0xFF00) == 0xFF00) {
+      //
+      // LocalBbsTable[DevIndex[Index] & 0xFF].BootPriority = BBS_DISABLED_ENTRY;
+      //
+    } else if (DevOrderPtr->Data[Index] != BbsIndex) {
+      LocalBbsTable[DevOrderPtr->Data[Index]].BootPriority = *Priority;
+      (*Priority)++;
+    }
+  }
+
+  FreePool (DevOrder);
+  return EFI_SUCCESS;
+}
+
+/**
+  Print the BBS Table.
+
+  @param LocalBbsTable   The BBS table.
+  @param BbsCount        The count of entry in BBS table.
+**/
+VOID
+LegacyBmPrintBbsTable (
+  IN BBS_TABLE  *LocalBbsTable,
+  IN UINT16     BbsCount
+  )
+{
+  UINT16  Index;
+
+  DEBUG ((DEBUG_INFO, "\n"));
+  DEBUG ((DEBUG_INFO, " NO  Prio bb/dd/ff cl/sc Type Stat segm:offs\n"));
+  DEBUG ((DEBUG_INFO, "=============================================\n"));
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
+      continue;
+    }
+
+    DEBUG (
+      (DEBUG_INFO,
+      " %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n",
+      (UINTN) Index,
+      (UINTN) LocalBbsTable[Index].BootPriority,
+      (UINTN) LocalBbsTable[Index].Bus,
+      (UINTN) LocalBbsTable[Index].Device,
+      (UINTN) LocalBbsTable[Index].Function,
+      (UINTN) LocalBbsTable[Index].Class,
+      (UINTN) LocalBbsTable[Index].SubClass,
+      (UINTN) LocalBbsTable[Index].DeviceType,
+      (UINTN) * (UINT16 *) &LocalBbsTable[Index].StatusFlags,
+      (UINTN) LocalBbsTable[Index].BootHandlerSegment,
+      (UINTN) LocalBbsTable[Index].BootHandlerOffset,
+      (UINTN) ((LocalBbsTable[Index].MfgStringSegment << 4) + LocalBbsTable[Index].MfgStringOffset),
+      (UINTN) ((LocalBbsTable[Index].DescStringSegment << 4) + LocalBbsTable[Index].DescStringOffset))
+      );
+  }
+
+  DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/**
+  Set the boot priority for BBS entries based on boot option entry and boot order.
+
+  @param  BootOption            The boot option is to be checked for refresh BBS table.
+
+  @retval EFI_SUCCESS           The boot priority for BBS entries is refreshed successfully.
+  @retval EFI_NOT_FOUND         BBS entries can't be found.
+  @retval EFI_OUT_OF_RESOURCES  Failed to get the legacy device boot order.
+**/
+EFI_STATUS
+LegacyBmRefreshBbsTableForBoot (
+  IN EFI_BOOT_MANAGER_LOAD_OPTION        *BootOption
+  )
+{
+  EFI_STATUS                    Status;
+  UINT16                        BbsIndex;
+  UINT16                        HddCount;
+  UINT16                        BbsCount;
+  HDD_INFO                      *LocalHddInfo;
+  BBS_TABLE                     *LocalBbsTable;
+  UINT16                        DevType;
+  EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
+  UINTN                         Index;
+  UINT16                        Priority;
+  UINT16                        *DeviceType;
+  UINTN                         DeviceTypeCount;
+  UINTN                         DeviceTypeIndex;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *Option;
+  UINTN                         OptionCount;
+
+  HddCount      = 0;
+  BbsCount      = 0;
+  LocalHddInfo  = NULL;
+  LocalBbsTable = NULL;
+  DevType       = BBS_UNKNOWN;
+
+  Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = LegacyBios->GetBbsInfo (
+                         LegacyBios,
+                         &HddCount,
+                         &LocalHddInfo,
+                         &BbsCount,
+                         &LocalBbsTable
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // First, set all the present devices' boot priority to BBS_UNPRIORITIZED_ENTRY
+  // We will set them according to the settings setup by user
+  //
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (LegacyBmValidBbsEntry (&LocalBbsTable[Index])) {
+      LocalBbsTable[Index].BootPriority = BBS_UNPRIORITIZED_ENTRY;
+    }
+  }
+  //
+  // boot priority always starts at 0
+  //
+  Priority = 0;
+  if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) &&
+      (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
+    //
+    // If BootOption stands for a legacy boot option, we prioritize the devices with the same type first.
+    //
+    DevType  = LegacyBmDeviceType (BootOption->FilePath);
+    BbsIndex = ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOption->OptionalData)->BbsIndex;
+    Status = LegacyBmSetPriorityForSameTypeDev (
+               DevType,
+               BbsIndex,
+               LocalBbsTable,
+               &Priority
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+  //
+  // we have to set the boot priority for other BBS entries with different device types
+  //
+  Option          = EfiBootManagerGetLoadOptions (&OptionCount, LoadOptionTypeBoot);
+  DeviceType      = AllocatePool (sizeof (UINT16) * OptionCount);
+  ASSERT (DeviceType != NULL);
+  DeviceType[0]   = DevType;
+  DeviceTypeCount = 1;
+  for (Index = 0; Index < OptionCount; Index++) {
+    if ((DevicePathType (Option[Index].FilePath) != BBS_DEVICE_PATH) ||
+        (DevicePathSubType (Option[Index].FilePath) != BBS_BBS_DP)) {
+      continue;
+    }
+
+    DevType = LegacyBmDeviceType (Option[Index].FilePath);
+    for (DeviceTypeIndex = 0; DeviceTypeIndex < DeviceTypeCount; DeviceTypeIndex++) {
+      if (DeviceType[DeviceTypeIndex] == DevType) {
+        break;
+      }
+    }
+    if (DeviceTypeIndex < DeviceTypeCount) {
+      //
+      // We don't want to process twice for a device type
+      //
+      continue;
+    }
+
+    DeviceType[DeviceTypeCount] = DevType;
+    DeviceTypeCount++;
+
+    Status = LegacyBmSetPriorityForSameTypeDev (
+               DevType,
+               (UINTN) -1,
+               LocalBbsTable,
+               &Priority
+               );
+  }
+  EfiBootManagerFreeLoadOptions (Option, OptionCount);
+
+  DEBUG_CODE_BEGIN();
+    LegacyBmPrintBbsTable (LocalBbsTable, BbsCount);
+  DEBUG_CODE_END();
+
+  return Status;
+}
+
+
+/**
+  Boot the legacy system with the boot option.
+
+  @param  BootOption The legacy boot option which have BBS device path
+                     On return, BootOption->Status contains the boot status.
+                     EFI_UNSUPPORTED    There is no legacybios protocol, do not support
+                                        legacy boot.
+                     EFI_STATUS         The status of LegacyBios->LegacyBoot ().
+**/
+VOID
+EFIAPI
+LegacyBmBoot (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION           *BootOption
+  )
+{
+  EFI_STATUS                Status;
+  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    //
+    // If no LegacyBios protocol we do not support legacy boot
+    //
+    BootOption->Status = EFI_UNSUPPORTED;
+    return;
+  }
+  //
+  // Notes: if we separate the int 19, then we don't need to refresh BBS
+  //
+  Status = LegacyBmRefreshBbsTableForBoot (BootOption);
+  if (EFI_ERROR (Status)) {
+    BootOption->Status = Status;
+    return;
+  }
+
+  BootOption->Status = LegacyBios->LegacyBoot (
+                                     LegacyBios,
+                                     (BBS_BBS_DEVICE_PATH *) BootOption->FilePath,
+                                     BootOption->OptionalDataSize,
+                                     BootOption->OptionalData
+                                     );
+}
+
+/**
+  This function enumerates all the legacy boot options.
+
+  @param BootOptionCount   Return the legacy boot option count.
+
+  @retval    Pointer to the legacy boot option buffer.
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+LegacyBmEnumerateAllBootOptions (
+  UINTN                         *BootOptionCount
+  )
+{
+  EFI_STATUS                    Status;
+  UINT16                        HddCount;
+  UINT16                        BbsCount;
+  HDD_INFO                      *HddInfo;
+  BBS_TABLE                     *BbsTable;
+  EFI_LEGACY_BIOS_PROTOCOL      *LegacyBios;
+  UINT16                        Index;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions;
+
+  ASSERT (BootOptionCount != NULL);
+
+  BootOptions      = NULL;
+  *BootOptionCount = 0;
+  BbsCount         = 0;
+
+  Status        = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  Status = LegacyBios->GetBbsInfo (
+                         LegacyBios,
+                         &HddCount,
+                         &HddInfo,
+                         &BbsCount,
+                         &BbsTable
+                         );
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  for (Index = 0; Index < BbsCount; Index++) {
+    if (!LegacyBmValidBbsEntry (&BbsTable[Index])) {
+      continue;
+    }
+
+    BootOptions = ReallocatePool (
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+                    BootOptions
+                    );
+    ASSERT (BootOptions != NULL);
+
+    Status = LegacyBmCreateLegacyBootOption (&BootOptions[(*BootOptionCount)++], &BbsTable[Index], Index);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  return BootOptions;
+}
+
+/**
+  Return the index of the boot option in the boot option array.
+
+  The function compares the Description, FilePath, OptionalData.
+
+  @param Key         The input boot option which is compared with.
+  @param Array       The input boot option array.
+  @param Count       The count of the input boot options.
+
+  @retval  The index of the input boot option in the array.
+
+**/
+INTN
+LegacyBmFindBootOption (
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
+  IN UINTN                              Count
+  )
+{
+  UINTN                             Index;
+
+  for (Index = 0; Index < Count; Index++) {
+    if ((StrCmp (Key->Description, Array[Index].Description) == 0) &&
+        (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
+        (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
+        (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
+      return (INTN) Index;
+    }
+  }
+
+  return -1;
+}
+
+/**
+  Refresh all legacy boot options.
+
+**/
+VOID
+EFIAPI
+LegacyBmRefreshAllBootOption (
+  VOID
+  )
+{
+  EFI_STATUS                                 Status;
+  EFI_LEGACY_BIOS_PROTOCOL                   *LegacyBios;
+  UINTN                                      RootBridgeHandleCount;
+  EFI_HANDLE                                 *RootBridgeHandleBuffer;
+  UINTN                                      HandleCount;
+  EFI_HANDLE                                 *HandleBuffer;
+  UINTN                                      RootBridgeIndex;
+  UINTN                                      Index;
+  UINTN                                      Flags;
+  EFI_BOOT_MANAGER_LOAD_OPTION               *BootOptions;
+  UINTN                                      BootOptionCount;
+  EFI_BOOT_MANAGER_LOAD_OPTION               *ExistingBootOptions;
+  UINTN                                      ExistingBootOptionCount;
+
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
+  if (EFI_ERROR (Status)) {
+    LegacyBmDeleteAllBootOptions ();
+    return;
+  }
+  PERF_START (NULL, "LegacyBootOptionEnum", "BDS", 0);
+
+  //
+  // Before enumerating the legacy boot option, we need to dispatch all the legacy option roms
+  // to ensure the GetBbsInfo() counts all the legacy devices.
+  //
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiPciRootBridgeIoProtocolGuid,
+         NULL,
+         &RootBridgeHandleCount,
+         &RootBridgeHandleBuffer
+         );
+  for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) {
+    gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE);
+    gBS->LocateHandleBuffer (
+           ByProtocol,
+           &gEfiPciIoProtocolGuid,
+           NULL,
+           &HandleCount,
+           &HandleBuffer
+           );
+    for (Index = 0; Index < HandleCount; Index++) {
+      //
+      // Start the thunk driver so that the legacy option rom gets dispatched.
+      // Note: We don't directly call InstallPciRom because some thunk drivers
+      // (e.g. BlockIo thunk driver) depend on the immediate result after dispatching
+      //
+      Status = LegacyBios->CheckPciRom (
+                             LegacyBios,
+                             HandleBuffer[Index],
+                             NULL,
+                             NULL,
+                             &Flags
+                             );
+      if (!EFI_ERROR (Status)) {
+        gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE);
+      }
+    }
+  }
+
+  //
+  // Same algorithm pattern as the EfiBootManagerRefreshAllBootOption
+  // Firstly delete the invalid legacy boot options,
+  // then enumreate and save the newly appeared legacy boot options
+  // the last step is legacy boot option special action to refresh the LegacyDevOrder variable
+  //
+  LegacyBmDeleteAllInvalidBootOptions ();
+
+  ExistingBootOptions = EfiBootManagerGetLoadOptions (&ExistingBootOptionCount, LoadOptionTypeBoot);
+  BootOptions         = LegacyBmEnumerateAllBootOptions   (&BootOptionCount);
+
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    if (LegacyBmFindBootOption (&BootOptions[Index], ExistingBootOptions, ExistingBootOptionCount) == -1) {
+      Status = EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);
+      DEBUG ((
+        EFI_D_INFO, "[LegacyBds] New Boot Option: Boot%04x Bbs0x%04x %s %r\n",
+        (UINTN) BootOptions[Index].OptionNumber,
+        (UINTN) ((LEGACY_BM_BOOT_OPTION_BBS_DATA *) BootOptions[Index].OptionalData)->BbsIndex,
+        BootOptions[Index].Description,
+        Status
+        ));
+      //
+      // Continue upon failure to add boot option.
+      //
+    }
+  }
+
+  EfiBootManagerFreeLoadOptions (ExistingBootOptions, ExistingBootOptionCount);
+  EfiBootManagerFreeLoadOptions (BootOptions,         BootOptionCount);
+
+  //
+  // Failure to create LegacyDevOrder variable only impacts the boot order.
+  //
+  LegacyBmUpdateDevOrder ();
+
+  PERF_END   (NULL, "LegacyBootOptionEnum", "BDS", 0);
+}
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxe.uni b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxe.uni
new file mode 100644
index 0000000000..326d9de822
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxe.uni
@@ -0,0 +1,17 @@
+// /** @file
+// Video driver based on legacy bios.
+//
+// This driver by using Legacy Bios protocol service to support csm Video
+// and produce Graphics Output Protocol.
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "Video driver based on legacy bios"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "This driver uses the Legacy Bios protocol service to utilize the CSM Video Support and produce the Graphics Output Protocol."
+
diff --git a/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxeExtra.uni b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxeExtra.uni
new file mode 100644
index 0000000000..23fb8fe0b0
--- /dev/null
+++ b/OvmfPkg/Csm/BiosThunk/VideoDxe/BiosVideoDxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// BiosVideoDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Legacy Video DXE Driver"
+
+
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/IA32/InterruptTable.nasm b/OvmfPkg/Csm/LegacyBiosDxe/IA32/InterruptTable.nasm
new file mode 100644
index 0000000000..90bfdffb0b
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/IA32/InterruptTable.nasm
@@ -0,0 +1,63 @@
+;; @file
+;  Interrupt Redirection Template
+;
+; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;;
+
+SECTION .text
+
+;----------------------------------------------------------------------------
+; Procedure:    InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F
+;
+; Input:        None
+;
+; Output:       None
+;
+; Prototype:    VOID
+;               InterruptRedirectionTemplate (
+;                                VOID
+;                                );
+;
+; Saves:        None
+;
+; Modified:     None
+;
+; Description:  Contains the code that is copied into low memory (below 640K).
+;               This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f.
+;               This template must be copied into low memory, and the IDT entries
+;               0x68-0x6F must be point to the low memory copy of this code.  Each
+;               entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily
+;               computed.
+;
+;----------------------------------------------------------------------------
+
+global ASM_PFX(InterruptRedirectionTemplate)
+ASM_PFX(InterruptRedirectionTemplate):
+  int     0x8
+  DB      0xcf          ; IRET
+  nop
+  int     0x9
+  DB      0xcf          ; IRET
+  nop
+  int     0xa
+  DB      0xcf          ; IRET
+  nop
+  int     0xb
+  DB      0xcf          ; IRET
+  nop
+  int     0xc
+  DB      0xcf          ; IRET
+  nop
+  int     0xd
+  DB      0xcf          ; IRET
+  nop
+  int     0xe
+  DB      0xcf          ; IRET
+  nop
+  int     0xf
+  DB      0xcf          ; IRET
+  nop
+
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.uni b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.uni
new file mode 100644
index 0000000000..1a41d6d856
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxe.uni
@@ -0,0 +1,16 @@
+// /** @file
+// Legacy Bios Module to support CSM.
+//
+// This driver installs Legacy Bios Protocol to support CSM module work in EFI system.
+//
+// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "Legacy Bios Module to support CSM"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "This driver installs Legacy Bios Protocol to support CSM module work in a EFI system."
+
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxeExtra.uni b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxeExtra.uni
new file mode 100644
index 0000000000..a02e783d94
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBiosDxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// LegacyBiosDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Legacy BIOS Support DXE Driver"
+
+
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/X64/InterruptTable.nasm b/OvmfPkg/Csm/LegacyBiosDxe/X64/InterruptTable.nasm
new file mode 100644
index 0000000000..afc2f0e639
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/X64/InterruptTable.nasm
@@ -0,0 +1,64 @@
+;; @file
+;  Interrupt Redirection Template
+;
+; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;;
+
+    DEFAULT REL
+    SECTION .text
+
+;----------------------------------------------------------------------------
+; Procedure:    InterruptRedirectionTemplate: Redirects interrupts 0x68-0x6F
+;
+; Input:        None
+;
+; Output:       None
+;
+; Prototype:    VOID
+;               InterruptRedirectionTemplate (
+;                                VOID
+;                                );
+;
+; Saves:        None
+;
+; Modified:     None
+;
+; Description:  Contains the code that is copied into low memory (below 640K).
+;               This code reflects interrupts 0x68-0x6f to interrupts 0x08-0x0f.
+;               This template must be copied into low memory, and the IDT entries
+;               0x68-0x6F must be point to the low memory copy of this code.  Each
+;               entry is 4 bytes long, so IDT entries 0x68-0x6F can be easily
+;               computed.
+;
+;----------------------------------------------------------------------------
+
+global ASM_PFX(InterruptRedirectionTemplate)
+ASM_PFX(InterruptRedirectionTemplate):
+  int     0x8
+  DB      0xcf          ; IRET
+  nop
+  int     0x9
+  DB      0xcf          ; IRET
+  nop
+  int     0xa
+  DB      0xcf          ; IRET
+  nop
+  int     0xb
+  DB      0xcf          ; IRET
+  nop
+  int     0xc
+  DB      0xcf          ; IRET
+  nop
+  int     0xd
+  DB      0xcf          ; IRET
+  nop
+  int     0xe
+  DB      0xcf          ; IRET
+  nop
+  int     0xf
+  DB      0xcf          ; IRET
+  nop
+
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.uni b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.uni
new file mode 100644
index 0000000000..c2c100d378
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Legacy Boot Maintainence UI module is library for BDS phase.
+//
+// Legacy Boot Maintainence UI module is library for BDS phase.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT
+#language en-US
+"Legacy Boot Maintainence UI module is library for BDS phase."
+
+#string STR_MODULE_DESCRIPTION
+#language en-US
+"Legacy Boot Maintainence UI module is library for BDS phase."
+
+
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiStrings.uni b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiStrings.uni
new file mode 100644
index 0000000000..8d40ca1af1
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiStrings.uni
@@ -0,0 +1,43 @@
+///** @file
+//
+//    String definitions for Legacy Boot Maintainece Ui.
+//
+//  Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+//  SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+//**/
+
+/=#
+
+#langdef   en-US "English"
+#langdef   fr-FR "Français"
+
+#string STR_LEGACY_BOOT_PROMPT         #language en-US  "Legacy Boot Options Menu"
+                                       #language fr-FR  "Legacy Boot Options Menu"
+#string STR_LEGACY_BOOT_HELP           #language en-US  "Manager legacy boot options in this driver."
+                                       #language fr-FR  "Manager legacy boot options in this driver."
+#string STR_FORM_FLOPPY_BOOT_TITLE     #language en-US  "Set Legacy Floppy Driver Order"
+                                       #language fr-FR  "Set Legacy Floppy Driver Order"
+#string STR_FORM_FLOPPY_BOOT_HELP      #language en-US  "Set Legacy Floppy Driver Order."
+                                       #language fr-FR  "Set Legacy Floppy Driver Order."
+#string STR_FORM_HARDDISK_BOOT_TITLE   #language en-US  "Set Legacy HARDDISK Driver Order"
+                                       #language fr-FR  "Set Legacy HARDDISK Driver Order"
+#string STR_FORM_HARDDISK_BOOT_HELP    #language en-US  "Set Legacy HARDDISK Driver Order."
+                                       #language fr-FR  "Set Legacy HARDDISK Driver Order."
+#string STR_FORM_CDROM_BOOT_TITLE      #language en-US  "Set Legacy CDROM Driver Order"
+                                       #language fr-FR  "Set Legacy CDROM Driver Order"
+#string STR_FORM_CDROM_BOOT_HELP       #language en-US  "Set Legacy CDROM Driver Order."
+                                       #language fr-FR  "Set Legacy CDROM Driver Order."
+#string STR_FORM_NET_BOOT_TITLE        #language en-US  "Set Legacy NET Driver Order"
+                                       #language fr-FR  "Set Legacy NET Driver Order"
+#string STR_FORM_NET_BOOT_HELP         #language en-US  "Set Legacy NET Driver Order."
+                                       #language fr-FR  "Set Legacy NET Driver Order."
+#string STR_FORM_BEV_BOOT_TITLE        #language en-US  "Set Legacy BEV Driver Order"
+                                       #language fr-FR  "Set Legacy BEV Driver Order"
+#string STR_FORM_BEV_BOOT_HELP         #language en-US  "Set Legacy BEV Driver Order."
+                                       #language fr-FR  "Set Legacy BEV Driver Order."
+#string STR_ORDER_CHANGE_PROMPT        #language en-US  "Change Driver Boot Order."
+                                       #language fr-FR  "Change Driver Boot Order."
+#string STR_DISABLE_LEGACY_DEVICE      #language en-US  "Disabled"
+                                       #language fr-FR  "Disabled"
+
diff --git a/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.Vfr b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.Vfr
new file mode 100644
index 0000000000..c879ca551f
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiVfr.Vfr
@@ -0,0 +1,67 @@
+///** @file
+//  
+//    Browser formset.
+//  
+//  Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//  SPDX-License-Identifier: BSD-2-Clause-Patent
+//  
+//**/
+
+#include "LegacyBootMaintUiVfr.h"
+
+
+formset
+  guid      = LEGACY_BOOT_OPTION_FORMSET_GUID,
+  title     = STRING_TOKEN(STR_LEGACY_BOOT_PROMPT),
+  help      = STRING_TOKEN(STR_LEGACY_BOOT_HELP),
+  classguid = EFI_IFR_BOOT_MAINTENANCE_GUID,
+  
+  varstore LEGACY_BOOT_NV_DATA,
+    varid = VARSTORE_ID_LEGACY_BOOT,
+    name = LegacyBootData,
+    guid = LEGACY_BOOT_OPTION_FORMSET_GUID;
+    
+  form formid = LEGACY_BOOT_FORM_ID,
+       title  = STRING_TOKEN(STR_LEGACY_BOOT_PROMPT);
+       
+    goto LEGACY_ORDER_CHANGE_FORM_ID,
+         prompt = STRING_TOKEN(STR_FORM_FLOPPY_BOOT_TITLE),
+         help = STRING_TOKEN(STR_FORM_FLOPPY_BOOT_HELP),
+         flags = INTERACTIVE,
+         key = FORM_FLOPPY_BOOT_ID;
+
+    goto LEGACY_ORDER_CHANGE_FORM_ID,
+         prompt = STRING_TOKEN(STR_FORM_HARDDISK_BOOT_TITLE),
+         help = STRING_TOKEN(STR_FORM_HARDDISK_BOOT_HELP),
+         flags = INTERACTIVE,
+         key = FORM_HARDDISK_BOOT_ID;
+
+    goto LEGACY_ORDER_CHANGE_FORM_ID,
+         prompt = STRING_TOKEN(STR_FORM_CDROM_BOOT_TITLE),
+         help = STRING_TOKEN(STR_FORM_CDROM_BOOT_HELP),
+         flags = INTERACTIVE,
+         key = FORM_CDROM_BOOT_ID;
+
+    goto LEGACY_ORDER_CHANGE_FORM_ID,
+         prompt = STRING_TOKEN(STR_FORM_NET_BOOT_TITLE),
+         help = STRING_TOKEN(STR_FORM_NET_BOOT_HELP),
+         flags = INTERACTIVE,
+         key = FORM_NET_BOOT_ID;
+
+    goto LEGACY_ORDER_CHANGE_FORM_ID,
+         prompt = STRING_TOKEN(STR_FORM_BEV_BOOT_TITLE),
+         help = STRING_TOKEN(STR_FORM_BEV_BOOT_HELP),
+         flags = INTERACTIVE,
+         key = FORM_BEV_BOOT_ID;
+         
+  endform;
+
+  form formid = LEGACY_ORDER_CHANGE_FORM_ID,
+       title  = STRING_TOKEN(STR_ORDER_CHANGE_PROMPT);
+       
+       label FORM_BOOT_LEGACY_DEVICE_ID;
+       label FORM_BOOT_LEGACY_LABEL_END;
+         
+  endform;
+
+endformset;
diff --git a/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.uni b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.uni
new file mode 100644
index 0000000000..da2915a9a3
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBootManagerLib/LegacyBootManagerLib.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Legacy Boot Manager module is library for BDS phase.
+//
+// Legacy Boot Manager module is library for BDS phase.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT
+#language en-US
+"Legacy Boot Manager module is library for BDS phase."
+
+#string STR_MODULE_DESCRIPTION
+#language en-US
+"Legacy Boot Manager module is library for BDS phase."
+
+
-- 
2.12.0.windows.1


  parent reply	other threads:[~2019-05-27  3:04 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-27  3:03 [PATCH v2 00/10] Duplicate required CSM components for OVMF Wu, Hao A
2019-05-27  3:03 ` [PATCH v2 01/10] Maintainers.txt: Add maintainer for CSM components in OvmfPkg Wu, Hao A
2019-06-13 13:26   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` Wu, Hao A [this message]
2019-06-13 13:40   ` [edk2-devel] [PATCH v2 02/10] OvmfPkg: Copy the required CSM components from framework packages Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 03/10] OvmfPkg/OvmfPkg.dec: Add definitions for CSM-related Guid & Protocol Wu, Hao A
2019-06-13 13:49   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 04/10] OvmfPkg/OvmfPkg.dec: Add the new include folder for CSM header files Wu, Hao A
2019-06-13 13:49   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 05/10] OvmfPkg/OvmfPkg.dec: Add PCD definitions used by copied CSM modules Wu, Hao A
2019-06-13 14:09   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 06/10] OvmfPkg/Csm/VideoDxe: Update to make it build for OVMF Wu, Hao A
2019-06-13 14:10   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 07/10] OvmfPkg/Csm/LegacyBiosDxe: " Wu, Hao A
2019-06-13 14:15   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 08/10] OvmfPkg/Csm/LegacyBootMaintUiLib: " Wu, Hao A
2019-06-13 14:16   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 09/10] OvmfPkg/Csm/LegacyBootManagerLib: " Wu, Hao A
2019-06-13 14:17   ` [edk2-devel] " Laszlo Ersek
2019-05-27  3:03 ` [PATCH v2 10/10] OvmfPkg: Update DSC/FDF files to consume CSM components in OvmfPkg Wu, Hao A
2019-06-13 14:22   ` [edk2-devel] " Laszlo Ersek
2019-05-28 11:48 ` [edk2-devel] [PATCH v2 00/10] Duplicate required CSM components for OVMF Laszlo Ersek
2019-05-29  1:12   ` Wu, Hao A
2019-06-03  9:29     ` Laszlo Ersek
2019-06-12 21:40 ` David Woodhouse
2019-06-14  5:08   ` [edk2-devel] " Wu, Hao A
2019-06-12 21:43 ` [PATCH 11/10] OvmfPkg/Csm/LegacyBiosDxe: Correct Legacy16GetTableAddress call for E820 data David Woodhouse
2019-06-13  6:28   ` [edk2-devel] " Wu, Hao A
2019-06-13  7:10     ` Ni, Ray
2019-06-13  7:40     ` Jordan Justen
2019-06-13  8:00       ` David Woodhouse
2019-06-13  8:34     ` David Woodhouse
2019-06-13 12:45       ` Laszlo Ersek
2019-06-13  8:40   ` [PATCH v2 11/10] OvmfPkg/Csm/LegacyBiosDxe: Fix " David Woodhouse
2019-06-13 14:23     ` [edk2-devel] " Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190527030350.11996-3-hao.a.wu@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox