\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
\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
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