]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
OvmfPkg: Copy the required CSM components from framework packages
[mirror_edk2.git] / OvmfPkg / Csm / LegacyBiosDxe / LegacyBios.c
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
new file mode 100644 (file)
index 0000000..05e3ffd
--- /dev/null
@@ -0,0 +1,1214 @@
+/** @file\r
+\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "LegacyBiosInterface.h"\r
+\r
+#define PHYSICAL_ADDRESS_TO_POINTER(Address)  ((VOID *) ((UINTN) Address))\r
+\r
+//\r
+// define maximum number of HDD system supports\r
+//\r
+#define MAX_HDD_ENTRIES 0x30\r
+\r
+//\r
+// Module Global:\r
+//  Since this driver will only ever produce one instance of the Private Data\r
+//  protocol you are not required to dynamically allocate the PrivateData.\r
+//\r
+LEGACY_BIOS_INSTANCE  mPrivateData;\r
+\r
+//\r
+// The SMBIOS table in EfiRuntimeServicesData memory\r
+//\r
+VOID                  *mRuntimeSmbiosEntryPoint = NULL;\r
+\r
+//\r
+// The SMBIOS table in EfiReservedMemoryType memory\r
+//\r
+EFI_PHYSICAL_ADDRESS  mReserveSmbiosEntryPoint = 0;\r
+EFI_PHYSICAL_ADDRESS  mStructureTableAddress   = 0;\r
+UINTN                 mStructureTablePages     = 0;\r
+BOOLEAN               mEndOfDxe                = FALSE;\r
+\r
+/**\r
+  Allocate memory for legacy usage. The memory is executable.\r
+\r
+  @param  AllocateType               The type of allocation to perform.\r
+  @param  MemoryType                 The type of memory to allocate.\r
+  @param  StartPageAddress           Start address of range\r
+  @param  Pages                      Number of pages to allocate\r
+  @param  Result                     Result of allocation\r
+\r
+  @retval EFI_SUCCESS                Legacy memory is allocated successfully.\r
+  @retval Other                      Legacy memory is not allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+AllocateLegacyMemory (\r
+  IN  EFI_ALLOCATE_TYPE         AllocateType,\r
+  IN  EFI_MEMORY_TYPE           MemoryType,\r
+  IN  EFI_PHYSICAL_ADDRESS      StartPageAddress,\r
+  IN  UINTN                     Pages,\r
+  OUT EFI_PHYSICAL_ADDRESS      *Result\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  EFI_PHYSICAL_ADDRESS            MemPage;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;\r
+\r
+  //\r
+  // Allocate Pages of memory less <= StartPageAddress\r
+  //\r
+  MemPage = (EFI_PHYSICAL_ADDRESS) (UINTN) StartPageAddress;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateType,\r
+                  MemoryType,\r
+                  Pages,\r
+                  &MemPage\r
+                  );\r
+  //\r
+  // Do not ASSERT on Status error but let caller decide since some cases\r
+  // memory is already taken but that is ok.\r
+  //\r
+  if (!EFI_ERROR (Status)) {\r
+    if (MemoryType != EfiBootServicesCode) {\r
+      //\r
+      // Make sure that the buffer can be used to store code.\r
+      //\r
+      Status = gDS->GetMemorySpaceDescriptor (MemPage, &MemDesc);\r
+      if (!EFI_ERROR (Status) && (MemDesc.Attributes & EFI_MEMORY_XP) != 0) {\r
+        Status = gDS->SetMemorySpaceAttributes (\r
+                        MemPage,\r
+                        EFI_PAGES_TO_SIZE (Pages),\r
+                        MemDesc.Attributes & (~EFI_MEMORY_XP)\r
+                        );\r
+      }\r
+      if (EFI_ERROR (Status)) {\r
+        gBS->FreePages (MemPage, Pages);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    *Result = (EFI_PHYSICAL_ADDRESS) (UINTN) MemPage;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000\r
+  64 KB blocks.\r
+\r
+  Note: inconsistency with the Framework CSM spec. Per the spec, this function may be\r
+  invoked only once. This limitation is relaxed to allow multiple calls in this implemenation.\r
+\r
+  @param  This                       Protocol instance pointer.\r
+  @param  LegacyMemorySize           Size of required region\r
+  @param  Region                     Region to use. 00 = Either 0xE0000 or 0xF0000\r
+                                     block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000\r
+                                     block\r
+  @param  Alignment                  Address alignment. Bit mapped. First non-zero\r
+                                     bit from right is alignment.\r
+  @param  LegacyMemoryAddress        Region Assigned\r
+\r
+  @retval EFI_SUCCESS                Region assigned\r
+  @retval EFI_ACCESS_DENIED          Procedure previously invoked\r
+  @retval Other                      Region not assigned\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LegacyBiosGetLegacyRegion (\r
+  IN    EFI_LEGACY_BIOS_PROTOCOL *This,\r
+  IN    UINTN                    LegacyMemorySize,\r
+  IN    UINTN                    Region,\r
+  IN    UINTN                    Alignment,\r
+  OUT   VOID                     **LegacyMemoryAddress\r
+  )\r
+{\r
+\r
+  LEGACY_BIOS_INSTANCE  *Private;\r
+  EFI_IA32_REGISTER_SET Regs;\r
+  EFI_STATUS            Status;\r
+  UINT32                Granularity;\r
+\r
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);\r
+\r
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));\r
+  Regs.X.AX = Legacy16GetTableAddress;\r
+  Regs.X.BX = (UINT16) Region;\r
+  Regs.X.CX = (UINT16) LegacyMemorySize;\r
+  Regs.X.DX = (UINT16) Alignment;\r
+  Private->LegacyBios.FarCall86 (\r
+     &Private->LegacyBios,\r
+     Private->Legacy16CallSegment,\r
+     Private->Legacy16CallOffset,\r
+     &Regs,\r
+     NULL,\r
+     0\r
+     );\r
+\r
+  if (Regs.X.AX == 0) {\r
+    *LegacyMemoryAddress  = (VOID *) (((UINTN) Regs.X.DS << 4) + Regs.X.BX);\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);\r
+  Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function is called when copying data to the region assigned by\r
+  EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion().\r
+\r
+  @param  This                       Protocol instance pointer.\r
+  @param  LegacyMemorySize           Size of data to copy\r
+  @param  LegacyMemoryAddress        Legacy Region destination address Note: must\r
+                                     be in region assigned by\r
+                                     LegacyBiosGetLegacyRegion\r
+  @param  LegacyMemorySourceAddress  Source of data\r
+\r
+  @retval EFI_SUCCESS                The data was copied successfully.\r
+  @retval EFI_ACCESS_DENIED          Either the starting or ending address is out of bounds.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LegacyBiosCopyLegacyRegion (\r
+  IN EFI_LEGACY_BIOS_PROTOCOL *This,\r
+  IN    UINTN                 LegacyMemorySize,\r
+  IN    VOID                  *LegacyMemoryAddress,\r
+  IN    VOID                  *LegacyMemorySourceAddress\r
+  )\r
+{\r
+\r
+  LEGACY_BIOS_INSTANCE  *Private;\r
+  UINT32                Granularity;\r
+\r
+  if ((LegacyMemoryAddress < (VOID *)(UINTN)0xE0000 ) ||\r
+      ((UINTN) LegacyMemoryAddress + LegacyMemorySize > (UINTN) 0x100000)\r
+        ) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+  //\r
+  // There is no protection from writes over lapping if this function is\r
+  // called multiple times.\r
+  //\r
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);\r
+  CopyMem (LegacyMemoryAddress, LegacyMemorySourceAddress, LegacyMemorySize);\r
+\r
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);\r
+  Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find\r
+  the $EFI table in the shadow area. Thunk into the Legacy16 code after it had\r
+  been shadowed.\r
+\r
+  @param  Private                    Legacy BIOS context data\r
+\r
+  @retval EFI_SUCCESS                Legacy16 code loaded\r
+  @retval Other                      No protocol installed, unload driver.\r
+\r
+**/\r
+EFI_STATUS\r
+ShadowAndStartLegacy16 (\r
+  IN  LEGACY_BIOS_INSTANCE  *Private\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  UINT8                             *Ptr;\r
+  UINT8                             *PtrEnd;\r
+  BOOLEAN                           Done;\r
+  EFI_COMPATIBILITY16_TABLE         *Table;\r
+  UINT8                             CheckSum;\r
+  EFI_IA32_REGISTER_SET             Regs;\r
+  EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable;\r
+  EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;\r
+  VOID                              *LegacyBiosImage;\r
+  UINTN                             LegacyBiosImageSize;\r
+  UINTN                             E820Size;\r
+  UINT32                            *ClearPtr;\r
+  BBS_TABLE                         *BbsTable;\r
+  LEGACY_EFI_HDD_TABLE              *LegacyEfiHddTable;\r
+  UINTN                             Index;\r
+  UINT32                            TpmPointer;\r
+  VOID                              *TpmBinaryImage;\r
+  UINTN                             TpmBinaryImageSize;\r
+  UINTN                             Location;\r
+  UINTN                             Alignment;\r
+  UINTN                             TempData;\r
+  EFI_PHYSICAL_ADDRESS              Address;\r
+  UINT16                            OldMask;\r
+  UINT16                            NewMask;\r
+  UINT32                            Granularity;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;\r
+\r
+  Location  = 0;\r
+  Alignment = 0;\r
+\r
+  //\r
+  // we allocate the C/D/E/F segment as RT code so no one will use it any more.\r
+  //\r
+  Address = 0xC0000;\r
+  gDS->GetMemorySpaceDescriptor (Address, &Descriptor);\r
+  if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {\r
+    //\r
+    // If it is already reserved, we should be safe, or else we allocate it.\r
+    //\r
+    Status = gBS->AllocatePages (\r
+                    AllocateAddress,\r
+                    EfiRuntimeServicesCode,\r
+                    0x40000/EFI_PAGE_SIZE,\r
+                    &Address\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory.\r
+      //\r
+      DEBUG ((DEBUG_ERROR, "Failed to allocate the C/D/E/F segment Status = %r", Status));\r
+    }\r
+  }\r
+\r
+  //\r
+  // start testtest\r
+  //    GetTimerValue (&Ticker);\r
+  //\r
+  //  gRT->SetVariable (L"StartLegacy",\r
+  //                    &gEfiGlobalVariableGuid,\r
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+  //                    sizeof (UINT64),\r
+  //                    (VOID *)&Ticker\r
+  //                    );\r
+  // end testtest\r
+  //\r
+  EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;\r
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (\r
+                                          Private->LegacyBiosPlatform,\r
+                                          EfiGetPlatformBinarySystemRom,\r
+                                          &LegacyBiosImage,\r
+                                          &LegacyBiosImageSize,\r
+                                          &Location,\r
+                                          &Alignment,\r
+                                          0,\r
+                                          0\r
+                                          );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Private->BiosStart            = (UINT32) (0x100000 - LegacyBiosImageSize);\r
+  Private->OptionRom            = 0xc0000;\r
+  Private->LegacyBiosImageSize  = (UINT32) LegacyBiosImageSize;\r
+\r
+  //\r
+  // Can only shadow into memory allocated for legacy useage.\r
+  //\r
+  ASSERT (Private->BiosStart > Private->OptionRom);\r
+\r
+  //\r
+  // Shadow Legacy BIOS. Turn on memory and copy image\r
+  //\r
+  Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity);\r
+\r
+  ClearPtr = (VOID *) ((UINTN) 0xc0000);\r
+\r
+  //\r
+  // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused\r
+  // regions to be used by EMM386 etc.\r
+  //\r
+  SetMem ((VOID *) ClearPtr, (UINTN) (0x40000 - LegacyBiosImageSize), 0xff);\r
+\r
+  TempData = Private->BiosStart;\r
+\r
+  CopyMem (\r
+    (VOID *) TempData,\r
+    LegacyBiosImage,\r
+    (UINTN) LegacyBiosImageSize\r
+    );\r
+\r
+  Private->Cpu->FlushDataCache (Private->Cpu, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate);\r
+\r
+  //\r
+  // Search for Legacy16 table in Shadowed ROM\r
+  //\r
+  Done  = FALSE;\r
+  Table = NULL;\r
+  for (Ptr = (UINT8 *) TempData; Ptr < (UINT8 *) ((UINTN) 0x100000) && !Done; Ptr += 0x10) {\r
+    if (*(UINT32 *) Ptr == SIGNATURE_32 ('I', 'F', 'E', '$')) {\r
+      Table   = (EFI_COMPATIBILITY16_TABLE *) Ptr;\r
+      PtrEnd  = Ptr + Table->TableLength;\r
+      for (CheckSum = 0; Ptr < PtrEnd; Ptr++) {\r
+        CheckSum = (UINT8) (CheckSum +*Ptr);\r
+      }\r
+\r
+      Done = TRUE;\r
+    }\r
+  }\r
+\r
+  if (Table == NULL) {\r
+    DEBUG ((EFI_D_ERROR, "No Legacy16 table found\n"));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (!Done) {\r
+    //\r
+    // Legacy16 table header checksum error.\r
+    //\r
+    DEBUG ((EFI_D_ERROR, "Legacy16 table found with bad talbe header checksum\n"));\r
+  }\r
+\r
+  //\r
+  // Remember location of the Legacy16 table\r
+  //\r
+  Private->Legacy16Table            = Table;\r
+  Private->Legacy16CallSegment      = Table->Compatibility16CallSegment;\r
+  Private->Legacy16CallOffset       = Table->Compatibility16CallOffset;\r
+  EfiToLegacy16InitTable            = &Private->IntThunk->EfiToLegacy16InitTable;\r
+  Private->Legacy16InitPtr          = EfiToLegacy16InitTable;\r
+  Private->Legacy16BootPtr          = &Private->IntThunk->EfiToLegacy16BootTable;\r
+  Private->InternalIrqRoutingTable  = NULL;\r
+  Private->NumberIrqRoutingEntries  = 0;\r
+  Private->BbsTablePtr              = NULL;\r
+  Private->LegacyEfiHddTable        = NULL;\r
+  Private->DiskEnd                  = 0;\r
+  Private->Disk4075                 = 0;\r
+  Private->HddTablePtr              = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo;\r
+  Private->NumberHddControllers     = MAX_IDE_CONTROLLER;\r
+  Private->Dump[0]                  = 'D';\r
+  Private->Dump[1]                  = 'U';\r
+  Private->Dump[2]                  = 'M';\r
+  Private->Dump[3]                  = 'P';\r
+\r
+  ZeroMem (\r
+    Private->Legacy16BootPtr,\r
+    sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE)\r
+    );\r
+\r
+  //\r
+  // Store away a copy of the EFI System Table\r
+  //\r
+  Table->EfiSystemTable = (UINT32) (UINTN) gST;\r
+\r
+  //\r
+  // IPF CSM integration -Bug\r
+  //\r
+  // Construct the Legacy16 boot memory map. This sets up number of\r
+  // E820 entries.\r
+  //\r
+  LegacyBiosBuildE820 (Private, &E820Size);\r
+  //\r
+  // Initialize BDA and EBDA standard values needed to load Legacy16 code\r
+  //\r
+  LegacyBiosInitBda (Private);\r
+  LegacyBiosInitCmos (Private);\r
+\r
+  //\r
+  // All legacy interrupt should be masked when do initialization work from legacy 16 code.\r
+  //\r
+  Private->Legacy8259->GetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL);\r
+  NewMask = 0xFFFF;\r
+  Private->Legacy8259->SetMask(Private->Legacy8259, &NewMask, NULL, NULL, NULL);\r
+\r
+  //\r
+  // Call into Legacy16 code to do an INIT\r
+  //\r
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));\r
+  Regs.X.AX = Legacy16InitializeYourself;\r
+  Regs.X.ES = EFI_SEGMENT (*((UINT32 *) &EfiToLegacy16InitTable));\r
+  Regs.X.BX = EFI_OFFSET (*((UINT32 *) &EfiToLegacy16InitTable));\r
+\r
+  Private->LegacyBios.FarCall86 (\r
+    &Private->LegacyBios,\r
+    Table->Compatibility16CallSegment,\r
+    Table->Compatibility16CallOffset,\r
+    &Regs,\r
+    NULL,\r
+    0\r
+    );\r
+\r
+  //\r
+  // Restore original legacy interrupt mask value\r
+  //\r
+  Private->Legacy8259->SetMask(Private->Legacy8259, &OldMask, NULL, NULL, NULL);\r
+\r
+  if (Regs.X.AX != 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // start testtest\r
+  //  GetTimerValue (&Ticker);\r
+  //\r
+  //  gRT->SetVariable (L"BackFromInitYourself",\r
+  //                    &gEfiGlobalVariableGuid,\r
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+  //                    sizeof (UINT64),\r
+  //                    (VOID *)&Ticker\r
+  //                    );\r
+  // end testtest\r
+  //\r
+  // Copy E820 table after InitializeYourself is completed\r
+  //\r
+  ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));\r
+  Regs.X.AX = Legacy16GetTableAddress;\r
+  Regs.X.CX = (UINT16) E820Size;\r
+  Regs.X.DX = 1;\r
+  Private->LegacyBios.FarCall86 (\r
+    &Private->LegacyBios,\r
+    Table->Compatibility16CallSegment,\r
+    Table->Compatibility16CallOffset,\r
+    &Regs,\r
+    NULL,\r
+    0\r
+    );\r
+\r
+  Table->E820Pointer  = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);\r
+  Table->E820Length   = (UINT32) E820Size;\r
+  if (Regs.X.AX != 0) {\r
+    DEBUG ((EFI_D_ERROR, "Legacy16 E820 length insufficient\n"));\r
+  } else {\r
+    TempData = Table->E820Pointer;\r
+    CopyMem ((VOID *) TempData, Private->E820Table, E820Size);\r
+  }\r
+  //\r
+  // Get PnPInstallationCheck Info.\r
+  //\r
+  Private->PnPInstallationCheckSegment  = Table->PnPInstallationCheckSegment;\r
+  Private->PnPInstallationCheckOffset   = Table->PnPInstallationCheckOffset;\r
+\r
+  //\r
+  // Check if PCI Express is supported. If yes, Save base address.\r
+  //\r
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (\r
+                                          Private->LegacyBiosPlatform,\r
+                                          EfiGetPlatformPciExpressBase,\r
+                                          NULL,\r
+                                          NULL,\r
+                                          &Location,\r
+                                          &Alignment,\r
+                                          0,\r
+                                          0\r
+                                          );\r
+  if (!EFI_ERROR (Status)) {\r
+    Private->Legacy16Table->PciExpressBase  = (UINT32)Location;\r
+    Location = 0;\r
+  }\r
+  //\r
+  // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it\r
+  // into, copy it and update pointer to binary image. This needs to be\r
+  // done prior to any OPROM for security purposes.\r
+  //\r
+  Status = Private->LegacyBiosPlatform->GetPlatformInfo (\r
+                                          Private->LegacyBiosPlatform,\r
+                                          EfiGetPlatformBinaryTpmBinary,\r
+                                          &TpmBinaryImage,\r
+                                          &TpmBinaryImageSize,\r
+                                          &Location,\r
+                                          &Alignment,\r
+                                          0,\r
+                                          0\r
+                                          );\r
+  if (!EFI_ERROR (Status)) {\r
+\r
+    ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));\r
+    Regs.X.AX = Legacy16GetTableAddress;\r
+    Regs.X.CX = (UINT16) TpmBinaryImageSize;\r
+    Regs.X.DX = 1;\r
+    Private->LegacyBios.FarCall86 (\r
+      &Private->LegacyBios,\r
+      Table->Compatibility16CallSegment,\r
+      Table->Compatibility16CallOffset,\r
+      &Regs,\r
+      NULL,\r
+      0\r
+      );\r
+\r
+    TpmPointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX);\r
+    if (Regs.X.AX != 0) {\r
+      DEBUG ((EFI_D_ERROR, "TPM cannot be loaded\n"));\r
+    } else {\r
+      CopyMem ((VOID *) (UINTN)TpmPointer, TpmBinaryImage, TpmBinaryImageSize);\r
+      Table->TpmSegment = Regs.X.DS;\r
+      Table->TpmOffset  = Regs.X.BX;\r
+\r
+    }\r
+  }\r
+  //\r
+  // Lock the Legacy BIOS region\r
+  //\r
+  Private->Cpu->FlushDataCache (Private->Cpu, Private->BiosStart, (UINT32) LegacyBiosImageSize, EfiCpuFlushTypeWriteBackInvalidate);\r
+  Private->LegacyRegion->Lock (Private->LegacyRegion, Private->BiosStart, (UINT32) LegacyBiosImageSize, &Granularity);\r
+\r
+  //\r
+  // Get the BbsTable from LOW_MEMORY_THUNK\r
+  //\r
+  BbsTable = (BBS_TABLE *)(UINTN)Private->IntThunk->BbsTable;\r
+  ZeroMem ((VOID *)BbsTable, sizeof (Private->IntThunk->BbsTable));\r
+\r
+  EfiToLegacy16BootTable->BbsTable  = (UINT32)(UINTN)BbsTable;\r
+  Private->BbsTablePtr              = (VOID *) BbsTable;\r
+  //\r
+  // Skip Floppy and possible onboard IDE drives\r
+  //\r
+  EfiToLegacy16BootTable->NumberBbsEntries = 1 + 2 * MAX_IDE_CONTROLLER;\r
+\r
+  for (Index = 0; Index < (sizeof (Private->IntThunk->BbsTable) / sizeof (BBS_TABLE)); Index++) {\r
+    BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY;\r
+  }\r
+  //\r
+  // Allocate space for Legacy HDD table\r
+  //\r
+  LegacyEfiHddTable = (LEGACY_EFI_HDD_TABLE *) AllocateZeroPool ((UINTN) MAX_HDD_ENTRIES * sizeof (LEGACY_EFI_HDD_TABLE));\r
+  ASSERT (LegacyEfiHddTable);\r
+\r
+  Private->LegacyEfiHddTable      = LegacyEfiHddTable;\r
+  Private->LegacyEfiHddTableIndex = 0x00;\r
+\r
+  //\r
+  // start testtest\r
+  //  GetTimerValue (&Ticker);\r
+  //\r
+  //  gRT->SetVariable (L"EndOfLoadFv",\r
+  //                    &gEfiGlobalVariableGuid,\r
+  //                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+  //                    sizeof (UINT64),\r
+  //                    (VOID *)&Ticker\r
+  //                    );\r
+  // end testtest\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Shadow all legacy16 OPROMs that haven't been shadowed.\r
+  Warning: Use this with caution. This routine disconnects all EFI\r
+  drivers. If used externally then caller must re-connect EFI\r
+  drivers.\r
+\r
+  @param  This                    Protocol instance pointer.\r
+\r
+  @retval EFI_SUCCESS             OPROMs shadowed\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LegacyBiosShadowAllLegacyOproms (\r
+  IN EFI_LEGACY_BIOS_PROTOCOL *This\r
+  )\r
+{\r
+  LEGACY_BIOS_INSTANCE  *Private;\r
+\r
+  //\r
+  //  EFI_LEGACY_BIOS_PLATFORM_PROTOCOL    *LegacyBiosPlatform;\r
+  //  EFI_LEGACY16_TABLE                   *Legacy16Table;\r
+  //\r
+  Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
+\r
+  //\r
+  //  LegacyBiosPlatform       = Private->LegacyBiosPlatform;\r
+  //  Legacy16Table            = Private->Legacy16Table;\r
+  //\r
+  // Shadow PCI ROMs. We must do this near the end since this will kick\r
+  // of Native EFI drivers that may be needed to collect info for Legacy16\r
+  //\r
+  //  WARNING: PciIo is gone after this call.\r
+  //\r
+  PciProgramAllInterruptLineRegisters (Private);\r
+\r
+  PciShadowRoms (Private);\r
+\r
+  //\r
+  // Shadow PXE base code, BIS etc.\r
+  //\r
+  //  LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform,\r
+  //                       &Private->OptionRom,\r
+  //                       Legacy16Table);\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get the PCI BIOS interface version.\r
+\r
+  @param  Private  Driver private data.\r
+\r
+  @return The PCI interface version number in Binary Coded Decimal (BCD) format.\r
+          E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00\r
+\r
+**/\r
+UINT16\r
+GetPciInterfaceVersion (\r
+  IN LEGACY_BIOS_INSTANCE *Private\r
+  )\r
+{\r
+  EFI_IA32_REGISTER_SET Reg;\r
+  BOOLEAN               ThunkFailed;\r
+  UINT16                PciInterfaceVersion;\r
+\r
+  PciInterfaceVersion = 0;\r
+\r
+  Reg.X.AX = 0xB101;\r
+  Reg.E.EDI = 0;\r
+\r
+  ThunkFailed = Private->LegacyBios.Int86 (&Private->LegacyBios, 0x1A, &Reg);\r
+  if (!ThunkFailed) {\r
+    //\r
+    // From PCI Firmware 3.0 Specification:\r
+    //   If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the\r
+    //   contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the\r
+    //   presence of the PCI function set. [BX] will further indicate the version level, with enough\r
+    //   granularity to allow for incremental changes in the code that don't affect the function interface.\r
+    //   Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10\r
+    //   would be returned as a 02h in the [BH] registers and 10h in the [BL] registers.\r
+    //\r
+    if ((Reg.X.Flags.CF == 0) && (Reg.H.AH == 0) && (Reg.E.EDX == SIGNATURE_32 ('P', 'C', 'I', ' '))) {\r
+      PciInterfaceVersion = Reg.X.BX;\r
+    }\r
+  }\r
+  return PciInterfaceVersion;\r
+}\r
+\r
+/**\r
+  Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.\r
+  SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.\r
+\r
+  @param  Event                 Event whose notification function is being invoked.\r
+  @param  Context               The pointer to the notification function's context,\r
+                                which is implementation-dependent.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+InstallSmbiosEventCallback (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  SMBIOS_TABLE_ENTRY_POINT    *EntryPointStructure;\r
+\r
+  //\r
+  // Get SMBIOS table from EFI configuration table\r
+  //\r
+  Status = EfiGetSystemConfigurationTable (\r
+            &gEfiSmbiosTableGuid,\r
+            &mRuntimeSmbiosEntryPoint\r
+            );\r
+  if ((EFI_ERROR (Status)) || (mRuntimeSmbiosEntryPoint == NULL)) {\r
+    return;\r
+  }\r
+\r
+  EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) mRuntimeSmbiosEntryPoint;\r
+\r
+  //\r
+  // Allocate memory for SMBIOS Entry Point Structure.\r
+  // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.\r
+  //\r
+  if (mReserveSmbiosEntryPoint == 0) {\r
+    //\r
+    // Entrypoint structure with fixed size is allocated only once.\r
+    //\r
+    mReserveSmbiosEntryPoint = SIZE_4GB - 1;\r
+    Status = gBS->AllocatePages (\r
+                    AllocateMaxAddress,\r
+                    EfiReservedMemoryType,\r
+                    EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength)),\r
+                    &mReserveSmbiosEntryPoint\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      mReserveSmbiosEntryPoint = 0;\r
+      return;\r
+    }\r
+    DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Entry Point Structure\n"));\r
+  }\r
+\r
+  if ((mStructureTableAddress != 0) &&\r
+      (mStructureTablePages < EFI_SIZE_TO_PAGES ((UINT32)EntryPointStructure->TableLength))) {\r
+    //\r
+    // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate\r
+    //\r
+    gBS->FreePages (mStructureTableAddress, mStructureTablePages);\r
+    mStructureTableAddress = 0;\r
+    mStructureTablePages   = 0;\r
+    DEBUG ((EFI_D_INFO, "Original size is not enough. Re-allocate the memory.\n"));\r
+  }\r
+\r
+  if (mStructureTableAddress == 0) {\r
+    //\r
+    // Allocate reserved memory below 4GB.\r
+    // Smbios spec requires the structure table is below 4GB.\r
+    //\r
+    mStructureTableAddress = SIZE_4GB - 1;\r
+    mStructureTablePages   = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);\r
+    Status = gBS->AllocatePages (\r
+                    AllocateMaxAddress,\r
+                    EfiReservedMemoryType,\r
+                    mStructureTablePages,\r
+                    &mStructureTableAddress\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->FreePages (\r
+        mReserveSmbiosEntryPoint,\r
+        EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength))\r
+        );\r
+      mReserveSmbiosEntryPoint = 0;\r
+      mStructureTableAddress   = 0;\r
+      mStructureTablePages     = 0;\r
+      return;\r
+    }\r
+    DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Structure Table\n"));\r
+  }\r
+}\r
+\r
+/**\r
+  Callback function to toggle EndOfDxe status. NULL pointer detection needs\r
+  this status to decide if it's necessary to change attributes of page 0.\r
+\r
+  @param  Event            Event whose notification function is being invoked.\r
+  @param  Context          The pointer to the notification function's context,\r
+                           which is implementation-dependent.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ToggleEndOfDxeStatus (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  mEndOfDxe = TRUE;\r
+  return;\r
+}\r
+\r
+/**\r
+  Install Driver to produce Legacy BIOS protocol.\r
+\r
+  @param  ImageHandle  Handle of driver image.\r
+  @param  SystemTable  Pointer to system table.\r
+\r
+  @retval EFI_SUCCESS  Legacy BIOS protocol installed\r
+  @retval No protocol installed, unload driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LegacyBiosInstall (\r
+  IN EFI_HANDLE           ImageHandle,\r
+  IN EFI_SYSTEM_TABLE     *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                         Status;\r
+  LEGACY_BIOS_INSTANCE               *Private;\r
+  EFI_TO_COMPATIBILITY16_INIT_TABLE  *EfiToLegacy16InitTable;\r
+  EFI_PHYSICAL_ADDRESS               MemoryAddress;\r
+  EFI_PHYSICAL_ADDRESS               EbdaReservedBaseAddress;\r
+  VOID                               *MemoryPtr;\r
+  EFI_PHYSICAL_ADDRESS               MemoryAddressUnder1MB;\r
+  UINTN                              Index;\r
+  UINT32                             *BaseVectorMaster;\r
+  EFI_PHYSICAL_ADDRESS               StartAddress;\r
+  UINT32                             *ClearPtr;\r
+  EFI_PHYSICAL_ADDRESS               MemStart;\r
+  UINT32                             IntRedirCode;\r
+  UINT32                             Granularity;\r
+  BOOLEAN                            DecodeOn;\r
+  UINT32                             MemorySize;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR    Descriptor;\r
+  UINT64                             Length;\r
+  UINT8                              *SecureBoot;\r
+  EFI_EVENT                          InstallSmbiosEvent;\r
+  EFI_EVENT                          EndOfDxeEvent;\r
+\r
+  //\r
+  // Load this driver's image to memory\r
+  //\r
+  Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // When UEFI Secure Boot is enabled, CSM module will not start any more.\r
+  //\r
+  SecureBoot = NULL;\r
+  GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);\r
+  if ((SecureBoot != NULL) && (*SecureBoot == SECURE_BOOT_MODE_ENABLE)) {\r
+    FreePool (SecureBoot);\r
+    return EFI_SECURITY_VIOLATION;\r
+  }\r
+\r
+  if (SecureBoot != NULL) {\r
+    FreePool (SecureBoot);\r
+  }\r
+\r
+  Private = &mPrivateData;\r
+  ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE));\r
+\r
+  //\r
+  // Grab a copy of all the protocols we depend on. Any error would\r
+  // be a dispatcher bug!.\r
+  //\r
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Private->Timer);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **) &Private->LegacyBiosPlatform);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &Private->Legacy8259);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **) &Private->LegacyInterrupt);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Locate Memory Test Protocol if exists\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiGenericMemTestProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &Private->GenericMemoryTest\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Make sure all memory from 0-640K is tested\r
+  //\r
+  for (StartAddress = 0; StartAddress < 0xa0000; ) {\r
+    gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);\r
+    if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {\r
+      StartAddress = Descriptor.BaseAddress + Descriptor.Length;\r
+      continue;\r
+    }\r
+    Length = MIN (Descriptor.Length, 0xa0000 - StartAddress);\r
+    Private->GenericMemoryTest->CompatibleRangeTest (\r
+                                  Private->GenericMemoryTest,\r
+                                  StartAddress,\r
+                                  Length\r
+                                  );\r
+    StartAddress = StartAddress + Length;\r
+  }\r
+  //\r
+  // Make sure all memory from 1MB to 16MB is tested and added to memory map\r
+  //\r
+  for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) {\r
+    gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);\r
+    if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {\r
+      StartAddress = Descriptor.BaseAddress + Descriptor.Length;\r
+      continue;\r
+    }\r
+    Length = MIN (Descriptor.Length, BASE_16MB - StartAddress);\r
+    Private->GenericMemoryTest->CompatibleRangeTest (\r
+                                  Private->GenericMemoryTest,\r
+                                  StartAddress,\r
+                                  Length\r
+                                  );\r
+    StartAddress = StartAddress + Length;\r
+  }\r
+\r
+  Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE;\r
+\r
+  Private->LegacyBios.Int86 = LegacyBiosInt86;\r
+  Private->LegacyBios.FarCall86 = LegacyBiosFarCall86;\r
+  Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom;\r
+  Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom;\r
+  Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot;\r
+  Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus;\r
+  Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo;\r
+  Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms;\r
+  Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi;\r
+  Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion;\r
+  Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion;\r
+  Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice;\r
+\r
+  Private->ImageHandle = ImageHandle;\r
+\r
+  //\r
+  // Enable read attribute of legacy region.\r
+  //\r
+  DecodeOn = TRUE;\r
+  Private->LegacyRegion->Decode (\r
+                           Private->LegacyRegion,\r
+                           0xc0000,\r
+                           0x40000,\r
+                           &Granularity,\r
+                           &DecodeOn\r
+                           );\r
+  //\r
+  // Set Cachebility for legacy region\r
+  // BUGBUG: Comments about this legacy region cacheability setting\r
+  //         This setting will make D865GCHProduction CSM Unhappy\r
+  //\r
+  if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) {\r
+    gDS->SetMemorySpaceAttributes (\r
+           0x0,\r
+           0xA0000,\r
+           EFI_MEMORY_WB\r
+           );\r
+    gDS->SetMemorySpaceAttributes (\r
+           0xc0000,\r
+           0x40000,\r
+           EFI_MEMORY_WB\r
+           );\r
+  }\r
+\r
+  gDS->SetMemorySpaceAttributes (\r
+         0xA0000,\r
+         0x20000,\r
+         EFI_MEMORY_UC\r
+         );\r
+\r
+  //\r
+  // Allocate 0 - 4K for real mode interupt vectors and BDA.\r
+  //\r
+  AllocateLegacyMemory (\r
+    AllocateAddress,\r
+    EfiReservedMemoryType,\r
+    0,\r
+    1,\r
+    &MemoryAddress\r
+    );\r
+  ASSERT (MemoryAddress == 0x000000000);\r
+\r
+  ClearPtr = (VOID *) ((UINTN) 0x0000);\r
+\r
+  //\r
+  // Initialize region from 0x0000 to 4k. This initializes interrupt vector\r
+  // range.\r
+  //\r
+  ACCESS_PAGE0_CODE (\r
+    gBS->SetMem ((VOID *) ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K);\r
+    ZeroMem ((VOID *) ((UINTN)ClearPtr + 0x400), 0xC00);\r
+  );\r
+\r
+  //\r
+  // Allocate pages for OPROM usage\r
+  //\r
+  MemorySize = PcdGet32 (PcdEbdaReservedMemorySize);\r
+  ASSERT ((MemorySize & 0xFFF) == 0);\r
+\r
+  Status = AllocateLegacyMemory (\r
+             AllocateAddress,\r
+             EfiReservedMemoryType,\r
+             CONVENTIONAL_MEMORY_TOP - MemorySize,\r
+             EFI_SIZE_TO_PAGES (MemorySize),\r
+             &MemoryAddress\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  ZeroMem ((VOID *) ((UINTN) MemoryAddress), MemorySize);\r
+\r
+  //\r
+  // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that\r
+  // don't use PMM but look for zeroed memory. Note that various non-BBS\r
+  // OpROMs expect different areas to be free\r
+  //\r
+  EbdaReservedBaseAddress = MemoryAddress;\r
+  MemoryAddress = PcdGet32 (PcdOpromReservedMemoryBase);\r
+  MemorySize    = PcdGet32 (PcdOpromReservedMemorySize);\r
+  //\r
+  // Check if base address and size for reserved memory are 4KB aligned.\r
+  //\r
+  ASSERT ((MemoryAddress & 0xFFF) == 0);\r
+  ASSERT ((MemorySize & 0xFFF) == 0);\r
+  //\r
+  // Check if the reserved memory is below EBDA reserved range.\r
+  //\r
+  ASSERT ((MemoryAddress < EbdaReservedBaseAddress) && ((MemoryAddress + MemorySize - 1) < EbdaReservedBaseAddress));\r
+  for (MemStart = MemoryAddress; MemStart < MemoryAddress + MemorySize; MemStart += 0x1000) {\r
+    Status = AllocateLegacyMemory (\r
+               AllocateAddress,\r
+               EfiBootServicesCode,\r
+               MemStart,\r
+               1,\r
+               &StartAddress\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      MemoryPtr = (VOID *) ((UINTN) StartAddress);\r
+      ZeroMem (MemoryPtr, 0x1000);\r
+    } else {\r
+      DEBUG ((EFI_D_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart));\r
+    }\r
+  }\r
+\r
+  //\r
+  // Allocate low PMM memory and zero it out\r
+  //\r
+  MemorySize = PcdGet32 (PcdLowPmmMemorySize);\r
+  ASSERT ((MemorySize & 0xFFF) == 0);\r
+  Status = AllocateLegacyMemory (\r
+             AllocateMaxAddress,\r
+             EfiBootServicesCode,\r
+             CONVENTIONAL_MEMORY_TOP,\r
+             EFI_SIZE_TO_PAGES (MemorySize),\r
+             &MemoryAddressUnder1MB\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  ZeroMem ((VOID *) ((UINTN) MemoryAddressUnder1MB), MemorySize);\r
+\r
+  //\r
+  // Allocate space for thunker and Init Thunker\r
+  //\r
+  Status = AllocateLegacyMemory (\r
+             AllocateMaxAddress,\r
+             EfiReservedMemoryType,\r
+             CONVENTIONAL_MEMORY_TOP,\r
+             (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2,\r
+             &MemoryAddress\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+  Private->IntThunk                   = (LOW_MEMORY_THUNK *) (UINTN) MemoryAddress;\r
+  EfiToLegacy16InitTable                   = &Private->IntThunk->EfiToLegacy16InitTable;\r
+  EfiToLegacy16InitTable->ThunkStart       = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;\r
+  EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32) (sizeof (LOW_MEMORY_THUNK));\r
+\r
+  Status = LegacyBiosInitializeThunk (Private);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Init the legacy memory map in memory < 1 MB.\r
+  //\r
+  EfiToLegacy16InitTable->BiosLessThan1MB         = (UINT32) MemoryAddressUnder1MB;\r
+  EfiToLegacy16InitTable->LowPmmMemory            = (UINT32) MemoryAddressUnder1MB;\r
+  EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = MemorySize;\r
+\r
+  MemorySize = PcdGet32 (PcdHighPmmMemorySize);\r
+  ASSERT ((MemorySize & 0xFFF) == 0);\r
+  //\r
+  // Allocate high PMM Memory under 16 MB\r
+  //\r
+  Status = AllocateLegacyMemory (\r
+             AllocateMaxAddress,\r
+             EfiBootServicesCode,\r
+             0x1000000,\r
+             EFI_SIZE_TO_PAGES (MemorySize),\r
+             &MemoryAddress\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // If it fails, allocate high PMM Memory under 4GB\r
+    //\r
+    Status = AllocateLegacyMemory (\r
+               AllocateMaxAddress,\r
+               EfiBootServicesCode,\r
+               0xFFFFFFFF,\r
+               EFI_SIZE_TO_PAGES (MemorySize),\r
+               &MemoryAddress\r
+               );\r
+  }\r
+  if (!EFI_ERROR (Status)) {\r
+    EfiToLegacy16InitTable->HiPmmMemory            = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;\r
+    EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = MemorySize;\r
+  }\r
+\r
+  //\r
+  //  ShutdownAPs();\r
+  //\r
+  // Start the Legacy BIOS;\r
+  //\r
+  Status = ShadowAndStartLegacy16 (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // Initialize interrupt redirection code and entries;\r
+  // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.\r
+  //\r
+  CopyMem (\r
+         Private->IntThunk->InterruptRedirectionCode,\r
+         (VOID *) (UINTN) InterruptRedirectionTemplate,\r
+         sizeof (Private->IntThunk->InterruptRedirectionCode)\r
+         );\r
+\r
+  //\r
+  // Save Unexpected interrupt vector so can restore it just prior to boot\r
+  //\r
+  ACCESS_PAGE0_CODE (\r
+    BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);\r
+    Private->BiosUnexpectedInt = BaseVectorMaster[0];\r
+    IntRedirCode = (UINT32) (UINTN) Private->IntThunk->InterruptRedirectionCode;\r
+    for (Index = 0; Index < 8; Index++) {\r
+      BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4);\r
+    }\r
+  );\r
+\r
+  //\r
+  // Save EFI value\r
+  //\r
+  Private->ThunkSeg = (UINT16) (EFI_SEGMENT (IntRedirCode));\r
+\r
+  //\r
+  // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists\r
+  //\r
+  InstallSmbiosEventCallback (NULL, NULL);\r
+\r
+  //\r
+  // Create callback function to update the size of reserved memory after LegacyBiosDxe starts\r
+  //\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  InstallSmbiosEventCallback,\r
+                  NULL,\r
+                  &gEfiSmbiosTableGuid,\r
+                  &InstallSmbiosEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Create callback to update status of EndOfDxe, which is needed by NULL\r
+  // pointer detection\r
+  //\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  ToggleEndOfDxeStatus,\r
+                  NULL,\r
+                  &gEfiEndOfDxeEventGroupGuid,\r
+                  &EndOfDxeEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Make a new handle and install the protocol\r
+  //\r
+  Private->Handle = NULL;\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &Private->Handle,\r
+                  &gEfiLegacyBiosProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &Private->LegacyBios\r
+                  );\r
+  Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private);\r
+\r
+  DEBUG ((EFI_D_INFO, "CSM16 PCI BIOS Interface Version: %02x.%02x\n",\r
+          (UINT8) (Private->Csm16PciInterfaceVersion >> 8),\r
+          (UINT8) Private->Csm16PciInterfaceVersion\r
+        ));\r
+  ASSERT (Private->Csm16PciInterfaceVersion != 0);\r
+  return Status;\r
+}\r