]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/X64/SetIdtEntry.c
MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe: Add support for PCD PcdPteMemoryEn...
[mirror_edk2.git] / MdeModulePkg / Universal / Acpi / BootScriptExecutorDxe / X64 / SetIdtEntry.c
index f70f2f986f1aa3df27757867d045ce59fe6f4932..d433cf128ccff9f89cdb9474ee285a875c2fb035 100644 (file)
@@ -3,7 +3,9 @@
 \r
   Set a IDT entry for interrupt vector 3 for debug purpose for x64 platform\r
 \r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
+\r
 \r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
@@ -15,24 +17,116 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 **/\r
 #include "ScriptExecute.h"\r
+\r
 //\r
-// INTERRUPT_GATE_DESCRIPTOR and SetIdtEntry () are used to setup IDT to do debug\r
+// 8 extra pages for PF handler.\r
 //\r
+#define EXTRA_PAGE_TABLE_PAGES      8\r
 \r
-#pragma pack(1)\r
+#define IA32_PG_P                   BIT0\r
+#define IA32_PG_RW                  BIT1\r
+#define IA32_PG_PS                  BIT7\r
 \r
-typedef struct {\r
-  UINT16    Offset15To0;\r
-  UINT16    SegmentSelector;\r
-  UINT16    Attributes;\r
-  UINT16    Offset31To16;\r
-  UINT32    Offset63To32;\r
-  UINT32    Reserved;\r
-} INTERRUPT_GATE_DESCRIPTOR;\r
+UINT64                              mPhyMask;\r
+VOID                                *mOriginalHandler;\r
+UINTN                               mPageFaultBuffer;\r
+UINTN                               mPageFaultIndex = 0;\r
+//\r
+// Store the uplink information for each page being used.\r
+//\r
+UINT64                              *mPageFaultUplink[EXTRA_PAGE_TABLE_PAGES];\r
 \r
-#define INTERRUPT_GATE_ATTRIBUTE   0x8e00\r
+/**\r
+  Page fault handler.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PageFaultHandlerHook (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Hook IDT with our page fault handler so that the on-demand paging works on page fault.\r
+\r
+  @param  IdtEntry  a pointer to IDT entry\r
+\r
+**/\r
+VOID\r
+HookPageFaultHandler (\r
+  IN IA32_IDT_GATE_DESCRIPTOR                   *IdtEntry\r
+  )\r
+{\r
+  UINT32         RegEax;\r
+  UINT8          PhysicalAddressBits;\r
+  UINTN          PageFaultHandlerHookAddress;\r
+\r
+  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+  if (RegEax >= 0x80000008) {\r
+    AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
+    PhysicalAddressBits = (UINT8) RegEax;\r
+  } else {\r
+    PhysicalAddressBits = 36;\r
+  }\r
+  mPhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;\r
+  mPhyMask &= (1ull << 48) - SIZE_4KB;\r
+\r
+  //\r
+  // Set Page Fault entry to catch >4G access\r
+  //\r
+  PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;\r
+  mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));\r
+  IdtEntry->Bits.OffsetLow      = (UINT16)PageFaultHandlerHookAddress;\r
+  IdtEntry->Bits.Selector       = (UINT16)AsmReadCs ();\r
+  IdtEntry->Bits.Reserved_0     = 0;\r
+  IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
+  IdtEntry->Bits.OffsetHigh     = (UINT16)(PageFaultHandlerHookAddress >> 16);\r
+  IdtEntry->Bits.OffsetUpper    = (UINT32)(PageFaultHandlerHookAddress >> 32);\r
+  IdtEntry->Bits.Reserved_1     = 0;\r
+\r
+  if (mPage1GSupport) {\r
+    mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(2);\r
+  }else {\r
+    mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(6);\r
+  }\r
+  ZeroMem (mPageFaultUplink, sizeof (mPageFaultUplink));\r
+}\r
+\r
+/**\r
+  The function will check if current waking vector is long mode.\r
+\r
+  @param  AcpiS3Context                 a pointer to a structure of ACPI_S3_CONTEXT\r
+\r
+  @retval TRUE   Current context need long mode waking vector.\r
+  @retval FALSE  Current context need not long mode waking vector.\r
+**/\r
+BOOLEAN\r
+IsLongModeWakingVector (\r
+  IN ACPI_S3_CONTEXT                *AcpiS3Context\r
+  )\r
+{\r
+  EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;\r
+\r
+  Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));\r
+  if ((Facs == NULL) ||\r
+      (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ||\r
+      ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) {\r
+    // Something wrong with FACS\r
+    return FALSE;\r
+  }\r
+  if (Facs->XFirmwareWakingVector != 0) {\r
+    if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&\r
+        ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&\r
+        ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {\r
+      // Both BIOS and OS wants 64bit vector\r
+      if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+        return TRUE;\r
+      }\r
+    }\r
+  }\r
+  return FALSE;\r
+}\r
 \r
-#pragma pack()\r
 /**\r
   Set a IDT entry for interrupt vector 3 for debug purpose.\r
 \r
@@ -44,24 +138,130 @@ SetIdtEntry (
   IN ACPI_S3_CONTEXT     *AcpiS3Context\r
   )\r
 {\r
-  INTERRUPT_GATE_DESCRIPTOR                     *IdtEntry;\r
+  IA32_IDT_GATE_DESCRIPTOR                      *IdtEntry;\r
   IA32_DESCRIPTOR                               *IdtDescriptor;\r
   UINTN                                         S3DebugBuffer;\r
+  EFI_STATUS                                    Status;\r
 \r
   //\r
   // Restore IDT for debug\r
   //\r
   IdtDescriptor = (IA32_DESCRIPTOR *) (UINTN) (AcpiS3Context->IdtrProfile);\r
-  IdtEntry = (INTERRUPT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (INTERRUPT_GATE_DESCRIPTOR)));\r
-  S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress);\r
+  AsmWriteIdtr (IdtDescriptor);\r
+\r
+  //\r
+  // Setup the default CPU exception handlers\r
+  //\r
+  Status = InitializeCpuExceptionHandlers (NULL);\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-  IdtEntry->Offset15To0     = (UINT16)S3DebugBuffer;\r
-  IdtEntry->SegmentSelector = (UINT16)AsmReadCs ();;\r
-  IdtEntry->Attributes      = (UINT16)INTERRUPT_GATE_ATTRIBUTE;\r
-  IdtEntry->Offset31To16    = (UINT16)(S3DebugBuffer >> 16);\r
-  IdtEntry->Offset63To32    = (UINT32)(S3DebugBuffer >> 32);\r
-  IdtEntry->Reserved        = 0;\r
+  DEBUG_CODE (\r
+    //\r
+    // Update IDT entry INT3 if the instruction is valid in it\r
+    //\r
+    S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress);\r
+    if (*(UINTN *)S3DebugBuffer != (UINTN) -1) {\r
+      IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));\r
+      IdtEntry->Bits.OffsetLow      = (UINT16)S3DebugBuffer;\r
+      IdtEntry->Bits.Selector       = (UINT16)AsmReadCs ();\r
+      IdtEntry->Bits.Reserved_0     = 0;\r
+      IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;\r
+      IdtEntry->Bits.OffsetHigh     = (UINT16)(S3DebugBuffer >> 16);\r
+      IdtEntry->Bits.OffsetUpper    = (UINT32)(S3DebugBuffer >> 32);\r
+      IdtEntry->Bits.Reserved_1     = 0;\r
+    }\r
+  );\r
 \r
-  AsmWriteIdtr (IdtDescriptor);\r
+  //\r
+  // If both BIOS and OS wants long mode waking vector,\r
+  // S3ResumePei should have established 1:1 Virtual to Physical identity mapping page table,\r
+  // no need to hook page fault handler.\r
+  //\r
+  if (!IsLongModeWakingVector (AcpiS3Context)) {\r
+    IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));\r
+    HookPageFaultHandler (IdtEntry);\r
+  }\r
 }\r
 \r
+/**\r
+  Acquire page for page fault.\r
+\r
+  @param[in, out] Uplink        Pointer to up page table entry.\r
+\r
+**/\r
+VOID\r
+AcquirePage (\r
+  IN OUT UINT64                 *Uplink\r
+  )\r
+{\r
+  UINTN             Address;\r
+\r
+  Address = mPageFaultBuffer + EFI_PAGES_TO_SIZE (mPageFaultIndex);\r
+  ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));\r
+\r
+  //\r
+  // Cut the previous uplink if it exists and wasn't overwritten.\r
+  //\r
+  if ((mPageFaultUplink[mPageFaultIndex] != NULL) &&\r
+     ((*mPageFaultUplink[mPageFaultIndex] & ~mAddressEncMask & mPhyMask) == Address)) {\r
+    *mPageFaultUplink[mPageFaultIndex] = 0;\r
+  }\r
+\r
+  //\r
+  // Link & Record the current uplink.\r
+  //\r
+  *Uplink = Address | mAddressEncMask | IA32_PG_P | IA32_PG_RW;\r
+  mPageFaultUplink[mPageFaultIndex] = Uplink;\r
+\r
+  mPageFaultIndex = (mPageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;\r
+}\r
+\r
+/**\r
+  The page fault handler that on-demand read >4G memory/MMIO.\r
+  \r
+  @retval TRUE     The page fault is correctly handled.\r
+  @retval FALSE    The page fault is not handled and is passed through to original handler.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+PageFaultHandler (\r
+  VOID\r
+  )\r
+{\r
+  UINT64         *PageTable;\r
+  UINT64         PFAddress;\r
+  UINTN          PTIndex;\r
+\r
+  PFAddress = AsmReadCr2 ();\r
+  DEBUG ((EFI_D_ERROR, "BootScript - PageFaultHandler: Cr2 - %lx\n", PFAddress));\r
+\r
+  if (PFAddress >= mPhyMask + SIZE_4KB) {\r
+    return FALSE;\r
+  }\r
+  PFAddress &= mPhyMask;\r
+\r
+  PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask);\r
+\r
+  PTIndex = BitFieldRead64 (PFAddress, 39, 47);\r
+  // PML4E\r
+  if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
+    AcquirePage (&PageTable[PTIndex]);\r
+  }\r
+  PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);\r
+  PTIndex = BitFieldRead64 (PFAddress, 30, 38);\r
+  // PDPTE\r
+  if (mPage1GSupport) {\r
+    PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
+  } else {\r
+    if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
+      AcquirePage (&PageTable[PTIndex]);\r
+    }\r
+    PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);\r
+    PTIndex = BitFieldRead64 (PFAddress, 21, 29);\r
+    // PD\r
+    PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;\r
+  }\r
+\r
+  return TRUE;\r
+}\r